diff --git a/.gitignore b/.gitignore index 95faf6f6a0..591a846c12 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ ospray-doc !/modules /modules/* !/modules/mpi +!/modules/pluggableGeometryExample diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7ad2034226..a2db8d48ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,6 +16,7 @@ build-centos6-gcc: build-centos6-icc: type: build script: + - module load gcc/4.9.3 - module load intel - export CC=icc - export CXX=icpc @@ -25,6 +26,19 @@ build-centos6-icc: - icc - build +build-centos6-icc15: + type: build + script: + - module load gcc/4.9.3 + - module load intel/2015 + - export CC=icc + - export CXX=icpc + - scripts/build_gitlab/linux.sh + tags: + - centos6 + - icc + - build + build-centos7-gcc: type: build script: @@ -71,15 +85,6 @@ build-ubuntu1404-gcc: - gcc - build -build-sles-gcc: - type: build - script: - - scripts/build_gitlab/linux.sh - tags: - - sles - - gcc - - build - build-debug: type: build script: @@ -96,6 +101,7 @@ build-mpi: tags: - linux - mpi + - gcc - build build-osx-clang: @@ -125,6 +131,15 @@ build-windows-msvc14: - build - win7 +build-windows-msvc15: + type: build + script: + - call scripts\build_gitlab\win.bat "Visual Studio 15 2017 Win64" "v141" + tags: + - msvc15 + - build + - win7 + build-windows-icc: type: build script: @@ -136,37 +151,15 @@ build-windows-icc: ### RELEASE JOBS ### -# NOTE(jda) - Current CentOS 6 release machine needs gcc environment -# debugged -#release-linux-gcc: -# type: deploy -# script: -# - module load gcc -# - export CC=gcc -# - export CXX=g++ -# - export OSPRAY_RELEASE_NO_VERIFY=1 -# - scripts/release/linux.sh -# tags: -# - gcc -# - release -# - centos6 -# only: -# - devel -# - master -# - release-1.0 -# artifacts: -# paths: -# - build_release/*.gz - release-linux-icc: type: deploy script: - module load cmake - - module load gcc + - module load gcc/4.9.3 - module load intel - - module load qt - export CC=icc - export CXX=icpc + - export OSPRAY_RELEASE_NO_VERIFY=1 #NOTE: temporary until lib/apps split in release - scripts/release/linux.sh tags: - icc @@ -176,7 +169,7 @@ release-linux-icc: only: - devel - master - - release-1.2.x + - release-1.3.x artifacts: paths: - build_release/*.gz @@ -195,7 +188,7 @@ release-osx-clang: only: - devel - master - - release-1.2.x + - release-1.3.x artifacts: paths: - build_release/*.gz @@ -213,7 +206,7 @@ release-windows: only: - devel - master - - release-1.2.x + - release-1.3.x artifacts: paths: - build_release\ospray*.zip diff --git a/CHANGELOG.md b/CHANGELOG.md index 76f4c157c5..37423682b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,52 @@ Version History --------------- +### Changes in v1.3.0: + +- New MPI distributed device to support MPI distributed applications + using OSPRay collectively for "in-situ" rendering (currently in "alpha") + - Enabled via new `mpi_distributed` device type + - Currently only supports `raycast` renderer, other renderers will be + supported in the future + - All API calls are expected to be exactly replicated (object instances + and parameters) except scene data (geometries and volumes) + - The original MPI device is now called the `mpi_offload` device to + differentiate between the two implementations +- Support of Intel® AVX-512 for next generation Intel® Xeon® processor + (codename Skylake), thus new minimum ISPC version is 1.9.1 +- Thread affinity of OSPRay's tasking system can now be controlled via + either device parameter `setAffinity`, or commandline parameter + `osp:setaffinity`, or environment variable `OSPRAY_SET_AFFINITY` +- Changed behavior of the background color in the SciVis renderer: + `bgColor` now includes alpha and is always blended (no + `backgroundEnabled` anymore). To disable the background don't set + bgColor, or set it to transparent black (0, 0, 0, 0) +- Geometries "`spheres`" and "`cylinders`" now support texture + coordinates +- The GLUT- and Qt-based demo viewer applications have been replaced + by an example viewer with minimal dependencies + - Building the sample applications now requires GCC 4.9 (previously + 4.8) for features used in the C++ standard library; OSPRay + itself can still be built with GCC 4.8 + - The new example viewer based on `ospray::sg` (called + `ospExampleViewerSg`) is the single application we are + consolidating to, `ospExampleViewer` will remain only as a + deprecated viewer for compatibility with the old `ospGlutViewer` + application +- Deprecated `ospCreateDevice()`; use `ospNewDevice()` instead +- Improved error handling + - Various API functions now return an `OSPError` value + - `ospDeviceSetStatusFunc` replaces the deprecated + `ospDeviceSetErrorMsgFunc` + - New API functions to query the last error + (`ospDeviceGetLastErrorCode()` and `ospDeviceGetLastErrorMsg()`) + or to register an error callback with `ospDeviceSetErrorFunc()` + - Fixed bug where exceptions could leak to C applications + ### Changes in v1.2.1: - Various bugfixes related to MPI distributed rendering, ISPC issues - on Windows, and other build related issues. + on Windows, and other build related issues ### Changes in v1.2.0: @@ -23,7 +65,7 @@ Version History via new material "`Luminous`" - Lights can optionally made invisible by using the new parameter `isVisible` (only relevant for path tracer) -- OSPRay Devices are now extendable through modules and the SDK. +- OSPRay Devices are now extendable through modules and the SDK - Devices can be created and set current, creating an alternative method for initializing the API - New API functions for committing parameters on Devices @@ -130,7 +172,7 @@ Version History - GLUT viewer now supports volume rendering - Command mode with preliminary scripting capabilities, enabled by pressing '`:`' key (not available when using - Intel C++ compiler (icc)) + Intel C++ Compiler (icc)) - Enhanced support of sample applications on Windows - New minimum ISPC version is 1.9.0 - Support of Intel® AVX-512 for second generation Intel Xeon Phi diff --git a/CMakeLists.txt b/CMakeLists.txt index b2e6ea232d..7f5a12e02a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,7 +95,6 @@ ENDIF() ADD_SUBDIRECTORY(modules) - # create a configure file that both ospray and ispc can read the cmake config # from needs to be at the end, after all cache variables have been set CONFIGURE_FILE(ospray/common/OSPConfig.h.in OSPConfig.h) diff --git a/README.md b/README.md index a0644eeccb..0d7ad43e1d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ OSPRay ====== -This is release v1.2.1 of OSPRay. For changes and new features see the +This is release v1.3.0 of OSPRay. For changes and new features see the [changelog](CHANGELOG.md). Also visit http://www.ospray.org for more information. @@ -65,11 +65,10 @@ before you can build OSPRay you need the following prerequisites: - To build OSPRay you need [CMake](http://www.cmake.org), any form of C++11 compiler (we recommend using GCC, but also support Clang and - the [Intel® C++ compiler - (ICC)](https://software.intel.com/en-us/c-compilers)), and standard - Linux development tools. To build the demo viewers, you should also - have some version of OpenGL and the GL Utility Toolkit (GLUT or - freeglut), as well as Qt 4.6 or higher. + the [Intel® C++ Compiler + (icc)](https://software.intel.com/en-us/c-compilers)), and standard + Linux development tools. To build the example viewers, you should + also have some version of OpenGL. - Additionally you require a copy of the [Intel® SPMD Program Compiler (ISPC)](http://ispc.github.io). Please obtain a copy of the latest binary release of ISPC (currently 1.9.1) from the [ISPC downloads @@ -94,20 +93,16 @@ Type the following to install the dependencies using `yum`: sudo yum install cmake.x86_64 sudo yum install tbb.x86_64 tbb-devel.x86_64 - sudo yum install freeglut.x86_64 freeglut-devel.x86_64 - sudo yum install qt-devel.x86_64 Type the following to install the dependencies using `apt-get`: sudo apt-get install cmake-curses-gui sudo apt-get install libtbb-dev - sudo apt-get install freeglut3-dev - sudo apt-get install libqt4-dev Under Mac OS X these dependencies can be installed using [MacPorts](http://www.macports.org/): - sudo port install cmake tbb freeglut qt4 + sudo port install cmake tbb Compiling OSPRay ---------------- @@ -147,8 +142,8 @@ CMake is easy: user@mymachine[~/Projects/ospray/release]: make -- You should now have `libospray.so` as well as a set of sample - viewers. You can test your version of OSPRay using any of the +- You should now have `libospray.so` as well as a set of example + application. You can test your version of OSPRay using any of the examples on the [OSPRay Demos and Examples](http://www.ospray.org/demos.html) page. @@ -156,9 +151,9 @@ Documentation ============= The following [API -documentation](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation") +documentation](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation") of OSPRay can also be found as a [pdf -document](http://www.sdvis.org/ospray/download/OSPRay_readme.pdf "OSPRay Documentation") +document](http://www.sdvis.org/ospray/download/OSPRay_readme_devel.pdf "OSPRay Documentation") (2.6MB). For a deeper explanation of the concepts, design, features and @@ -196,19 +191,22 @@ The first is to do so by giving OSPRay the command line from `main()` by calling ``` {.cpp} -void ospInit(int *argc, const char **argv); +OSPError ospInit(int *argc, const char **argv); ``` OSPRay parses (and removes) its known command line parameters from your application's `main` function. For an example see the -[tutorial](#tutorial). The following parameters (which are prefixed by -convention with "`--osp:`") are understood: +[tutorial](#tutorial). For possible error codes see section [Error +Handling and Status Messages](#error-handling-and-status-messages). It +is important to note that the arguments passed to `ospInit()` are +prcessed in order they are listed. The following parameters (which are +prefixed by convention with "`--osp:`") are understood: - +
--++ @@ -238,16 +236,32 @@ convention with "`--osp:`") are understood: + + + + - + + + + + - - + + + + + + - + + + + +
Command line parameters accepted by OSPRay's ospInit.
shortcut for --osp:loglevel 2
--osp:module:<name>load a module during initialization; equivalent to calling ospLoadModule(name)
--osp:mpienables MPI mode for parallel rendering, to be used in conjunction with mpirunenables MPI mode for parallel rendering with the mpi_offload device, to be used in conjunction with mpirun; this will automatically load the "mpi" module if it is not yet loaded or linked
--osp:mpi-offloadsame as --osp:mpi
--osp:logoutputconvenience for setting where error/status messages go; valid values are cerr and cout--osp:mpi-distributedsame as --osp:mpi, but will create an mpi_distributed device instead; Note that this will likely require application changes to work properly
--osp:logoutput <dst>convenience for setting where error/status messages go; valid values for dst are cerr and cout
--osp:device:<name>use name as the type of device for OSPRay to create; e.g. --osp:device:default gives you the default local device and --osp:device:mpi gives you the MPI deviceuse name as the type of device for OSPRay to create; e.g. --osp:device:default gives you the default local device; Note if the device to be used is defined in a module, remember to pass --osp:module:<name> first
--osp:setaffinity <n>if 1, bind software threads to hardware threads; 0 disables binding; default is 1 on KNL and 0 otherwise
@@ -258,18 +272,20 @@ convention with "`--osp:`") are understood: The second method of initialization is to explicitly create the device yourself, and possibly set parameters. This method looks almost -identical to how other objects are created and used by OSPRay (described -in later sections). The first step is to create the device with +identical to how other [objects](#objects) are created and used by +OSPRay (described in later sections). The first step is to create the +device with ``` {.cpp} -OSPDevice ospCreateDevice(const char *type); +OSPDevice ospNewDevice(const char *type); ``` where the `type` string maps to a specific device implementation. OSPRay always provides the "`default`" device, which maps to a local CPU rendering device. If it is enabled in the build, you can also use -"`mpi`" to access the MPI multi-node rendering device. Once a device is -created, you can call +"`mpi`" to access the MPI multi-node rendering device (see [Parallel +Rendering with MPI](#parallel-rendering-with-mpi) section for more +info). Once a device is created, you can call ``` {.cpp} void ospDeviceSet1i(OSPDevice, const char *id, int val); @@ -284,11 +300,12 @@ void ospDeviceSetString(OSPDevice, const char *id, const char *val); to set parameters on the device. The following parameters can be set on all devices: -| Type | Name | Description | -|:-----|:-----------|:----------------------------------------------------------| -| int | numThreads | number of threads which OSPRay should use | -| int | logLevel | logging level | -| int | debug | set debug mode; equivalent to logLevel=2 and numThreads=1 | +| Type | Name | Description | +|:-----|:------------|:------------------------------------------------------------------------------------------------------------------------| +| int | numThreads | number of threads which OSPRay should use | +| int | logLevel | logging level | +| int | debug | set debug mode; equivalent to logLevel=2 and numThreads=1 | +| int | setAffinity | bind software threads to hardware threads if set to 1; 0 disables binding omitting the parameter will let OSPRay choose | : Parameters shared by all devices. @@ -327,29 +344,77 @@ environment variables for easy changes to OSPRay's behavior without needing to change the application (variables are prefixed by convention with "`OSPRAY_`"): -| Variable | Description | -|:--------------------|:---------------------------------| -| OSPRAY\_THREADS | equivalent to `--osp:numthreads` | -| OSPRAY\_LOG\_LEVEL | equivalent to `--osp:loglevel` | -| OSPRAY\_LOG\_OUTPUT | equivalent to `--osp:logoutput` | -| OSPRAY\_DEBUG | equivalent to `--osp:debug` | +| Variable | Description | +|:----------------------|:----------------------------------| +| OSPRAY\_THREADS | equivalent to `--osp:numthreads` | +| OSPRAY\_LOG\_LEVEL | equivalent to `--osp:loglevel` | +| OSPRAY\_LOG\_OUTPUT | equivalent to `--osp:logoutput` | +| OSPRAY\_DEBUG | equivalent to `--osp:debug` | +| OSPRAY\_SET\_AFFINITY | equivalent to `--osp:setaffinity` | : Environment variables interpreted by OSPRay. -### Handling Error and Status Messages from OSPRay +### Error Handling and Status Messages + +The following errors are currently used by OSPRay: + +| Name | Description | +|:------------------------|:------------------------------------------------------| +| OSP\_NO\_ERROR | no error occured | +| OSP\_UNKNOWN\_ERROR | an unknown error occured | +| OSP\_INVALID\_ARGUMENT | an invalid argument was specified | +| OSP\_INVALID\_OPERATION | the operation is not allowed for the specified object | +| OSP\_OUT\_OF\_MEMORY | there is not enough memory to execute the command | +| OSP\_UNSUPPORTED\_CPU | the CPU is not supported (minimum ISA is SSE4.1) | + +: Possible error codes, i.e. valid named constants of type `OSPError`. + +These error codes are either directly return by some API functions, or +are recorded to be later queried by the application via + +``` {.cpp} +OSPError ospDeviceGetLastErrorCode(OSPDevice); +``` + +A more descriptive error message can be queried by calling + +``` {.cpp} +const char* ospDeviceGetLastErrorMsg(OSPDevice); +``` + +Alternatively, the application can also register a callback function of +type + +``` {.cpp} +typedef void (*OSPErrorFunc)(OSPError, const char* errorDetails); +``` + +via + +``` {.cpp} +void ospDeviceSetErrorFunc(OSPDevice, OSPErrorFunc); +``` + +to get notified when errors occur. Applications may be interested in messages which OSPRay emits, whether for debugging or logging events. Applications can call ``` {.cpp} -void ospDeviceSetErrorMsgFunc(OSPDevice, OSPErrorMsgFunc callback); +void ospDeviceSetStatusFunc(OSPDevice, OSPStatusFunc); +``` + +in order to register a callback function of type + +``` {.cpp} +typedef void (*OSPStatusFunc)(const char* messageText); ``` -in order to set the callback OSPRay will use to emit error messages. By -default, OSPRay uses a callback which does nothing, so any output -desired by an application will require that a callback be provided. Note -that callbacks for C++ std::cout and std::cerr can be alternatively set -through ospInit() or OSPRAY\_LOG\_OUTPUT environment variable. +which OSPRay will use to emit status messages. By default, OSPRay uses a +callback which does nothing, so any output desired by an application +will require that a callback is provided. Note that callbacks for C++ +`std::cout` and `std::cerr` can be alternatively set through `ospInit()` +or the `OSPRAY_LOG_OUTPUT` environment variable. ### Loading OSPRay Extensions at Runtime @@ -359,12 +424,12 @@ implemented in shared libraries. To load plugin `name` from `ospray_module_.dll` (on Windows) use ``` {.cpp} -int32_t ospLoadModule(const char *name); +OSPError ospLoadModule(const char *name); ``` Modules are searched in OS-dependent paths, which include the -application directory. `ospLoadModule` returns `0` if the plugin could -be loaded and an error code `> 0` otherwise. +application directory. `ospLoadModule` returns `OSP_NO_ERROR` if the +plugin could be successfully loaded. Objects ------- @@ -561,9 +626,9 @@ rearrangement of voxel data it cannot be shared the with the application anymore, but has to be transferred to OSPRay via ``` {.cpp} -int ospSetRegion(OSPVolume, void *source, - const vec3i ®ionCoords, - const vec3i ®ionSize); +OSPError ospSetRegion(OSPVolume, void *source, + const vec3i ®ionCoords, + const vec3i ®ionSize); ``` The voxel data pointed to by `source` is copied into the given volume @@ -571,10 +636,9 @@ starting at position `regionCoords`, must be of size `regionSize` and be placed in memory in XYZ order. Note that OSPRay distinguishes between volume data and volume parameters. This function must be called only after all volume parameters (in particular `dimensions` and `voxelType`, -see below) have been set and *before* `ospCommit(volume)` is called. -Memory for the volume is allocated on the first call to this function. -If allocation is unsuccessful or the region size is invalid, the return -value is `0`, and non-zero otherwise. +see below) have been set and *before* `ospCommit(volume)` is called. If +necessary then memory for the volume is allocated on the first call to +this function. The common parameters understood by both structured volume variants are summarized in the table below. If `voxelRange` is not provided for a @@ -653,13 +717,13 @@ A traditional triangle mesh (indexed face set) geometry is created by calling `ospNewGeometry` with type string "`triangles`". Once created, a triangle mesh recognizes the following parameters: -| Type | Name | Description | -|:---------------------|:----------------|:---------------------------------------------------------| -| vec3f(a)\[\] | vertex | [data](#data) array of vertex positions | -| vec3f(a)\[\] | vertex.normal | [data](#data) array of vertex normals | -| vec4f\[\]/vec3fa\[\] | vertex.color | [data](#data) array of vertex colors (RGBA/RGB) | -| vec2f\[\] | vertex.texcoord | [data](#data) array of vertex texture coordinates | -| vec3i(a)\[\] | index | [data](#data) array of triangle indices (into vertex.\*) | +| Type | Name | Description | +|:-----------------------|:----------------|:---------------------------------------------------------| +| vec3f(a)\[\] | vertex | [data](#data) array of vertex positions | +| vec3f(a)\[\] | vertex.normal | [data](#data) array of vertex normals | +| vec4f\[\] / vec3fa\[\] | vertex.color | [data](#data) array of vertex colors (RGBA/RGB) | +| vec2f\[\] | vertex.texcoord | [data](#data) array of vertex texture coordinates | +| vec3i(a)\[\] | index | [data](#data) array of triangle indices (into vertex.\*) | : Parameters defining a triangle mesh geometry. @@ -676,10 +740,10 @@ of specifying the data of center position and radius within a ----++++ @@ -700,7 +764,7 @@ of specifying the data of center position and radius within a - + @@ -720,6 +784,18 @@ of specifying the data of center position and radius within a + + + + + + + + + + + +
Parameters defining a spheres geometry.
OSPData spheres NULLmemory holding the data of all spheresmemory holding the spatial data of all spheres
int-1 offset (in bytes) of each sphere's "float radius" within the spheres array (-1 means disabled and use radius)
vec4f[] / vec3f(a)[]colorNULLdata array of colors (RGBA/RGB), color is constant for each sphere
vec2f[]texcoordNULLdata array of texture coordinates, coordinate is constant for each sphere
@@ -739,10 +815,10 @@ listed in the table below. -+--++ @@ -763,12 +839,12 @@ listed in the table below. - + - + @@ -789,11 +865,27 @@ listed in the table below. + + + + + + + + + + + +
Parameters defining a cylinders geometry.
OSPData cylinders NULLmemory holding the data of all cylindersmemory holding the spatial data of all cylinders
int bytes_per_cylinder2824 size (in bytes) of each cylinder within the cylinders array
-1 offset (in bytes) of each cylinder's "float radius" within the cylinders array (-1 means disabled and use radius instead)
vec4f[] / vec3f(a)[]colorNULLdata array of colors (RGBA/RGB), color is constant for each cylinder
OSPDatatexcoordNULLdata array of texture coordinates, in pairs (each a vec2f at vertex v0 and v1)
: Parameters defining a cylinders geometry. +For texturing each cylinder is seen as a 1D primitive, i.e. a line +segment: the 2D texture coordinates at its vertices v0 and v1 are +linearly interpolated. + ### Streamlines A geometry consisting of multiple stream lines of constant radius is @@ -915,10 +1007,10 @@ special parameters: -+--++ @@ -957,21 +1049,15 @@ special parameters: - + - + - - + + - - - - - - @@ -986,6 +1072,13 @@ Note that the intensity (and color) of AO is controlled via an [ambient light](#ambient-light). If `aoSamples` is zero (the default) then ambient lights cause ambient illumination (without occlusion). +Per default the background of the rendered image will be transparent +black, i.e. the alpha channel holds the opacity of the rendered objects. +This facilitates transparency-aware blending of the image with an +arbitraty background image by the application. The parameter `bgColor` +can be used to already blend with a constant background color (and +alpha) during rendering. + The SciVis renderer supports depth composition with images of other renderers, for example to incorporate help geometries of a 3D UI that were rendered with OpenGL. The screen-sized [texture](#texture) @@ -1589,7 +1682,7 @@ values of `OSPFrameBufferChannel` listed in the table below. | Name | Description | |:------------------|:--------------------------------------------------------------| | OSP\_FB\_COLOR | RGB color including alpha | -| OSP\_FB\_DEPTH | Euclidean distance to the camera (*not* to the image plane) | +| OSP\_FB\_DEPTH | euclidean distance to the camera (*not* to the image plane) | | OSP\_FB\_ACCUM | accumulation buffer for progressive refinement | | OSP\_FB\_VARIANCE | estimate of the current variance, see [rendering](#rendering) | @@ -1625,8 +1718,8 @@ void ospFreeFrameBuffer(OSPFrameBuffer); Because OSPRay uses reference counting internally the framebuffer may not immediately be deleted at this time. -The application can map the given channel of a framebuffer -- and thus -access the stored pixel information -- via +The application can map the given channel of a framebuffer – and thus +access the stored pixel information – via ``` {.cpp} const void *ospMapFrameBuffer(OSPFrameBuffer, @@ -1698,6 +1791,136 @@ 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. +Parallel Rendering with MPI +=========================== + +OSPRay has the ability to scale to multiple nodes in a cluster via MPI. +This enables applications to take advantage of larger compute and memory +resources when available. + +Prerequisites for MPI Mode +-------------------------- + +In addition to the standard build requirements of OSPRay, you must have +the following items available in your environment in order to build&run +OSPRay in MPI mode: + +- An MPI enabled multi-node environment, such as an HPC cluster +- An MPI implementation you can build against (i.e. Intel MPI, + MVAPICH2, etc...) + +Enabling the MPI module in your build +------------------------------------- + +To build the MPI module the CMake variable `OSPRAY_MODULE_MPI` must be +enabled, which can be done directly on the command line (with `-D...`) +or through a configuration dialog (`ccmake`, `cmake-gui`), see also +\[Compiling OSPRay\]. + +This will trigger CMake to go look for an MPI implementation in your +environment. You can then inspect the CMake value of `MPI_LIBRARY` to +make sure that CMake found your MPI build environment correctly. + +This will result in an OSPRay module being built. To enable using it, +applications will need to either link `libospray_module_mpi`, or call + +``` {.cpp} +ospLoadModule("mpi"); +``` + +before initializing OSPRay. + +Modes of using OSPRay's MPI features +------------------------------------ + +OSPRay provides two ways of using MPI to scale up rendering: offload and +distributed. + +The "offload" rendering mode is where a single (not-distributed) calling +application treats the OSPRay API the same as with local rendering. +However, OSPRay uses multiple MPI connected nodes to evenly distribute +frame rendering work, where each node contains a full copy of all scene +data. This method is most effective for scenes which can fit into +memory, but are very expensive to render: for example, path tracing with +many samples-per-pixel is very compute heavy, making it a good situation +to use the offload feature. This can be done with any application which +already uses OSPRay for local rendering without the need for any code +changes. + +The "distributed" rendering mode is where a MPI distributed application +(such as a scientific simulation) uses OSPRay collectively to render +frames. In this case, the API expects all calls (both created objects +and parameters) to be the same on every application rank, except each +rank can specify arbitrary geometries and volumes. Each renderer will +have its own limitations on the topology of the data (i.e. overlapping +data regions, concave data, etc.), but the API calls will only differ +for scene objects. Thus all other calls (i.e. setting camera, creating +framebuffer, rendering frame, etc.) will all be assumed to be identical, +but only rendering a frame and committing the model must be in +lock-step. This mode targets using all available aggregate memory for +very large scenes and for "in-situ" visualization where the data is +already distributed by a simulation app. + +Running an application with the "offload" device +------------------------------------------------ + +As an example, our sample viewer can be run as a single application +which offloads rendering work to multiple MPI processes running on +multiple machines. + +The example apps are setup to be launched in two different setups. In +either setup, the application must initialize OSPRay with the offload +device. This can be done by creating an "`mpi_offload`" device and +setting it as the current device (via the `ospSetCurrentDevice()` +function), or passing either "`--osp:mpi`" or "`--osp:mpi-offload`" as a +command line parameter to `ospInit()`. Note that passing a command line +parameter will automatically call `ospLoadModule("mpi")` to load the MPI +module, while the application will have to load the module explicitly if +using `ospNewDevice()`. + +**Option 1: single MPI launch** + +OSPRay is initialized with the `ospInit()` function call which takes +command line arguments in and configures OSPRay based on what it finds. +In this setup, the app is launched across all ranks, but workers will +never return from `ospInit()`, essentially turning the application into +a worker process for OSPRay. Here's an example of running the +ospVolumeViewer data-replicated, using `c1`-`c4` as compute nodes and +`localhost` the process running the viewer itself: + +``` {.cpp} +% mpirun -perhost 1 -hosts localhost,c1,c2,c3,c4 ./ospExampleViewer [scene_file] --osp:mpi +``` + +**Option 2: separate app/worker launches** + +The second option is to explicitly launch the app on rank 0 and worker +ranks on the other nodes. This is done by running `ospray_mpi_worker` on +worker nodes and the application on the display node. Here's the same +example above using this syntax: + +``` {.cpp} +% mpirun -perhost 1 -hosts localhost ./ospExampleViewer [scene_file] --osp:mpi \ + : -hosts c1,c2,c3,c4 ./ospray_mpi_worker --osp:mpi +``` + +This method of launching the application and OSPRay worker separately +works best for applications which do not immediately call `ospInit()` in +thier `main()` function, or for environments where application +dependencies (such as GUI libraries) may not be available on compute +nodes. + +Running an application with the "distributed" device +---------------------------------------------------- + +Applications using the new distributed device should initialize OSPRay +by creating (and setting current) an "`mpi_distributed`" device or pass +`"--osp:mpi-distributed"` as a command line argument to `ospInit()`. +Note that due to the semantic differences the distributed device gives +the OSPRay API, it is not expected for applications which can already +use the offload device to correctly use the distributed device without +changes to the application. + Examples ======== @@ -1705,7 +1928,7 @@ Tutorial -------- A minimal working example demonstrating how to use OSPRay can be found -at `apps/ospTutorial.cpp`[^6]. On Linux build it in the build\_directory +at `apps/ospTutorial.cpp`[^6]. On Linux build it in the build directory with g++ ../apps/ospTutorial.cpp -I ../ospray/include -I .. ./libospray.so -Wl,-rpath,. -o ospTutorial @@ -1717,7 +1940,7 @@ On Windows build it in the build\_directory\\\$Configuration with Running `ospTutorial` will create two images of two triangles, rendered with the Scientific Visualization renderer with full Ambient Occlusion. The first image `firstFrame.ppm` shows the result after one call to -`ospRenderFrame` -- jagged edges and noise in the shadow can be seen. +`ospRenderFrame` – jagged edges and noise in the shadow can be seen. Calling `ospRenderFrame` multiple times enables progressive refinement, resulting in antialiased edges and converged shadows, shown after ten frames in the second image `accumulatedFrames.png`. @@ -1727,23 +1950,26 @@ frames in the second image `accumulatedFrames.png`. ![After accumulating ten frames.](https://ospray.github.io/images/tutorial_accumulatedframe.png) -Qt Viewer ---------- - -OSPRay also includes a demo viewer application `ospQtViewer`, showcasing -all features of OSPRay. - -![Screenshot of -`ospQtViewer`.](https://ospray.github.io/images/QtViewer.jpg) +Example Viewer +-------------- -Volume Viewer -------------- +OSPRay also includes an exemplary viewer application +`ospExampleViewerSg`, showcasing all features of OSPRay. The Example +Viewer uses the ImGui library for user interface controls. The viewer is +based on a prototype OSPRay scenegraph interface where its nodes are +displayed in the GUI and can be manipulated interactively. For instance, +simply run it as `ospExampleViewerSg teapot.obj`. -Additionally, OSPRay includes a demo viewer application -`ospVolumeViewer`, which is specifically tailored for volume rendering. +This application also functions as an OSPRay state debugger – invalid +values will be shown in red up the hierarchy and won't change the viewer +until corrected. You can also add new nodes where appropriate: for +example, when "lights" is expanded right clicking on "lights" and typing +in a light type, such as "point", will add it to the scene. Similarly, +right clicking on "world" and creating an "Importer" node will add a new +scene importer from a file. Changing the filename to an appropriate file +will load the scene and propagate the resulting state. -![Screenshot of -`ospVolumeViewer`.](https://ospray.github.io/images/VolumeViewer.png) +Screenshot of ospExampleViewerSg Demos ----- @@ -1753,8 +1979,7 @@ at the [OSPRay Demos and Examples](http://www.ospray.org/demos.html) page. [^1]: For example, if OSPRay is in `~/Projects/ospray`, ISPC will also - be searched in `~/Projects/ispc-v1.9.1-linux` and - `~/Projects/ispc-v1.9.0-linux` + be searched in `~/Projects/ispc-v1.9.1-linux` [^2]: The [HDRI Light](#hdri-light) is an exception, it knows about `intensity`, but not about `color`. diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index aa515bdf42..01fda20b69 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -21,6 +21,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_LIST_DIR} common + common/boost.any ) # common utilities @@ -50,42 +51,7 @@ IF(OSPRAY_APPS_ENABLE_SCRIPTING) ADD_SUBDIRECTORY(common/script) ENDIF() -# mini-scene graph viewer implemented with GLUT -OPTION(OSPRAY_APPS_GLUTVIEWER "Build ospGlutViewer application." ON) - -IF(OSPRAY_APPS_GLUTVIEWER) - INCLUDE(glut) - # common utility classes for GLUT-based 3D viewer widget - ADD_SUBDIRECTORY(common/widgets) - ADD_SUBDIRECTORY(glutViewer) -ENDIF() - -# NOTE(jda) - Disable Qt based viewers when on OS X using ICC due to -# unresolved issues -# qt-based viewer for geometry (and soon volumes) -IF(NOT (APPLE AND OSPRAY_COMPILER_ICC) AND NOT WIN32) - OPTION(OSPRAY_APPS_QTVIEWER "Build ospQtViewer (Qt-based model viewer)" ON) - IF (OSPRAY_APPS_QTVIEWER) - ADD_SUBDIRECTORY(qtViewer) - ENDIF() -ENDIF() - -# volume viewer application -IF(NOT (APPLE AND OSPRAY_COMPILER_ICC)) - OPTION(OSPRAY_APPS_VOLUMEVIEWER "Build ospVolumeViewer application." ON) - IF(OSPRAY_APPS_VOLUMEVIEWER) - ADD_SUBDIRECTORY(volumeViewer) - ENDIF() -ENDIF() - -# ------------------------------------------------------- -# redistribute QT on Windows -# ------------------------------------------------------- -IF (OSPRAY_INSTALL_DEPENDENCIES AND WIN32 AND - (OSPRAY_APPS_QTVIEWER OR OSPRAY_APPS_VOLUMEVIEWER)) - FOREACH(QT_COMPONENT QTCORE QTGUI QTOPENGL) - STRING(REGEX REPLACE lib$ dll QT_DLL ${QT_${QT_COMPONENT}_LIBRARY_RELEASE}) - INSTALL(PROGRAMS ${QT_DLL} - DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT redist) - ENDFOREACH() +OPTION(OSPRAY_APPS_EXAMPLEVIEWER "Build example viewer apps" ON) +IF (OSPRAY_APPS_EXAMPLEVIEWER) + ADD_SUBDIRECTORY(exampleViewer) ENDIF() diff --git a/apps/bench/BenchScriptHandler.cpp b/apps/bench/BenchScriptHandler.cpp index b250bdec22..13829e434e 100644 --- a/apps/bench/BenchScriptHandler.cpp +++ b/apps/bench/BenchScriptHandler.cpp @@ -30,6 +30,8 @@ #include "BenchScriptHandler.h" +namespace bench { + BenchScriptHandler::BenchScriptHandler(std::shared_ptr &fixture) : OSPRayScriptHandler(fixture->model.handle(), fixture->renderer.handle(), fixture->camera.handle()) { @@ -128,6 +130,8 @@ void BenchScriptHandler::registerScriptTypes() { chai.add(m); } +} + #endif diff --git a/apps/bench/BenchScriptHandler.h b/apps/bench/BenchScriptHandler.h index 5c82ec714b..bb5f2bb70c 100644 --- a/apps/bench/BenchScriptHandler.h +++ b/apps/bench/BenchScriptHandler.h @@ -22,6 +22,8 @@ #include "common/script/OSPRayScriptHandler.h" #include "OSPRayFixture.h" +namespace bench { + class BenchScriptHandler : public ospray::OSPRayScriptHandler { public: BenchScriptHandler(std::shared_ptr &fixture); @@ -33,5 +35,7 @@ class BenchScriptHandler : public ospray::OSPRayScriptHandler { using BenchStats = pico_bench::Statistics; }; +} + #endif diff --git a/apps/bench/OSPRayFixture.cpp b/apps/bench/OSPRayFixture.cpp index 4710beb25d..7e8547a050 100644 --- a/apps/bench/OSPRayFixture.cpp +++ b/apps/bench/OSPRayFixture.cpp @@ -46,7 +46,6 @@ void writePPM(const std::string &fileName, const int sizeX, const int sizeY, fprintf(file, "\n"); fclose(file); } -} OSPRayFixture::OSPRayFixture(cpp::Renderer r, cpp::Camera c, cpp::Model m) : renderer(r), camera(c), model(m), width(DEFAULT_WIDTH), height(DEFAULT_HEIGHT), @@ -58,6 +57,7 @@ OSPRayFixture::OSPRayFixture(cpp::Renderer r, cpp::Camera c, cpp::Model m) renderer.set("camera", camera); renderer.commit(); } + pico_bench::Statistics OSPRayFixture::benchmark(const size_t warmUpFrames, const size_t benchFrames) { const size_t warmup = warmUpFrames == 0 ? defaultWarmupFrames : warmUpFrames; @@ -74,16 +74,18 @@ OSPRayFixture::benchmark(const size_t warmUpFrames, const size_t benchFrames) { stats.time_suffix = " ms"; return stats; } + void OSPRayFixture::saveImage(const std::string &fname) { auto *lfb = (uint32_t*)fb.map(OSP_FB_COLOR); bench::writePPM(fname + ".ppm", width, height, lfb); fb.unmap(lfb); } + void OSPRayFixture::setFrameBuffer(const int w, const int h, const int fbFlags) { width = w > 0 ? w : width; height = h > 0 ? h : height; - fb = cpp::FrameBuffer(osp::vec2i{width, height}, OSP_FB_SRGBA, fbFlags); + fb = cpp::FrameBuffer(vec2i{width, height}, OSP_FB_SRGBA, fbFlags); fb.clear(fbFlags); framebufferFlags = fbFlags; @@ -91,3 +93,4 @@ void OSPRayFixture::setFrameBuffer(const int w, const int h, const int fbFlags) camera.commit(); } +} diff --git a/apps/bench/OSPRayFixture.h b/apps/bench/OSPRayFixture.h index 41991db8a3..aa78526266 100644 --- a/apps/bench/OSPRayFixture.h +++ b/apps/bench/OSPRayFixture.h @@ -28,7 +28,6 @@ namespace bench { void writePPM(const std::string &fileName, const int sizeX, const int sizeY, const uint32_t *pixel); -} struct OSPRayFixture { using Millis = std::chrono::duration>; @@ -68,3 +67,4 @@ struct OSPRayFixture { int framebufferFlags; }; +}// ::bench diff --git a/apps/bench/bench.cpp b/apps/bench/bench.cpp index 294d8b6e7f..8e75845f55 100644 --- a/apps/bench/bench.cpp +++ b/apps/bench/bench.cpp @@ -22,225 +22,236 @@ #include "commandline/Utility.h" #include "pico_bench/pico_bench.h" -using std::cout; -using std::endl; -using std::string; - -void printUsageAndExit() -{ - cout << "Usage: ospBenchmark [options] model_file" << endl; - - cout << endl << "Args:" << endl; - - cout << endl; - cout << " model_file --> Scene used for benchmarking, supported types" - << " are:" << endl; - cout << " stl, msg, tri, xml, obj, hbp, x3d" << endl; - - cout << endl; - cout << "Options:" << endl; - - cout << endl; - cout << "**generic rendering options**" << endl; - - cout << endl; - cout << " -i | --image --> Specify the base filename to write the" - << " framebuffer to a file." << endl; - cout << " If ommitted, no file will be written." << endl; - cout << " NOTE: this option adds '.ppm' to the end of the" - << " filename" << endl; - - cout << endl; - cout << " -w | --width --> Specify the width of the benchmark frame" - << endl; - cout << " default: 1024" << endl; - - cout << endl; - cout << " -h | --height --> Specify the height of the benchmark frame" - << endl; - cout << " default: 1024" << endl; - - cout << endl; - cout << " -r | --renderer --> Specify the renderer to be benchmarked." - << endl; - cout << " Ex: -r pathtracer" << endl; - cout << " default: ao1" << endl; - - /* - * TODO: This was never used anyway? - cout << endl; - cout << " -bg | --background --> Specify the background color: R G B" - << endl; - */ - - cout << endl; - cout << " -wf | --warmup --> Specify the number of warmup frames: N" - << endl; - cout << " default: 10" << endl; - - cout << endl; - cout << " -bf | --bench --> Specify the number of benchmark frames: N" - << endl; - cout << " default: 100" << endl; - - cout << endl; - cout << " -lft | --log-frame-times --> Log frame time in ms for every frame rendered" - << endl; - - cout << endl; - cout << "**camera rendering options**" << endl; - - cout << endl; - cout << " -vp | --eye --> Specify the camera eye as: ex ey ez " << endl; - - cout << endl; - cout << " -vi | --gaze --> Specify the camera gaze point as: ix iy iz " - << endl; - - cout << endl; - cout << " -vu | --up --> Specify the camera up as: ux uy uz " << endl; - - - cout << endl; - cout << "**volume rendering options**" << endl; - - cout << endl; - cout << " -s | --sampling-rate --> Specify the sampling rate for volumes." - << endl; - cout << " default: 0.125" << endl; - - cout << endl; - cout << " -dr | --data-range --> Specify the data range for volumes." - << " If not specified, then the min and max data" << endl - << " values will be used when reading the data into memory." << endl; - cout << " Format: low high" << endl; - - cout << endl; - cout << " -tfc | --tf-color --> Specify the next color to in the transfer" - << " function for volumes. Each entry will add to the total list of" - << " colors in the order they are specified." << endl; - cout << " Format: R G B A" << endl; - cout << " Value Range: [0,1]" << endl; - - cout << " -tfs | --tf-scale --> Specify the opacity the transfer function" - << " will scale to: [0,x] where x is the input value." << endl; - cout << " default: 1.0" << endl; - - cout << " -tff | --tf-file --> Specify the transfer function file to use" - << endl; - - cout << endl; - cout << " -is | --surface --> Specify an isosurface at value: val " - << endl; - - cout << endl; - cout << " --help --> Print this help text" << endl; +namespace bench { + + using std::cout; + using std::endl; + using std::string; + + using namespace commandline; + + void printUsageAndExit() + { + cout << "Usage: ospBenchmark [options] model_file" << endl; + + cout << endl << "Args:" << endl; + + cout << endl; + cout << " model_file --> Scene used for benchmarking, supported types" + << " are:" << endl; + cout << " stl, msg, tri, xml, obj, hbp, x3d" << endl; + + cout << endl; + cout << "Options:" << endl; + + cout << endl; + cout << "**generic rendering options**" << endl; + + cout << endl; + cout << " -i | --image --> Specify the base filename to write the" + << " framebuffer to a file." << endl; + cout << " If ommitted, no file will be written." << endl; + cout << " NOTE: this option adds '.ppm' to the end of the" + << " filename" << endl; + + cout << endl; + cout << " -w | --width --> Specify the width of the benchmark frame" + << endl; + cout << " default: 1024" << endl; + + cout << endl; + cout << " -h | --height --> Specify the height of the benchmark frame" + << endl; + cout << " default: 1024" << endl; + + cout << endl; + cout << " -r | --renderer --> Specify the renderer to be benchmarked." + << endl; + cout << " Ex: -r pathtracer" << endl; + cout << " default: ao1" << endl; + + /* + * TODO: This was never used anyway? + cout << endl; + cout << " -bg | --background --> Specify the background color: R G B" + << endl; + */ + + cout << endl; + cout << " -wf | --warmup --> Specify the number of warmup frames: N" + << endl; + cout << " default: 10" << endl; + + cout << endl; + cout << " -bf | --bench --> Specify the number of benchmark frames: N" + << endl; + cout << " default: 100" << endl; + + cout << endl; + cout << " -lft | --log-frame-times --> Log frame time in ms for every frame rendered" + << endl; + + cout << endl; + cout << "**camera rendering options**" << endl; + + cout << endl; + cout << " -vp | --eye --> Specify the camera eye as: ex ey ez " << endl; + + cout << endl; + cout << " -vi | --gaze --> Specify the camera gaze point as: ix iy iz " + << endl; + + cout << endl; + cout << " -vu | --up --> Specify the camera up as: ux uy uz " << endl; + + + cout << endl; + cout << "**volume rendering options**" << endl; + + cout << endl; + cout << " -s | --sampling-rate --> Specify the sampling rate for volumes." + << endl; + cout << " default: 0.125" << endl; + + cout << endl; + cout << " -dr | --data-range --> Specify the data range for volumes." + << " If not specified, then the min and max data" << endl + << " values will be used when reading the data into memory." << endl; + cout << " Format: low high" << endl; + + cout << endl; + cout << " -tfc | --tf-color --> Specify the next color to in the transfer" + << " function for volumes. Each entry will add to the total list of" + << " colors in the order they are specified." << endl; + cout << " Format: R G B A" << endl; + cout << " Value Range: [0,1]" << endl; + + cout << " -tfs | --tf-scale --> Specify the opacity the transfer function" + << " will scale to: [0,x] where x is the input value." << endl; + cout << " default: 1.0" << endl; + + cout << " -tff | --tf-file --> Specify the transfer function file to use" + << endl; + + cout << endl; + cout << " -is | --surface --> Specify an isosurface at value: val " + << endl; + + cout << endl; + cout << " --help --> Print this help text" << endl; #ifdef OSPRAY_APPS_ENABLE_SCRIPTING - cout << endl; - cout << " --script --> Specify a script file to drive the benchmarker.\n" - << " In a script you can access the parsed world and command\n" - << " line benchmark configuration via the following variables:\n" - << " defaultFixture -> benchmark settings from the commandline\n" - << " m -> world model parsed from command line scene args\n" - << " c -> camera set from command line args\n" - << " r -> renderer set from command line args\n"; + cout << endl; + cout << " --script --> Specify a script file to drive the benchmarker.\n" + << " In a script you can access the parsed world and command\n" + << " line benchmark configuration via the following variables:\n" + << " defaultFixture -> benchmark settings from the commandline\n" + << " m -> world model parsed from command line scene args\n" + << " c -> camera set from command line args\n" + << " r -> renderer set from command line args\n"; #endif - exit(0); -} - -std::string imageOutputFile = ""; -std::string scriptFile = ""; -size_t numWarmupFrames = 0; -size_t numBenchFrames = 0; -bool logFrameTimes = false; -// This is the fixture setup by the command line arguments -std::shared_ptr cmdlineFixture; - -void parseCommandLine(int argc, const char *argv[]) -{ - using namespace ospcommon; - using namespace ospray::cpp; - if (argc <= 1) { - printUsageAndExit(); + exit(0); } - int width = 0; - int height = 0; - for (int i = 1; i < argc; ++i) { - string arg = argv[i]; - if (arg == "--help") { + std::string imageOutputFile = ""; + std::string scriptFile = ""; + size_t numWarmupFrames = 0; + size_t numBenchFrames = 0; + bool logFrameTimes = false; + // This is the fixture setup by the command line arguments + std::shared_ptr cmdlineFixture; + + void parseCommandLine(int argc, const char *argv[]) + { + using namespace ospcommon; + using namespace ospray::cpp; + if (argc <= 1) { printUsageAndExit(); - } else if (arg == "-i" || arg == "--image") { - imageOutputFile = argv[++i]; - } else if (arg == "-w" || arg == "--width") { - width = atoi(argv[++i]); - } else if (arg == "-h" || arg == "--height") { - height = atoi(argv[++i]); - } else if (arg == "-wf" || arg == "--warmup") { - numWarmupFrames = atoi(argv[++i]); - } else if (arg == "-bf" || arg == "--bench") { - numBenchFrames = atoi(argv[++i]); - } else if (arg == "-lft" || arg == "--log-frame-times") { - logFrameTimes = true; - } else if (arg == "--script") { - scriptFile = argv[++i]; } - } - auto ospObjs = parseWithDefaultParsers(argc, argv); + int width = 0; + int height = 0; + for (int i = 1; i < argc; ++i) { + string arg = argv[i]; + if (arg == "--help") { + printUsageAndExit(); + } else if (arg == "-i" || arg == "--image") { + imageOutputFile = argv[++i]; + } else if (arg == "-w" || arg == "--width") { + width = atoi(argv[++i]); + } else if (arg == "-h" || arg == "--height") { + height = atoi(argv[++i]); + } else if (arg == "-wf" || arg == "--warmup") { + numWarmupFrames = atoi(argv[++i]); + } else if (arg == "-bf" || arg == "--bench") { + numBenchFrames = atoi(argv[++i]); + } else if (arg == "-lft" || arg == "--log-frame-times") { + logFrameTimes = true; + } else if (arg == "--script") { + scriptFile = argv[++i]; + } + } - std::deque model; - Renderer renderer; - Camera camera; - std::tie(std::ignore, model, renderer, camera) = ospObjs; - cmdlineFixture = std::make_shared(renderer, camera, model[0]); - if (width > 0 || height > 0) { - cmdlineFixture->setFrameBuffer(width, height); - } - // Set the default warm up and bench frames - if (numWarmupFrames > 0) { - cmdlineFixture->defaultWarmupFrames = numWarmupFrames; - } - if (numBenchFrames > 0){ - cmdlineFixture->defaultBenchFrames = numBenchFrames; + auto ospObjs = parseWithDefaultParsers(argc, argv); + + std::deque model; + Renderer renderer; + Camera camera; + std::tie(std::ignore, model, renderer, camera) = ospObjs; + + cmdlineFixture = std::make_shared(renderer, camera, model[0]); + if (width > 0 || height > 0) { + cmdlineFixture->setFrameBuffer(width, height); + } + // Set the default warm up and bench frames + if (numWarmupFrames > 0) { + cmdlineFixture->defaultWarmupFrames = numWarmupFrames; + } + if (numBenchFrames > 0){ + cmdlineFixture->defaultBenchFrames = numBenchFrames; + } } -} -int main(int argc, const char *argv[]) { - ospInit(&argc, argv); - parseCommandLine(argc, argv); - - if (scriptFile.empty()) { - // If we don't have a script do this, otherwise run the script - // and let it setup bench scenes and benchmrk them and so on - auto stats = cmdlineFixture->benchmark(); - if (logFrameTimes) { - for (size_t i = 0; i < stats.size(); ++i) { - std::cout << stats[i].count() << stats.time_suffix << "\n"; - } + extern "C" int main(int argc, const char *argv[]) { + int init_error = ospInit(&argc, argv); + if (init_error != OSP_NO_ERROR) { + std::cerr << "FATAL ERROR DURING INITIALIZATION!" << std::endl; + return init_error; } - std::cout << "Frame Time " << stats << "\n" - << "FPS Statistics:\n" - << "\tmax: " << 1000.0 / stats.min().count() << " fps\n" - << "\tmin: " << 1000.0 / stats.max().count() << " fps\n" - << "\tmedian: " << 1000.0 / stats.median().count() << " fps\n" - << "\tmean: " << 1000.0 / stats.mean().count() << " fps\n"; - } else { + + parseCommandLine(argc, argv); + + if (scriptFile.empty()) { + // If we don't have a script do this, otherwise run the script + // and let it setup bench scenes and benchmrk them and so on + auto stats = cmdlineFixture->benchmark(); + if (logFrameTimes) { + for (size_t i = 0; i < stats.size(); ++i) { + std::cout << stats[i].count() << stats.time_suffix << "\n"; + } + } + std::cout << "Frame Time " << stats << "\n" + << "FPS Statistics:\n" + << "\tmax: " << 1000.0 / stats.min().count() << " fps\n" + << "\tmin: " << 1000.0 / stats.max().count() << " fps\n" + << "\tmedian: " << 1000.0 / stats.median().count() << " fps\n" + << "\tmean: " << 1000.0 / stats.mean().count() << " fps\n"; + } else { #ifdef OSPRAY_APPS_ENABLE_SCRIPTING - // The script will be responsible for setting up the benchmark config - // and calling `benchmark(N)` to benchmark the scene - BenchScriptHandler scriptHandler(cmdlineFixture); - scriptHandler.runScriptFromFile(scriptFile); + // The script will be responsible for setting up the benchmark config + // and calling `benchmark(N)` to benchmark the scene + BenchScriptHandler scriptHandler(cmdlineFixture); + scriptHandler.runScriptFromFile(scriptFile); #else - throw std::runtime_error("You must build with OSPRAY_APPS_ENABLE_SCRIPTING=ON " - "to use scripting"); + throw std::runtime_error("You must build with OSPRAY_APPS_ENABLE_SCRIPTING=ON " + "to use scripting"); #endif - } + } - if (!imageOutputFile.empty()) { - cmdlineFixture->saveImage(imageOutputFile); + if (!imageOutputFile.empty()) { + cmdlineFixture->saveImage(imageOutputFile); + } + return 0; } - return 0; -} +} diff --git a/apps/common/commandline/LightsParser.cpp b/apps/common/commandline/LightsParser.cpp index ee9bb798d8..f019bf324b 100644 --- a/apps/common/commandline/LightsParser.cpp +++ b/apps/common/commandline/LightsParser.cpp @@ -15,56 +15,55 @@ // ======================================================================== // #include "LightsParser.h" - #include - #include - #include -using namespace ospcommon; +namespace commandline { -DefaultLightsParser::DefaultLightsParser(ospray::cpp::Renderer renderer) : - renderer(renderer), - defaultDirLight_direction(.3, -1, -.2), defaultDirLight_intensity(3.14f) -{ -} + using namespace ospcommon; -bool DefaultLightsParser::parse(int ac, const char **&av) -{ - std::vector lights; + DefaultLightsParser::DefaultLightsParser(ospray::cpp::Renderer renderer) : + renderer(renderer), + defaultDirLight_direction(.3, -1, -.2), defaultDirLight_intensity(3.14f) + { + } + + bool DefaultLightsParser::parse(int ac, const char **&av) + { + std::vector lights; - int HDRI_up = 1;//y - float HDRI_intensity = 0.f; - const char * HDRI_file_name; - vec4f ambient(.85,.9,1,.2*3.14); + int HDRI_up = 1;//y + float HDRI_intensity = 0.f; + const char * HDRI_file_name; + vec4f ambient(.85,.9,1,.2*3.14); - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--sun-dir") { - if (!strcmp(av[i+1],"none")) { - ++i; - defaultDirLight_direction = vec3f(0.f); - } else { - defaultDirLight_direction.x = atof(av[++i]); - defaultDirLight_direction.y = atof(av[++i]); - defaultDirLight_direction.z = atof(av[++i]); - } - } else if (arg == "--sun-int") { + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--sun-dir") { + if (!strcmp(av[i+1],"none")) { + ++i; + defaultDirLight_direction = vec3f(0.f); + } else { + defaultDirLight_direction.x = atof(av[++i]); + defaultDirLight_direction.y = atof(av[++i]); + defaultDirLight_direction.z = atof(av[++i]); + } + } else if (arg == "--sun-int") { defaultDirLight_intensity = atof(av[++i]); - } else if (arg == "--ambient") { + } else if (arg == "--ambient") { ambient.x = atof(av[++i]); ambient.y = atof(av[++i]); ambient.z = atof(av[++i]); ambient.w = atof(av[++i]); - } else if (arg == "--hdri-light") { + } else if (arg == "--hdri-light") { if (i+2 >= ac) throw std::runtime_error("Not enough arguments! Usage:\n\t" - "--hdri-light .(pfm|ppm)"); + "--hdri-light .(pfm|ppm)"); HDRI_intensity = atof(av[++i]); HDRI_file_name = av[++i]; - } else if (arg == "--backplate") { + } else if (arg == "--backplate") { FileName imageFile(av[++i]); ospray::miniSG::Texture2D *backplate = ospray::miniSG::loadTexture(imageFile.path(), imageFile.base()); if (backplate == NULL){ @@ -72,86 +71,88 @@ bool DefaultLightsParser::parse(int ac, const char **&av) } OSPTexture2D ospBackplate = ospray::miniSG::createTexture2D(backplate); renderer.set("backplate", ospBackplate); - } else if (arg == "--hdri-up") { - if (!strcmp(av[i+1],"x") || !strcmp(av[i+1],"X")) { - HDRI_up = 0; - } else if (!strcmp(av[i+1],"y") || !strcmp(av[i+1],"Y")) { - HDRI_up = 1; - } else if (!strcmp(av[i+1],"z") || !strcmp(av[i+1],"Z")) { - HDRI_up = 2; - } else { - printf("--hdri-up must be x, y, or z.\n"); + } else if (arg == "--hdri-up") { + if (!strcmp(av[i+1],"x") || !strcmp(av[i+1],"X")) { + HDRI_up = 0; + } else if (!strcmp(av[i+1],"y") || !strcmp(av[i+1],"Y")) { + HDRI_up = 1; + } else if (!strcmp(av[i+1],"z") || !strcmp(av[i+1],"Z")) { + HDRI_up = 2; + } else { + printf("--hdri-up must be x, y, or z.\n"); + } } - } - }// Done reading commandline args. + }// Done reading commandline args. - // HDRI environment light. - if (HDRI_intensity > 0.f) { - auto ospHdri = renderer.newLight("hdri"); - ospHdri.set("name", "hdri light"); - if (HDRI_up == 0) {// up = x - ospHdri.set("up", 1.f, 0.f, 0.f); - ospHdri.set("dir", 0.f, 0.f, 1.0f); - } else if (HDRI_up == 1) {// up = y - ospHdri.set("up", 0.f, 1.f, 0.f); - ospHdri.set("dir", 1.f, 0.f, 0.0f); - } else if (HDRI_up == 2) {// up = z - ospHdri.set("up", 0.f, 0.f, 1.f); - ospHdri.set("dir", 0.f, 1.f, 0.0f); - } - ospHdri.set( "intensity", HDRI_intensity); - FileName imageFile(HDRI_file_name); - ospray::miniSG::Texture2D *lightMap = ospray::miniSG::loadTexture(imageFile.path(), imageFile.base()); - if (lightMap == NULL){ - std::cout << "Failed to load hdri-light texture '" << imageFile << "'" << std::endl; - } else { - std::cout << "Successfully loaded hdri-light texture '" << imageFile << "'" << std::endl; - OSPTexture2D ospLightMap = ospray::miniSG::createTexture2D(lightMap); - ospHdri.set( "map", ospLightMap); - ospHdri.commit(); - lights.push_back(ospHdri.handle()); + // HDRI environment light. + if (HDRI_intensity > 0.f) { + auto ospHdri = renderer.newLight("hdri"); + ospHdri.set("name", "hdri light"); + if (HDRI_up == 0) {// up = x + ospHdri.set("up", 1.f, 0.f, 0.f); + ospHdri.set("dir", 0.f, 0.f, 1.0f); + } else if (HDRI_up == 1) {// up = y + ospHdri.set("up", 0.f, 1.f, 0.f); + ospHdri.set("dir", 1.f, 0.f, 0.0f); + } else if (HDRI_up == 2) {// up = z + ospHdri.set("up", 0.f, 0.f, 1.f); + ospHdri.set("dir", 0.f, 1.f, 0.0f); + } + ospHdri.set( "intensity", HDRI_intensity); + FileName imageFile(HDRI_file_name); + ospray::miniSG::Texture2D *lightMap = ospray::miniSG::loadTexture(imageFile.path(), imageFile.base()); + if (lightMap == NULL){ + std::cout << "Failed to load hdri-light texture '" << imageFile << "'" << std::endl; + } else { + std::cout << "Successfully loaded hdri-light texture '" << imageFile << "'" << std::endl; + OSPTexture2D ospLightMap = ospray::miniSG::createTexture2D(lightMap); + ospHdri.set( "map", ospLightMap); + ospHdri.commit(); + lights.push_back(ospHdri.handle()); + } } - } - //TODO: Need to figure out where we're going to read lighting data from + //TODO: Need to figure out where we're going to read lighting data from - if (defaultDirLight_direction != vec3f(0.f) - && defaultDirLight_intensity > 0.f) { - auto ospLight = renderer.newLight("directional"); - if (ospLight.handle() == nullptr) { - throw std::runtime_error("Failed to create a 'DirectionalLight'!"); + if (defaultDirLight_direction != vec3f(0.f) + && defaultDirLight_intensity > 0.f) { + auto ospLight = renderer.newLight("directional"); + if (ospLight.handle() == nullptr) { + throw std::runtime_error("Failed to create a 'DirectionalLight'!"); + } + ospLight.set("name", "sun"); + ospLight.set("color", 1.f, .94f, .88f); + ospLight.set("direction", defaultDirLight_direction); + ospLight.set("intensity", defaultDirLight_intensity); + ospLight.set("angularDiameter", 0.53f); + ospLight.commit(); + lights.push_back(ospLight.handle()); } - ospLight.set("name", "sun"); - ospLight.set("color", 1.f, .94f, .88f); - ospLight.set("direction", defaultDirLight_direction); - ospLight.set("intensity", defaultDirLight_intensity); - ospLight.set("angularDiameter", 0.53f); - ospLight.commit(); - lights.push_back(ospLight.handle()); - } - if (ambient.w > 0.f && reduce_max(ambient) > 0.f) { - auto ospLight = renderer.newLight("ambient"); - if (ospLight.handle() == nullptr) { - throw std::runtime_error("Failed to create a 'AmbientLight'!"); + if (ambient.w > 0.f && reduce_max(ambient) > 0.f) { + auto ospLight = renderer.newLight("ambient"); + if (ospLight.handle() == nullptr) { + throw std::runtime_error("Failed to create a 'AmbientLight'!"); + } + ospLight.set("name", "ambient"); + ospLight.set("color", ambient.x, ambient.y, ambient.z); + ospLight.set("intensity", ambient.w); + ospLight.commit(); + lights.push_back(ospLight.handle()); } - ospLight.set("name", "ambient"); - ospLight.set("color", ambient.x, ambient.y, ambient.z); - ospLight.set("intensity", ambient.w); - ospLight.commit(); - lights.push_back(ospLight.handle()); - } - auto lightArray = ospray::cpp::Data(lights.size(), OSP_OBJECT, lights.data()); - //lightArray.commit(); - renderer.set("lights", lightArray); + auto lightArray = ospray::cpp::Data(lights.size(), OSP_OBJECT, lights.data()); + //lightArray.commit(); + renderer.set("lights", lightArray); - finalize(); + finalize(); - return true; -} + return true; + } -void DefaultLightsParser::finalize() -{ + void DefaultLightsParser::finalize() + { + } + } diff --git a/apps/common/commandline/LightsParser.h b/apps/common/commandline/LightsParser.h index 5188259b34..0130c53e5f 100644 --- a/apps/common/commandline/LightsParser.h +++ b/apps/common/commandline/LightsParser.h @@ -22,27 +22,31 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE LightsParser : public CommandLineParser -{ -public: - virtual ~LightsParser() = default; -}; - -class OSPRAY_COMMANDLINE_INTERFACE DefaultLightsParser : public LightsParser -{ -public: - DefaultLightsParser(ospray::cpp::Renderer renderer); - bool parse(int ac, const char **&av) override; - -protected: - - ospray::cpp::Renderer renderer; - - /*! when using the OBJ renderer, we create a automatic dirlight with this - * direction; use ''--sun-dir x y z' to change */ - ospcommon::vec3f defaultDirLight_direction; - float defaultDirLight_intensity; -private: - - void finalize(); -}; +namespace commandline { + + class OSPRAY_COMMANDLINE_INTERFACE LightsParser : public CommandLineParser + { + public: + virtual ~LightsParser() = default; + }; + + class OSPRAY_COMMANDLINE_INTERFACE DefaultLightsParser : public LightsParser + { + public: + DefaultLightsParser(ospray::cpp::Renderer renderer); + bool parse(int ac, const char **&av) override; + + protected: + + ospray::cpp::Renderer renderer; + + /*! when using the OBJ renderer, we create a automatic dirlight with this + * direction; use ''--sun-dir x y z' to change */ + ospcommon::vec3f defaultDirLight_direction; + float defaultDirLight_intensity; + private: + + void finalize(); + }; + +} diff --git a/apps/common/commandline/RendererParser.cpp b/apps/common/commandline/RendererParser.cpp index 8d2a0e85a8..9fda389aff 100644 --- a/apps/common/commandline/RendererParser.cpp +++ b/apps/common/commandline/RendererParser.cpp @@ -16,51 +16,60 @@ #include "RendererParser.h" -bool DefaultRendererParser::parse(int ac, const char **&av) -{ - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--renderer" || arg == "-r") { - assert(i+1 < ac); - rendererType = av[++i]; - } else if (arg == "--spp" || arg == "-spp") { - spp = atoi(av[++i]); - } else if (arg == "--noshadows" || arg == "-ns") { - shadows = 0; - } else if (arg == "--ao-samples" || arg == "-ao") { - aoSamples = atoi(av[++i]); - } else if (arg == "--max-depth") { - maxDepth = atoi(av[++i]); +namespace commandline { + + bool DefaultRendererParser::parse(int ac, const char **&av) + { + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--renderer" || arg == "-r") { + assert(i+1 < ac); + rendererType = av[++i]; + } else if (arg == "--spp" || arg == "-spp") { + spp = atoi(av[++i]); + } else if (arg == "--noshadows" || arg == "-ns") { + shadows = 0; + } else if (arg == "--ao-samples" || arg == "-ao") { + aoSamples = atoi(av[++i]); + } else if (arg == "--max-depth") { + maxDepth = atoi(av[++i]); + } } + + // iw - moved finalize into create() so we can create multiple + // renderers from one parsed set of settings + // finalize(); + + return true; } - finalize(); + ospray::cpp::Renderer DefaultRendererParser::create() + { + // iw - moved finalize into create() so we can create multiple + // renderers from one parsed set of settings + finalize(); + return parsedRenderer; + } - return true; -} + void DefaultRendererParser::finalize() + { + if (rendererType.empty()) + rendererType = "scivis"; -ospray::cpp::Renderer DefaultRendererParser::renderer() -{ - return parsedRenderer; -} + parsedRenderer = ospray::cpp::Renderer(rendererType.c_str()); -void DefaultRendererParser::finalize() -{ - if (rendererType.empty()) - rendererType = "scivis"; + // Set renderer defaults (if not using 'aoX' renderers) + if (rendererType[0] != 'a' && rendererType[1] != 'o') + { + parsedRenderer.set("aoSamples", aoSamples); + parsedRenderer.set("shadowsEnabled", shadows); + parsedRenderer.set("aoTransparencyEnabled", 1); + } - parsedRenderer = ospray::cpp::Renderer(rendererType.c_str()); + parsedRenderer.set("spp", spp); + parsedRenderer.set("maxDepth", maxDepth); - // Set renderer defaults (if not using 'aoX' renderers) - if (rendererType[0] != 'a' && rendererType[1] != 'o') - { - parsedRenderer.set("aoSamples", aoSamples); - parsedRenderer.set("shadowsEnabled", shadows); - parsedRenderer.set("aoTransparencyEnabled", 1); + parsedRenderer.commit(); } - parsedRenderer.set("spp", spp); - parsedRenderer.set("maxDepth", maxDepth); - - parsedRenderer.commit(); } diff --git a/apps/common/commandline/RendererParser.h b/apps/common/commandline/RendererParser.h index ec3790a272..6ac1b14308 100644 --- a/apps/common/commandline/RendererParser.h +++ b/apps/common/commandline/RendererParser.h @@ -22,31 +22,33 @@ #include -class OSPRAY_COMMANDLINE_INTERFACE RendererParser : public CommandLineParser -{ -public: - virtual ~RendererParser() = default; - virtual ospray::cpp::Renderer renderer() = 0; -}; - -class OSPRAY_COMMANDLINE_INTERFACE DefaultRendererParser : public RendererParser -{ -public: - DefaultRendererParser() = default; - bool parse(int ac, const char **&av) override; - ospray::cpp::Renderer renderer() override; - -protected: - - std::string rendererType; - ospray::cpp::Renderer parsedRenderer; - - int spp{1}; - int maxDepth{5}; - int shadows{1}; - int aoSamples{1}; - -private: - - void finalize(); -}; +namespace commandline { + class OSPRAY_COMMANDLINE_INTERFACE RendererParser : public CommandLineParser + { + public: + virtual ~RendererParser() = default; + virtual ospray::cpp::Renderer create() = 0; + }; + + class OSPRAY_COMMANDLINE_INTERFACE DefaultRendererParser : public RendererParser + { + public: + DefaultRendererParser() = default; + bool parse(int ac, const char **&av) override; + ospray::cpp::Renderer create() override; + + protected: + + std::string rendererType; + ospray::cpp::Renderer parsedRenderer; + + int spp{1}; + int maxDepth{5}; + int shadows{1}; + int aoSamples{1}; + + private: + + void finalize(); + }; +} diff --git a/apps/common/commandline/SceneParser/MultiSceneParser.cpp b/apps/common/commandline/SceneParser/MultiSceneParser.cpp index 31eb5aff1d..8801026ff9 100644 --- a/apps/common/commandline/SceneParser/MultiSceneParser.cpp +++ b/apps/common/commandline/SceneParser/MultiSceneParser.cpp @@ -26,70 +26,100 @@ # include "volume/VolumeSceneParser.h" #endif -using namespace ospray; -using namespace ospcommon; +namespace commandline { -MultiSceneParser::MultiSceneParser(cpp::Renderer renderer) : - renderer(renderer) -{ -} + using namespace ospray; + using namespace ospcommon; -bool MultiSceneParser::parse(int ac, const char **&av) -{ - TriangleMeshSceneParser triangleMeshParser(renderer); + MultiSceneParser::MultiSceneParser(cpp::Renderer renderer) + : renderer(renderer) + { + } + + bool MultiSceneParser::parse(int ac, const char **&av) + { + TriangleMeshSceneParser triangleMeshParser(renderer); #ifdef OSPRAY_TACHYON_SUPPORT - TachyonSceneParser tachyonParser(renderer); + TachyonSceneParser tachyonParser(renderer); #endif - ParticleSceneParser particleParser(renderer); - StreamLineSceneParser streamlineParser(renderer); + ParticleSceneParser particleParser(renderer); + StreamLineSceneParser streamlineParser(renderer); #ifndef _WIN32 - VolumeSceneParser volumeParser(renderer); + VolumeSceneParser volumeParser(renderer); #endif - bool gotTriangleMeshScene = triangleMeshParser.parse(ac, av); + bool gotTriangleMeshScene = triangleMeshParser.parse(ac, av); #ifdef OSPRAY_TACHYON_SUPPORT - bool gotTachyonScene = tachyonParser.parse(ac, av); + bool gotTachyonScene = tachyonParser.parse(ac, av); #endif - bool gotParticleScene = particleParser.parse(ac, av); - bool gotStreamLineScene = streamlineParser.parse(ac, av); + bool gotParticleScene = particleParser.parse(ac, av); + bool gotStreamLineScene = streamlineParser.parse(ac, av); #ifndef _WIN32 - bool gotVolumeScene = volumeParser.parse(ac, av); + bool gotVolumeScene = volumeParser.parse(ac, av); #endif - SceneParser *parser = nullptr; + SceneParser *parser = nullptr; - if (gotTriangleMeshScene) - parser = &triangleMeshParser; + if (gotTriangleMeshScene) + parser = &triangleMeshParser; #ifdef OSPRAY_TACHYON_SUPPORT - else if (gotTachyonScene) - parser = &tachyonParser; + else if (gotTachyonScene) + parser = &tachyonParser; #endif - else if (gotParticleScene) - parser = &particleParser; - else if (gotStreamLineScene) - parser = &streamlineParser; + else if (gotParticleScene) + parser = &particleParser; + else if (gotStreamLineScene) + parser = &streamlineParser; #ifndef _WIN32 - else if (gotVolumeScene) - parser = &volumeParser; + else if (gotVolumeScene) + parser = &volumeParser; #endif - if (parser) { - sceneModels = parser->model(); - sceneBboxes = parser->bbox(); - } else { - sceneModels.push_back(cpp::Model()); - sceneModels[0].commit(); + + if (parser) { + sceneModels = parser->model(); + sceneBboxes = parser->bbox(); + } else { + sceneModels.push_back(cpp::Model()); + sceneModels[0].commit(); + } + + /* iw, mar 17 - since the above code creates a single model for + every file it loads we here merge them all together; the bette + way would be to have a single model to contain several meshes - + that would be faster _and_ cleaner - but that requires + significant changes to how above code works ... */ + if (sceneModels.size() > 1) { + std::cout << "#msp: forced merging of multiple input models into a single model" << std::endl; + ospray::cpp::Model jointModel; + for (int i=0;i 1) { + box3f mergedBounds = ospcommon::empty; + for (auto box : sceneBboxes) + mergedBounds.extend(box); + sceneBboxes.clear(); + sceneBboxes.push_back(mergedBounds); + } + + return parser != nullptr; } - return parser != nullptr; -} + std::deque MultiSceneParser::model() const + { + return sceneModels; + } -std::deque MultiSceneParser::model() const -{ - return sceneModels; -} + std::deque MultiSceneParser::bbox() const + { + return sceneBboxes; + } -std::deque MultiSceneParser::bbox() const -{ - return sceneBboxes; -} \ No newline at end of file +} // ::commandline diff --git a/apps/common/commandline/SceneParser/MultiSceneParser.h b/apps/common/commandline/SceneParser/MultiSceneParser.h index ffcf4f1812..467767d93d 100644 --- a/apps/common/commandline/SceneParser/MultiSceneParser.h +++ b/apps/common/commandline/SceneParser/MultiSceneParser.h @@ -20,23 +20,27 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE MultiSceneParser : public SceneParser -{ -public: - MultiSceneParser(ospray::cpp::Renderer); +namespace commandline { - bool parse(int ac, const char **&av) override; + class OSPRAY_COMMANDLINE_INTERFACE MultiSceneParser : public SceneParser + { + public: + MultiSceneParser(ospray::cpp::Renderer); - std::deque model() const override; - std::deque bbox() const override; + bool parse(int ac, const char **&av) override; -protected: + std::deque model() const override; + std::deque bbox() const override; - ospray::cpp::Renderer renderer; - std::deque sceneModels; - std::deque sceneBboxes; + protected: -private: + ospray::cpp::Renderer renderer; + std::deque sceneModels; + std::deque sceneBboxes; - void finalize(); -}; + private: + + void finalize(); + }; + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/SceneParser.h b/apps/common/commandline/SceneParser/SceneParser.h index 157b4a20b0..5e8d1a6d3c 100644 --- a/apps/common/commandline/SceneParser/SceneParser.h +++ b/apps/common/commandline/SceneParser/SceneParser.h @@ -22,10 +22,14 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE SceneParser : public CommandLineParser -{ -public: - virtual ~SceneParser() = default; - virtual std::deque model() const = 0; - virtual std::deque bbox() const = 0; -}; +namespace commandline { + + class OSPRAY_COMMANDLINE_INTERFACE SceneParser : public CommandLineParser + { + public: + virtual ~SceneParser() = default; + virtual std::deque model() const = 0; + virtual std::deque bbox() const = 0; + }; + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/particle/Model.cpp b/apps/common/commandline/SceneParser/particle/Model.cpp index a524a45ddc..802f44346d 100644 --- a/apps/common/commandline/SceneParser/particle/Model.cpp +++ b/apps/common/commandline/SceneParser/particle/Model.cpp @@ -123,7 +123,9 @@ namespace ospray { if (!fgets(line,10000,file)) throw std::runtime_error("could not fgets"); - std::cout << "#" << fileName << " (.dat.xyz format): expecting " << numAtoms << " atoms" << std::endl; + std::cout << "#" << fileName << " (.dat.xyz format): expecting " + << numAtoms << " atoms" << std::endl; + for (int i=0;iradius; + if (a.radius == 0.f) + a.radius = defaultRadius; + if (a.radius == 0.f) + throw std::runtime_error("particle has invalid radius. Either " + "specify proper radius in the def file, or " + "use '--radius' cmdline parameter to set a " + "valid radius"); + atom.push_back(a); + } + } + box3f Model::getBBox() const { box3f bbox = empty; diff --git a/apps/common/commandline/SceneParser/particle/Model.h b/apps/common/commandline/SceneParser/particle/Model.h index 2d224d7334..e6fcd0abf1 100644 --- a/apps/common/commandline/SceneParser/particle/Model.h +++ b/apps/common/commandline/SceneParser/particle/Model.h @@ -58,6 +58,7 @@ namespace ospray { /*! load xyz files in which there is *no* atom count, but just a list of "type x y z" lines */ void loadXYZ2(const std::string &fileName); + void loadXYZ3(const std::string &fileName); box3f getBBox() const; diff --git a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp index 4a13b480d0..6962c3383a 100644 --- a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp +++ b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.cpp @@ -22,108 +22,115 @@ #include -using namespace ospray; -using namespace ospcommon; - -// TODO: Need to convert ospray.h calls to ospray::cpp objects! - -// Helper functions /////////////////////////////////////////////////////////// - -particle::Model *createTestCube(int numPerSide) -{ - particle::Model *m = new particle::Model; - int type = m->getAtomType("testParticle"); - for (int z=0;zatom.push_back(a); - } - return m; -} +namespace commandline { + + using namespace ospray; + using namespace ospcommon; + + // TODO: Need to convert ospray.h calls to ospray::cpp objects! + + // Helper functions /////////////////////////////////////////////////////////// + + particle::Model *createTestCube(int numPerSide) + { + particle::Model *m = new particle::Model; + int type = m->getAtomType("testParticle"); + for (int z=0;zatom.push_back(a); + } + return m; + } -OSPData makeMaterials(OSPRenderer renderer, particle::Model *model) -{ - int numMaterials = model->atomType.size(); - std::vector matArray(numMaterials); - for (int i = 0; i < numMaterials; i++) { - OSPMaterial mat = ospNewMaterial(renderer,"OBJMaterial"); - assert(mat); - ospSet3fv(mat,"kd",&model->atomType[i]->color.x); - ospCommit(mat); - matArray[i] = mat; + OSPData makeMaterials(OSPRenderer renderer, particle::Model *model) + { + int numMaterials = model->atomType.size(); + std::vector matArray(numMaterials); + for (int i = 0; i < numMaterials; i++) { + OSPMaterial mat = ospNewMaterial(renderer,"OBJMaterial"); + assert(mat); + ospSet3fv(mat,"kd",&model->atomType[i]->color.x); + ospCommit(mat); + matArray[i] = mat; + } + OSPData data = ospNewData(numMaterials,OSP_OBJECT,matArray.data()); + ospCommit(data); + return data; } - OSPData data = ospNewData(numMaterials,OSP_OBJECT,matArray.data()); - ospCommit(data); - return data; -} -// Helper types /////////////////////////////////////////////////////////////// + // Helper types /////////////////////////////////////////////////////////////// -struct DeferredLoadJob { - DeferredLoadJob(particle::Model *model, - const FileName &xyzFileName, - const FileName &defFileName) - : model(model), xyzFileName(xyzFileName), defFileName(defFileName) - {} + struct DeferredLoadJob { + DeferredLoadJob(particle::Model *model, + const FileName &xyzFileName, + const FileName &defFileName) + : model(model), xyzFileName(xyzFileName), defFileName(defFileName) + {} - //! the mode we still have to load - particle::Model *model; - //! file name of xyz file to be loaded into this model - FileName xyzFileName; - //! name of atom type defintion file active when this xyz file was added - FileName defFileName; -}; + //! the mode we still have to load + particle::Model *model; + //! file name of xyz file to be loaded into this model + FileName xyzFileName; + //! name of atom type defintion file active when this xyz file was added + FileName defFileName; + }; -// Class definitions ////////////////////////////////////////////////////////// + // Class definitions ////////////////////////////////////////////////////////// -ParticleSceneParser::ParticleSceneParser(cpp::Renderer renderer) : - renderer(renderer) -{ -} + ParticleSceneParser::ParticleSceneParser(cpp::Renderer renderer) : + renderer(renderer) + { + } -bool ParticleSceneParser::parse(int ac, const char **&av) -{ - bool loadedScene = false; - std::vector particleModel; - std::vector deferredLoadingListXYZ; - FileName defFileName = ""; - int timeStep = 0; - - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--radius") { - ospray::particle::Model::defaultRadius = atof(av[++i]); - } else if (arg == "--atom-defs") { - defFileName = av[++i]; - } else if (arg == "--particle-timestep") { - timeStep = atoi(av[++i]); - } else { - FileName fn = arg; - if (fn.str() == "___CUBE_TEST___") { - int numPerSide = atoi(av[++i]); - particle::Model *m = createTestCube(numPerSide); - particleModel.push_back(m); - loadedScene = true; - } else if (fn.ext() == "xyz") { - particle::Model *m = new particle::Model; - deferredLoadingListXYZ.push_back(new DeferredLoadJob(m,fn,defFileName)); - particleModel.push_back(m); - loadedScene = true; - } else if (fn.ext() == "xyz2") { - particle::Model *m = new particle::Model; - m->loadXYZ2(fn); - particleModel.push_back(m); - loadedScene = true; + bool ParticleSceneParser::parse(int ac, const char **&av) + { + bool loadedScene = false; + std::vector particleModel; + std::vector deferredLoadingListXYZ; + FileName defFileName = ""; + int timeStep = 0; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--radius") { + ospray::particle::Model::defaultRadius = atof(av[++i]); + } else if (arg == "--atom-defs") { + defFileName = av[++i]; + } else if (arg == "--particle-timestep") { + timeStep = atoi(av[++i]); + } else { + FileName fn = arg; + if (fn.str() == "___CUBE_TEST___") { + int numPerSide = atoi(av[++i]); + particle::Model *m = createTestCube(numPerSide); + particleModel.push_back(m); + loadedScene = true; + } else if (fn.ext() == "xyz") { + particle::Model *m = new particle::Model; + deferredLoadingListXYZ.push_back(new DeferredLoadJob(m,fn,defFileName)); + particleModel.push_back(m); + loadedScene = true; + } else if (fn.ext() == "xyz2") { + particle::Model *m = new particle::Model; + m->loadXYZ2(fn); + particleModel.push_back(m); + loadedScene = true; + } else if (fn.ext() == "xyz3") { + particle::Model *m = new particle::Model; + m->loadXYZ3(fn); + particleModel.push_back(m); + loadedScene = true; #if 1 // NOTE(jda) - The '.xml' file extension conflicts with RIVL files in // TriangleMeshSceneParser...disabling here for now until the // the problem requires a solution. - } + } #else } else if (fn.ext() == "xml") { particle::Model *m = particle::parse__Uintah_timestep_xml(fn); @@ -184,3 +191,5 @@ std::deque ParticleSceneParser::bbox() const boxes.push_back(sceneBbox); return boxes; } + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h index 044a3152d4..1b964b3e20 100644 --- a/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h +++ b/apps/common/commandline/SceneParser/particle/ParticleSceneParser.h @@ -20,20 +20,24 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE ParticleSceneParser : public SceneParser -{ -public: - ParticleSceneParser(ospray::cpp::Renderer); +namespace commandline { - bool parse(int ac, const char **&av) override; + class OSPRAY_COMMANDLINE_INTERFACE ParticleSceneParser : public SceneParser + { + public: + ParticleSceneParser(ospray::cpp::Renderer); - std::deque model() const override; - std::deque bbox() const override; + bool parse(int ac, const char **&av) override; -private: + std::deque model() const override; + std::deque bbox() const override; - ospray::cpp::Renderer renderer; - ospcommon::box3f sceneBbox; + private: - std::deque modelTimeStep; -}; + ospray::cpp::Renderer renderer; + ospcommon::box3f sceneBbox; + + std::deque modelTimeStep; + }; + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp index eb5645e506..28b09e5f0a 100644 --- a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp +++ b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.cpp @@ -18,578 +18,584 @@ #include "common/xml/XML.h" -using namespace ospray; -using namespace ospcommon; - #include -using std::cout; -using std::endl; -// Helper types /////////////////////////////////////////////////////////////// +namespace commandline { -struct Triangles { - std::vector vertex; - std::vector color; // vertex color, from sv's 'v' value - std::vector index; + using namespace ospray; + using namespace ospcommon; - struct SVVertex { - float v; - vec3f pos; - }; - struct SVTriangle { - SVVertex vertex[3]; - }; + using std::cout; + using std::endl; - vec3f lerpf(float x, float x0, float x1, vec3f y0, vec3f y1) - { - float f = (x-x0)/(x1-x0); - return f*y1+(1-f)*y0; - } - - vec4f colorOf(const float f) - { - if (f < .5f) - return vec4f(lerpf(f, 0.f,.5f,vec3f(0),vec3f(0,1,0)), 1.f); - else - return vec4f(lerpf(f, .5f,1.f,vec3f(0,1,0),vec3f(1,0,0)), 1.f); - } - void parseSV(const FileName &fn) - { - FILE *file = fopen(fn.str().c_str(),"rb"); - if (!file) return; - SVTriangle triangle; - while (fread(&triangle,sizeof(triangle),1,file)) { - index.push_back(vec3i(0,1,2)+vec3i(vertex.size())); - vertex.push_back(vec3fa(triangle.vertex[0].pos)); - vertex.push_back(vec3fa(triangle.vertex[1].pos)); - vertex.push_back(vec3fa(triangle.vertex[2].pos)); - color.push_back(colorOf(triangle.vertex[0].v)); - color.push_back(colorOf(triangle.vertex[1].v)); - color.push_back(colorOf(triangle.vertex[2].v)); - } - fclose(file); - } + // Helper types /////////////////////////////////////////////////////////////// - box3f getBounds() const - { - box3f bounds = empty; - for (const auto &v : vertex) - bounds.extend(v); - return bounds; - } -}; + struct Triangles { + std::vector vertex; + std::vector color; // vertex color, from sv's 'v' value + std::vector index; -struct StreamLines { - std::vector vertex; - std::vector index; - float radius {0.001f}; + struct SVVertex { + float v; + vec3f pos; + }; - void parsePNT(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"r"); - if (!file) { - cout << "WARNING: could not open file " << fn << endl; - return; + struct SVTriangle { + SVVertex vertex[3]; + }; + + vec3f lerpf(float x, float x0, float x1, vec3f y0, vec3f y1) + { + float f = (x-x0)/(x1-x0); + return f*y1+(1-f)*y0; } - vec3fa pnt; - static size_t totalSegments = 0; - size_t segments = 0; - // cout << "parsing file " << fn << ":" << std::flush; - int rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z); - vertex.push_back(pnt); - Assert(rc == 3); - while ((rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z)) == 3) { - index.push_back(vertex.size()-1); - vertex.push_back(pnt); - segments++; + + vec4f colorOf(const float f) + { + if (f < .5f) + return vec4f(lerpf(f, 0.f,.5f,vec3f(0),vec3f(0,1,0)), 1.f); + else + return vec4f(lerpf(f, .5f,1.f,vec3f(0,1,0),vec3f(1,0,0)), 1.f); + } + void parseSV(const FileName &fn) + { + FILE *file = fopen(fn.str().c_str(),"rb"); + if (!file) return; + SVTriangle triangle; + while (fread(&triangle,sizeof(triangle),1,file)) { + index.push_back(vec3i(0,1,2)+vec3i(vertex.size())); + vertex.push_back(vec3fa(triangle.vertex[0].pos)); + vertex.push_back(vec3fa(triangle.vertex[1].pos)); + vertex.push_back(vec3fa(triangle.vertex[2].pos)); + color.push_back(colorOf(triangle.vertex[0].v)); + color.push_back(colorOf(triangle.vertex[1].v)); + color.push_back(colorOf(triangle.vertex[2].v)); + } + fclose(file); } - totalSegments += segments; - fclose(file); - } + box3f getBounds() const + { + box3f bounds = empty; + for (const auto &v : vertex) + bounds.extend(v); + return bounds; + } + }; - void parsePNTlist(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - char *eol = strstr(line,"\n"); if (eol) *eol = 0; - parsePNT(line); + struct StreamLines { + std::vector vertex; + std::vector index; + float radius {0.001f}; + + void parsePNT(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"r"); + if (!file) { + cout << "WARNING: could not open file " << fn << endl; + return; + } + vec3fa pnt; + static size_t totalSegments = 0; + size_t segments = 0; + // cout << "parsing file " << fn << ":" << std::flush; + int rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z); + vertex.push_back(pnt); + Assert(rc == 3); + while ((rc = fscanf(file,"%f %f %f\n",&pnt.x,&pnt.y,&pnt.z)) == 3) { + index.push_back(vertex.size()-1); + vertex.push_back(pnt); + segments++; + } + totalSegments += segments; + fclose(file); } - fclose(file); - } - void parseSLRAW(const FileName &fn) - { - FILE *file = fopen(fn.c_str(),"rb"); - if (!file) { - cout << "WARNING: could not open file " << fn << endl; - return; + void parsePNTlist(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + char *eol = strstr(line,"\n"); if (eol) *eol = 0; + parsePNT(line); + } + fclose(file); } - int numStreamlines; - int rc = fread(&numStreamlines, sizeof(int), 1, file); - (void)rc; - Assert(rc == 1); + void parseSLRAW(const FileName &fn) + { + FILE *file = fopen(fn.c_str(),"rb"); - for(int s=0; s filePoints; - - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - radius = 99.f; - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - if (line[0] == '#') continue; - - vec3fa v; float f1; int ID, i, j; - sscanf(line,"%i %i %f %f %f %f %i\n",&ID,&i,&v.x,&v.y,&v.z,&f1,&j); - if (f1 < radius) - radius = f1; - filePoints.push_back(v); - - index.push_back(vertex.size()); - vertex.push_back(v); - vertex.push_back(filePoints[j-1]); - } - fclose(file); - } + void parseSWC(const FileName &fn) + { + std::vector filePoints; - void parse(const FileName &fn) - { - if (fn.ext() == "pnt") - parsePNT(fn); - else if (fn.ext() == "pntlist") { FILE *file = fopen(fn.c_str(),"r"); Assert(file); + radius = 99.f; for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - char *eol = strstr(line,"\n"); if (eol) *eol = 0; - parsePNT(line); + if (line[0] == '#') continue; + + vec3fa v; float f1; int ID, i, j; + sscanf(line,"%i %i %f %f %f %f %i\n",&ID,&i,&v.x,&v.y,&v.z,&f1,&j); + if (f1 < radius) + radius = f1; + filePoints.push_back(v); + + index.push_back(vertex.size()); + vertex.push_back(v); + vertex.push_back(filePoints[j-1]); } fclose(file); - } else if (fn.ext() == "slraw") { - parseSLRAW(fn); - } else - throw std::runtime_error("unknown input file format "+fn.str()); - } - box3f getBounds() const - { - box3f bounds = empty; - for (const auto &v : vertex) - bounds.extend(v); - return bounds; - } -}; + } -struct StockleyWhealCannon { - struct Cylinder { - vec3f v0; - vec3f v1; - float r; - }; - struct Sphere { - vec3f v; - float r; + void parse(const FileName &fn) + { + if (fn.ext() == "pnt") + parsePNT(fn); + else if (fn.ext() == "pntlist") { + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + char *eol = strstr(line,"\n"); if (eol) *eol = 0; + parsePNT(line); + } + fclose(file); + } else if (fn.ext() == "slraw") { + parseSLRAW(fn); + } else + throw std::runtime_error("unknown input file format "+fn.str()); + } + box3f getBounds() const + { + box3f bounds = empty; + for (const auto &v : vertex) + bounds.extend(v); + return bounds; + } }; - std::vector spheres[3]; - std::vector cylinders[3]; - box3f bounds; + struct StockleyWhealCannon { + struct Cylinder { + vec3f v0; + vec3f v1; + float r; + }; + struct Sphere { + vec3f v; + float r; + }; + + std::vector spheres[3]; + std::vector cylinders[3]; + box3f bounds; + + void parse(const FileName &fn) + { + std::vector filePoints; - void parse(const FileName &fn) - { - std::vector filePoints; - - FILE *file = fopen(fn.c_str(),"r"); - Assert(file); - bounds = empty; - for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { - if (line[0] == '#') continue; - - vec3f v; float radius; int id, parent, type; - sscanf(line,"%i %i %f %f %f %f %i\n", - &id, &type, &v.x, &v.y, &v.z, &radius, &parent); - filePoints.push_back(v); - Assert(filePoints.size() == id); // assumes index-1==id - bounds.extend(v); // neglects radius - - if (parent == -1) // root soma, just one sphere - spheres[0].push_back(Sphere{v, radius}); - else { // cylinder with sphere at end - int idx; - switch (type) { - case 3: idx = 1; break; // basal dendrite - case 4: idx = 2; break; // apical dendrite - default: idx = 0; break; // soma / axon + FILE *file = fopen(fn.c_str(),"r"); + Assert(file); + bounds = empty; + for (char line[10000]; fgets(line,10000,file) && !feof(file); ) { + if (line[0] == '#') continue; + + vec3f v; float radius; int id, parent, type; + sscanf(line,"%i %i %f %f %f %f %i\n", + &id, &type, &v.x, &v.y, &v.z, &radius, &parent); + filePoints.push_back(v); + Assert(filePoints.size() == id); // assumes index-1==id + bounds.extend(v); // neglects radius + + if (parent == -1) // root soma, just one sphere + spheres[0].push_back(Sphere{v, radius}); + else { // cylinder with sphere at end + int idx; + switch (type) { + case 3: idx = 1; break; // basal dendrite + case 4: idx = 2; break; // apical dendrite + default: idx = 0; break; // soma / axon + } + spheres[idx].push_back(Sphere{v, radius}); + cylinders[idx].push_back(Cylinder{filePoints[parent-1], v, radius}); } - spheres[idx].push_back(Sphere{v, radius}); - cylinders[idx].push_back(Cylinder{filePoints[parent-1], v, radius}); } + fclose(file); } - fclose(file); - } - box3f getBounds() const + box3f getBounds() const + { + return bounds; + } + }; + + static const char *delim = "\n\t\r "; + + void osxParseInts(std::vector &vec, const std::string &content) { - return bounds; + char *s = strdup(content.c_str()); + char *tok = strtok(s,delim); + while (tok) { + vec.push_back(atol(tok)); + tok = strtok(NULL,delim); + } + free(s); } -}; -static const char *delim = "\n\t\r "; + template T ato(const char *); + template<> inline int ato(const char *s) { return atol(s); } + template<> inline float ato(const char *s) { return atof(s); } + + template + vec_t osxParseVec3(char * &tok) { + vec_t v; + + assert(tok); + v.x = ato(tok); + tok = strtok(NULL,delim); + + assert(tok); + v.y = ato(tok); + tok = strtok(NULL,delim); -void osxParseInts(std::vector &vec, const std::string &content) -{ - char *s = strdup(content.c_str()); - char *tok = strtok(s,delim); - while (tok) { - vec.push_back(atol(tok)); + assert(tok); + v.z = ato(tok); tok = strtok(NULL,delim); + + return v; } - free(s); -} - -template T ato(const char *); -template<> inline int ato(const char *s) { return atol(s); } -template<> inline float ato(const char *s) { return atof(s); } - -template -vec_t osxParseVec3(char * &tok) { - vec_t v; - - assert(tok); - v.x = ato(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.y = ato(tok); - tok = strtok(NULL,delim); - - assert(tok); - v.z = ato(tok); - tok = strtok(NULL,delim); - - return v; -} - -void osxParseVec3is(std::vector &vec, const std::string &content) -{ - char *s = strdup(content.c_str()); - char *tok = strtok(s,delim); - while (tok) - vec.push_back(osxParseVec3(tok)); - free(s); -} - -void osxParseVec3fas(std::vector &vec, const std::string &content) -{ - char *s = strdup(content.c_str()); - char *tok = strtok(s,delim); - while (tok) - vec.push_back(osxParseVec3(tok)); - free(s); -} - -void osxParseColors(std::vector &vec, const std::string &content) -{ - char *s = strdup(content.c_str()); - char *tok = strtok(s,delim); - while (tok) - vec.push_back(vec4f(osxParseVec3(tok), 1.f)); - free(s); -} - -/*! parse ospray xml file */ -void parseOSX(StreamLines *streamLines, - Triangles *triangles, - const std::string &fn) -{ - std::shared_ptr doc = xml::readXML(fn); - assert(doc); - if (doc->child.size() != 1 || doc->child[0]->name != "OSPRay") - throw std::runtime_error("could not parse osx file: Not in OSPRay format!?"); - const xml::Node &root_element = *doc->child[0]; - xml::for_each_child_of(root_element,[&](const xml::Node &node){ - if (node.name == "Info") { - // ignore - } - else if (node.name == "Model") { - const xml::Node &model_node = node; - xml::for_each_child_of(model_node,[&](const xml::Node &node){ - if (node.name == "StreamLines") { - const xml::Node &sl_node = node; - xml::for_each_child_of(sl_node,[&](const xml::Node &node){ - if (node.name == "vertex") { - osxParseVec3fas(streamLines->vertex,node.content); - } - else if (node.name == "index") { - osxParseInts(streamLines->index,node.content); - }; - }); - } - else if (node.name == "TriangleMesh") { - const xml::Node &tris_node = node; - xml::for_each_child_of(tris_node,[&](const xml::Node &node){ - if (node.name == "vertex") { - osxParseVec3fas(triangles->vertex,node.content); - } - else if (node.name == "color") { - osxParseColors(triangles->color,node.content); - } - else if (node.name == "index") { - osxParseVec3is(triangles->index,node.content); - } - }); - } - }); - } - }); -} - -void exportOSX(const char *fn,StreamLines *streamLines, Triangles *triangles) -{ - FILE *file = fopen(fn,"w"); - fprintf(file,"\n\n"); - fprintf(file,"\n"); + + void osxParseVec3is(std::vector &vec, const std::string &content) + { + char *s = strdup(content.c_str()); + char *tok = strtok(s,delim); + while (tok) + vec.push_back(osxParseVec3(tok)); + free(s); + } + + void osxParseVec3fas(std::vector &vec, const std::string &content) + { + char *s = strdup(content.c_str()); + char *tok = strtok(s,delim); + while (tok) + vec.push_back(osxParseVec3(tok)); + free(s); + } + + void osxParseColors(std::vector &vec, const std::string &content) + { + char *s = strdup(content.c_str()); + char *tok = strtok(s,delim); + while (tok) + vec.push_back(vec4f(osxParseVec3(tok), 1.f)); + free(s); + } + + /*! parse ospray xml file */ + void parseOSX(StreamLines *streamLines, + Triangles *triangles, + const std::string &fn) { - fprintf(file,"\n"); + std::shared_ptr doc = xml::readXML(fn); + assert(doc); + if (doc->child.size() != 1 || doc->child[0]->name != "OSPRay") + throw std::runtime_error("could not parse osx file: Not in OSPRay format!?"); + const xml::Node &root_element = *doc->child[0]; + xml::for_each_child_of(root_element,[&](const xml::Node &node){ + if (node.name == "Info") { + // ignore + } + else if (node.name == "Model") { + const xml::Node &model_node = node; + xml::for_each_child_of(model_node,[&](const xml::Node &node){ + if (node.name == "StreamLines") { + const xml::Node &sl_node = node; + xml::for_each_child_of(sl_node,[&](const xml::Node &node){ + if (node.name == "vertex") { + osxParseVec3fas(streamLines->vertex,node.content); + } + else if (node.name == "index") { + osxParseInts(streamLines->index,node.content); + }; + }); + } + else if (node.name == "TriangleMesh") { + const xml::Node &tris_node = node; + xml::for_each_child_of(tris_node,[&](const xml::Node &node){ + if (node.name == "vertex") { + osxParseVec3fas(triangles->vertex,node.content); + } + else if (node.name == "color") { + osxParseColors(triangles->color,node.content); + } + else if (node.name == "index") { + osxParseVec3is(triangles->index,node.content); + } + }); + } + }); + } + }); + } + + void exportOSX(const char *fn,StreamLines *streamLines, Triangles *triangles) + { + FILE *file = fopen(fn,"w"); + fprintf(file,"\n\n"); + fprintf(file,"\n"); { - fprintf(file,"\n"); + fprintf(file,"\n"); { - fprintf(file,"\n"); - for (const auto &v : streamLines->vertex) - fprintf(file,"%f %f %f\n", v.x, v.y, v.z); - fprintf(file,"\n"); - - fprintf(file,"\n"); - for (const auto & i : streamLines->index) - fprintf(file,"%i ",i); - fprintf(file,"\n\n"); - } - fprintf(file,"\n"); + fprintf(file,"\n"); + { + fprintf(file,"\n"); + for (const auto &v : streamLines->vertex) + fprintf(file,"%f %f %f\n", v.x, v.y, v.z); + fprintf(file,"\n"); + + fprintf(file,"\n"); + for (const auto & i : streamLines->index) + fprintf(file,"%i ",i); + fprintf(file,"\n\n"); + } + fprintf(file,"\n"); - fprintf(file,"\n"); - { - fprintf(file,"\n"); - for (const auto &v : triangles->vertex) - fprintf(file,"%f %f %f\n", v.x, v.y, v.z); - fprintf(file,"\n"); + fprintf(file,"\n"); + { + fprintf(file,"\n"); + for (const auto &v : triangles->vertex) + fprintf(file,"%f %f %f\n", v.x, v.y, v.z); + fprintf(file,"\n"); - fprintf(file,"\n"); - for (const auto &c : triangles->color) - fprintf(file,"%f %f %f\n", c.x, c.y, c.z); - fprintf(file,"\n"); + fprintf(file,"\n"); + for (const auto &c : triangles->color) + fprintf(file,"%f %f %f\n", c.x, c.y, c.z); + fprintf(file,"\n"); - fprintf(file,"\n"); - for (const auto &i : triangles->index) - fprintf(file,"%i %i %i\n", i.x, i.y, i.z); - fprintf(file,"\n"); + fprintf(file,"\n"); + for (const auto &i : triangles->index) + fprintf(file,"%i %i %i\n", i.x, i.y, i.z); + fprintf(file,"\n"); + } + fprintf(file,"\n"); } - fprintf(file,"\n"); - } - fprintf(file,"\n"); - } - fprintf(file,"\n"); - fclose(file); -} - -// Class definitions ////////////////////////////////////////////////////////// - -StreamLineSceneParser::StreamLineSceneParser(cpp::Renderer renderer) : - renderer(renderer) -{ -} - -bool StreamLineSceneParser::parse(int ac, const char **&av) -{ - bool loadedScene = false; - - StreamLines *streamLines = nullptr;//new StreamLines; - Triangles *triangles = nullptr;//new Triangles; - StockleyWhealCannon *swc = nullptr;//new StockleyWhealCannon; - - for (int i = 1; i < ac; i++) { - std::string arg = av[i]; - if (arg[0] != '-') { - const FileName fn = arg; - if (fn.ext() == "osx") { - streamLines = new StreamLines; - triangles = new Triangles; - parseOSX(streamLines, triangles, fn); - loadedScene = true; - } - else if (fn.ext() == "pnt") { - streamLines = new StreamLines; - streamLines->parsePNT(fn); - loadedScene = true; - } - else if (fn.ext() == "swc") { - swc = new StockleyWhealCannon; - swc->parse(fn); - loadedScene = true; - } - else if (fn.ext() == "pntlist") { - streamLines = new StreamLines; - streamLines->parsePNTlist(fn); - loadedScene = true; - } - else if (fn.ext() == "slraw") { - streamLines = new StreamLines; - streamLines->parseSLRAW(fn); - loadedScene = true; - } - else if (fn.ext() == "sv") { - triangles = new Triangles; - triangles->parseSV(fn); - loadedScene = true; - } - } else if (arg == "--streamline-radius") { - streamLines->radius = atof(av[++i]); -#if 0 - } else if (arg == "--streamline-export") { - exportOSX(av[++i], streamLines, triangles); -#endif + fprintf(file,"\n"); } + fprintf(file,"\n"); + fclose(file); } - if (loadedScene) { - sceneModel = make_unique(); + // Class definitions ////////////////////////////////////////////////////////// - auto &model = *sceneModel; + StreamLineSceneParser::StreamLineSceneParser(cpp::Renderer renderer) : + renderer(renderer) + { + } - OSPMaterial mat = ospNewMaterial(renderer.handle(), "default"); - if (mat) { - ospSet3f(mat, "kd", .7, .7, .7); - ospCommit(mat); + bool StreamLineSceneParser::parse(int ac, const char **&av) + { + bool loadedScene = false; + + StreamLines *streamLines = nullptr;//new StreamLines; + Triangles *triangles = nullptr;//new Triangles; + StockleyWhealCannon *swc = nullptr;//new StockleyWhealCannon; + + for (int i = 1; i < ac; i++) { + std::string arg = av[i]; + if (arg[0] != '-') { + const FileName fn = arg; + if (fn.ext() == "osx") { + streamLines = new StreamLines; + triangles = new Triangles; + parseOSX(streamLines, triangles, fn); + loadedScene = true; + } + else if (fn.ext() == "pnt") { + streamLines = new StreamLines; + streamLines->parsePNT(fn); + loadedScene = true; + } + else if (fn.ext() == "swc") { + swc = new StockleyWhealCannon; + swc->parse(fn); + loadedScene = true; + } + else if (fn.ext() == "pntlist") { + streamLines = new StreamLines; + streamLines->parsePNTlist(fn); + loadedScene = true; + } + else if (fn.ext() == "slraw") { + streamLines = new StreamLines; + streamLines->parseSLRAW(fn); + loadedScene = true; + } + else if (fn.ext() == "sv") { + triangles = new Triangles; + triangles->parseSV(fn); + loadedScene = true; + } + } else if (arg == "--streamline-radius") { + streamLines->radius = atof(av[++i]); +#if 0 + } else if (arg == "--streamline-export") { + exportOSX(av[++i], streamLines, triangles); +#endif + } } - box3f bounds(empty); - - if (streamLines && !streamLines->index.empty()) { - OSPGeometry geom = ospNewGeometry("streamlines"); - Assert(geom); - OSPData vertex = ospNewData(streamLines->vertex.size(), - OSP_FLOAT3A, &streamLines->vertex[0]); - OSPData index = ospNewData(streamLines->index.size(), - OSP_UINT, &streamLines->index[0]); - ospSetObject(geom,"vertex",vertex); - ospSetObject(geom,"index",index); - ospSet1f(geom,"radius",streamLines->radius); - if (mat) - ospSetMaterial(geom,mat); - ospCommit(geom); - ospAddGeometry(model.handle(), geom); - bounds.extend(streamLines->getBounds()); - } + if (loadedScene) { + sceneModel = make_unique(); - if (triangles && !triangles->index.empty()) { - OSPGeometry geom = ospNewGeometry("triangles"); - Assert(geom); - OSPData vertex = ospNewData(triangles->vertex.size(), - OSP_FLOAT3A, &triangles->vertex[0]); - OSPData index = ospNewData(triangles->index.size(), - OSP_INT3, &triangles->index[0]); - OSPData color = ospNewData(triangles->color.size(), - OSP_FLOAT4, &triangles->color[0]); - ospSetObject(geom, "vertex", vertex); - ospSetObject(geom, "index", index); - ospSetObject(geom, "vertex.color", color); - ospSetMaterial(geom, mat); - ospCommit(geom); - ospAddGeometry(model.handle(), geom); - bounds.extend(triangles->getBounds()); - } + auto &model = *sceneModel; - if (swc && !swc->bounds.empty()) { - OSPMaterial material[3]; - material[0] = mat; - material[1] = ospNewMaterial(renderer.handle(), "default"); - if (material[1]) { - ospSet3f(material[1], "kd", .0, .7, .0); // OBJ renderer, green - ospCommit(material[1]); + OSPMaterial mat = ospNewMaterial(renderer.handle(), "default"); + if (mat) { + ospSet3f(mat, "kd", .7, .7, .7); + ospCommit(mat); } - material[2] = ospNewMaterial(renderer.handle(), "default"); - if (material[2]) { - ospSet3f(material[2], "kd", .7, .0, .7); // OBJ renderer, magenta - ospCommit(material[2]); + + box3f bounds(empty); + + if (streamLines && !streamLines->index.empty()) { + OSPGeometry geom = ospNewGeometry("streamlines"); + Assert(geom); + OSPData vertex = ospNewData(streamLines->vertex.size(), + OSP_FLOAT3A, &streamLines->vertex[0]); + OSPData index = ospNewData(streamLines->index.size(), + OSP_UINT, &streamLines->index[0]); + ospSetObject(geom,"vertex",vertex); + ospSetObject(geom,"index",index); + ospSet1f(geom,"radius",streamLines->radius); + if (mat) + ospSetMaterial(geom,mat); + ospCommit(geom); + ospAddGeometry(model.handle(), geom); + bounds.extend(streamLines->getBounds()); + } + + if (triangles && !triangles->index.empty()) { + OSPGeometry geom = ospNewGeometry("triangles"); + Assert(geom); + OSPData vertex = ospNewData(triangles->vertex.size(), + OSP_FLOAT3A, &triangles->vertex[0]); + OSPData index = ospNewData(triangles->index.size(), + OSP_INT3, &triangles->index[0]); + OSPData color = ospNewData(triangles->color.size(), + OSP_FLOAT4, &triangles->color[0]); + ospSetObject(geom, "vertex", vertex); + ospSetObject(geom, "index", index); + ospSetObject(geom, "vertex.color", color); + ospSetMaterial(geom, mat); + ospCommit(geom); + ospAddGeometry(model.handle(), geom); + bounds.extend(triangles->getBounds()); } - OSPGeometry spheres[3], cylinders[3]; - for (int i = 0; i < 3; i++) { - spheres[i] = ospNewGeometry("spheres"); - Assert(spheres[i]); + if (swc && !swc->bounds.empty()) { + OSPMaterial material[3]; + material[0] = mat; + material[1] = ospNewMaterial(renderer.handle(), "default"); + if (material[1]) { + ospSet3f(material[1], "kd", .0, .7, .0); // OBJ renderer, green + ospCommit(material[1]); + } + material[2] = ospNewMaterial(renderer.handle(), "default"); + if (material[2]) { + ospSet3f(material[2], "kd", .7, .0, .7); // OBJ renderer, magenta + ospCommit(material[2]); + } + + OSPGeometry spheres[3], cylinders[3]; + for (int i = 0; i < 3; i++) { + spheres[i] = ospNewGeometry("spheres"); + Assert(spheres[i]); + + OSPData data = ospNewData(swc->spheres[i].size(), OSP_FLOAT4, + &swc->spheres[i][0]); + ospSetObject(spheres[i], "spheres", data); + ospSet1i(spheres[i], "offset_radius", 3*sizeof(float)); - OSPData data = ospNewData(swc->spheres[i].size(), OSP_FLOAT4, - &swc->spheres[i][0]); - ospSetObject(spheres[i], "spheres", data); - ospSet1i(spheres[i], "offset_radius", 3*sizeof(float)); + if (material[i]) + ospSetMaterial(spheres[i], material[i]); - if (material[i]) - ospSetMaterial(spheres[i], material[i]); + ospCommit(spheres[i]); + ospAddGeometry(model.handle(), spheres[i]); - ospCommit(spheres[i]); - ospAddGeometry(model.handle(), spheres[i]); + cylinders[i] = ospNewGeometry("cylinders"); + Assert(cylinders[i]); - cylinders[i] = ospNewGeometry("cylinders"); - Assert(cylinders[i]); + data = ospNewData(swc->cylinders[i].size()*7, OSP_FLOAT, + &swc->cylinders[i][0]); + ospSetObject(cylinders[i], "cylinders", data); - data = ospNewData(swc->cylinders[i].size()*7, OSP_FLOAT, - &swc->cylinders[i][0]); - ospSetObject(cylinders[i], "cylinders", data); + if (material[i]) + ospSetMaterial(cylinders[i], material[i]); - if (material[i]) - ospSetMaterial(cylinders[i], material[i]); + ospCommit(cylinders[i]); + ospAddGeometry(model.handle(), cylinders[i]); + } - ospCommit(cylinders[i]); - ospAddGeometry(model.handle(), cylinders[i]); + bounds.extend(swc->getBounds()); } - bounds.extend(swc->getBounds()); + model.commit(); + sceneBbox = bounds; } - model.commit(); - sceneBbox = bounds; + if (streamLines) delete streamLines; + if (triangles) delete triangles; + if (swc) delete swc; + + return loadedScene; + } + + std::deque StreamLineSceneParser::model() const + { + std::deque models; + models.push_back(sceneModel.get() == nullptr ? cpp::Model() : *sceneModel); + return models; + } + + std::deque StreamLineSceneParser::bbox() const + { + std::deque boxes; + boxes.push_back(sceneBbox); + return boxes; } - if (streamLines) delete streamLines; - if (triangles) delete triangles; - if (swc) delete swc; - - return loadedScene; -} - -std::deque StreamLineSceneParser::model() const -{ - std::deque models; - models.push_back(sceneModel.get() == nullptr ? cpp::Model() : *sceneModel); - return models; -} - -std::deque StreamLineSceneParser::bbox() const -{ - std::deque boxes; - boxes.push_back(sceneBbox); - return boxes; -} +} // ::commandline diff --git a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h index 7ebc7d23eb..b02afa5fc6 100644 --- a/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h +++ b/apps/common/commandline/SceneParser/streamlines/StreamLineSceneParser.h @@ -20,21 +20,25 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE StreamLineSceneParser : public SceneParser -{ -public: - StreamLineSceneParser(ospray::cpp::Renderer); +namespace commandline { - bool parse(int ac, const char **&av) override; + class OSPRAY_COMMANDLINE_INTERFACE StreamLineSceneParser : public SceneParser + { + public: + StreamLineSceneParser(ospray::cpp::Renderer); - std::deque model() const override; - std::deque bbox() const override; + bool parse(int ac, const char **&av) override; -private: + std::deque model() const override; + std::deque bbox() const override; - std::unique_ptr sceneModel; - ospray::cpp::Renderer renderer; - ospcommon::box3f sceneBbox; + private: - void finalize(); -}; + std::unique_ptr sceneModel; + ospray::cpp::Renderer renderer; + ospcommon::box3f sceneBbox; + + void finalize(); + }; + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h index 3fec53c6ab..478d230466 100644 --- a/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h +++ b/apps/common/commandline/SceneParser/tachyon/TachyonSceneParser.h @@ -17,7 +17,7 @@ #pragma once #include -#include +#include class TachyonSceneParser : public SceneParser { diff --git a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp index fbbda22b90..b610e5b459 100644 --- a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp +++ b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.cpp @@ -19,426 +19,433 @@ #include #include -using namespace ospray; -using namespace ospcommon; - #include #include -using std::cerr; -using std::endl; -// Static local helper functions ////////////////////////////////////////////// +namespace commandline { + + using namespace ospray; + using namespace ospcommon; + + using std::cerr; + using std::endl; -static void warnMaterial(const std::string &type) -{ - static std::map numOccurances; - if (numOccurances[type] == 0) + // Static local helper functions ////////////////////////////////////////////// + + static void warnMaterial(const std::string &type) { - cerr << "could not create material type '"<< type << - "'. Replacing with default material." << endl; - } - numOccurances[type]++; -} - -// SceneParser definitions //////////////////////////////////////////////////// - -TriangleMeshSceneParser::TriangleMeshSceneParser(cpp::Renderer renderer, - std::string geometryType) : - renderer(renderer), - geometryType(geometryType), - alpha(false), - shouldCreateDefaultMaterial(true), - maxObjectsToConsider((uint32_t)-1), - forceInstancing(false), - forceNoInstancing(false), - msgModel(new miniSG::Model) -{ -} - -bool TriangleMeshSceneParser::parse(int ac, const char **&av) -{ - bool total_loadedScene = false; - - for (int i = 1; i < ac; i++) { - bool loadedScene = false; - const std::string arg = av[i]; - if (arg == "--max-objects") { - maxObjectsToConsider = atoi(av[++i]); - } else if (arg == "--force-instancing") { - forceInstancing = true; - } else if (arg == "--force-no-instancing") { - forceNoInstancing = true; - } else if (arg == "--alpha") { - alpha = true; - } else if (arg == "--no-default-material") { - shouldCreateDefaultMaterial = false; - } else if (arg == "--trianglemesh-type") { - geometryType = av[++i]; - } else { - FileName fn = arg; - if (fn.ext() == "stl") { - miniSG::importSTL(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "msg") { - miniSG::importMSG(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "tri") { - miniSG::importTRI(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "xml") { - miniSG::importRIVL(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "obj") { - miniSG::importOBJ(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "hbp") { - miniSG::importHBP(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "x3d") { - miniSG::importX3D(*msgModel,fn); - loadedScene = true; - } else if (fn.ext() == "astl") { - miniSG::importSTL(msgAnimation,fn); - loadedScene = true; + static std::map numOccurances; + if (numOccurances[type] == 0) + { + cerr << "could not create material type '"<< type << + "'. Replacing with default material." << endl; } - } - if (loadedScene) - { - msgModels.push_back(msgModel); - sceneModels.push_back(cpp::Model()); - msgModel = new miniSG::Model; - total_loadedScene = true; - } + numOccurances[type]++; } - if (total_loadedScene) { - finalize(); + // SceneParser definitions //////////////////////////////////////////////////// + + TriangleMeshSceneParser::TriangleMeshSceneParser(cpp::Renderer renderer, + std::string geometryType) + : renderer(renderer), + geometryType(geometryType), + alpha(false), + shouldCreateDefaultMaterial(true), + maxObjectsToConsider((uint32_t)-1), + forceInstancing(false), + forceNoInstancing(false), + msgModel(new miniSG::Model) + { } - return total_loadedScene; -} + bool TriangleMeshSceneParser::parse(int ac, const char **&av) + { + bool total_loadedScene = false; + + for (int i = 1; i < ac; i++) { + bool loadedScene = false; + const std::string arg = av[i]; + if (arg == "--max-objects") { + maxObjectsToConsider = atoi(av[++i]); + } else if (arg == "--force-instancing") { + forceInstancing = true; + } else if (arg == "--force-no-instancing") { + forceNoInstancing = true; + } else if (arg == "--alpha") { + alpha = true; + } else if (arg == "--no-default-material") { + shouldCreateDefaultMaterial = false; + } else if (arg == "--trianglemesh-type") { + geometryType = av[++i]; + } else { + FileName fn = arg; + if (fn.ext() == "stl") { + miniSG::importSTL(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "msg") { + miniSG::importMSG(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "tri") { + miniSG::importTRI_xyz(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "xyzs") { + miniSG::importTRI_xyzs(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "xml") { + miniSG::importRIVL(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "obj") { + miniSG::importOBJ(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "hbp") { + miniSG::importHBP(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "x3d") { + miniSG::importX3D(*msgModel,fn); + loadedScene = true; + } else if (fn.ext() == "astl") { + miniSG::importSTL(msgAnimation,fn); + loadedScene = true; + } + } -std::deque TriangleMeshSceneParser::model() const -{ - return sceneModels; -} + if (loadedScene) { + msgModels.push_back(msgModel); + sceneModels.push_back(cpp::Model()); + msgModel = new miniSG::Model; + total_loadedScene = true; + } + } -std::deque TriangleMeshSceneParser::bbox() const -{ - std::deque bboxes; - for(auto model : msgModels) - bboxes.push_back(model.ptr->getBBox()); - return bboxes; -} + if (total_loadedScene) + finalize(); -cpp::Material -TriangleMeshSceneParser::createDefaultMaterial(cpp::Renderer ren) -{ - if(!shouldCreateDefaultMaterial) return nullptr; + return total_loadedScene; + } - static auto ospMat = cpp::Material(nullptr); + std::deque TriangleMeshSceneParser::model() const + { + return sceneModels; + } - if (ospMat.handle()) return ospMat; + std::deque TriangleMeshSceneParser::bbox() const + { + std::deque bboxes; + for(auto model : msgModels) + bboxes.push_back(model.ptr->getBBox()); + return bboxes; + } - ospMat = ren.newMaterial("OBJMaterial"); + cpp::Material + TriangleMeshSceneParser::createDefaultMaterial(cpp::Renderer ren) + { + if(!shouldCreateDefaultMaterial) return nullptr; - ospMat.set("Kd", .8f, 0.f, 0.f); - ospMat.commit(); - return ospMat; -} + static auto ospMat = cpp::Material(nullptr); -cpp::Material TriangleMeshSceneParser::createMaterial(cpp::Renderer ren, - miniSG::Material *mat) -{ - if (mat == nullptr) return createDefaultMaterial(ren); + if (ospMat.handle()) return ospMat; - static std::map alreadyCreatedMaterials; + ospMat = ren.newMaterial("OBJMaterial"); - if (alreadyCreatedMaterials.find(mat) != alreadyCreatedMaterials.end()) { - return alreadyCreatedMaterials[mat]; + ospMat.set("Kd", .8f, 0.f, 0.f); + ospMat.commit(); + return ospMat; } - const char *type = mat->getParam("type", "OBJMaterial"); - assert(type); + cpp::Material TriangleMeshSceneParser::createMaterial(cpp::Renderer ren, + miniSG::Material *mat) + { + if (mat == nullptr) return createDefaultMaterial(ren); - cpp::Material ospMat; - try { - ospMat = alreadyCreatedMaterials[mat] = ren.newMaterial(type); - } catch (const std::runtime_error &/*e*/) { - warnMaterial(type); - return createDefaultMaterial(ren); - } + static std::map alreadyCreatedMaterials; + + if (alreadyCreatedMaterials.find(mat) != alreadyCreatedMaterials.end()) { + return alreadyCreatedMaterials[mat]; + } + + const char *type = mat->getParam("type", "OBJMaterial"); + assert(type); + + cpp::Material ospMat; + try { + ospMat = alreadyCreatedMaterials[mat] = ren.newMaterial(type); + } catch (const std::runtime_error &/*e*/) { + warnMaterial(type); + return createDefaultMaterial(ren); + } - const bool isOBJMaterial = !strcmp(type, "OBJMaterial"); - bool workaround3DMax = false; - float Tf_prime = 0.f; - - // workaround strange behavior of 3DMax exporter (fully transparent scenes) - // detect d==Tf==1-Tr, correct to d'=1, Tf'=1-d - if (isOBJMaterial) { - auto itTr = mat->params.find("Tr"); - auto end = mat->params.end(); - if (itTr != end) { - auto itd = mat->params.find("d"); - auto itTf = mat->params.find("Tf"); - if (itTf != end && itd != end) { - const miniSG::Material::Param *pd = itd->second.ptr; - const miniSG::Material::Param *pTr = itTr->second.ptr; - const miniSG::Material::Param *pTf = itTf->second.ptr; - if (pd->type == miniSG::Material::Param::FLOAT && - pTf->type == miniSG::Material::Param::FLOAT_3 && - pTr->type == miniSG::Material::Param::FLOAT) { - float d(pd->f[0]); - vec3f Tf(pTf->f[0], pTf->f[1], pTf->f[2]); - float Tr(pTr->f[0]); - if (reduce_max(abs(Tf + Tr - 1.0f)) < 1e-6f && - reduce_max(abs(Tf - d)) < 1e-6f) { - workaround3DMax = true; - Tf_prime = 1.f - d; + const bool isOBJMaterial = !strcmp(type, "OBJMaterial"); + bool workaround3DMax = false; + float Tf_prime = 0.f; + + // workaround strange behavior of 3DMax exporter (fully transparent scenes) + // detect d==Tf==1-Tr, correct to d'=1, Tf'=1-d + if (isOBJMaterial) { + auto itTr = mat->params.find("Tr"); + auto end = mat->params.end(); + if (itTr != end) { + auto itd = mat->params.find("d"); + auto itTf = mat->params.find("Tf"); + if (itTf != end && itd != end) { + const miniSG::Material::Param *pd = itd->second.ptr; + const miniSG::Material::Param *pTr = itTr->second.ptr; + const miniSG::Material::Param *pTf = itTf->second.ptr; + if (pd->type == miniSG::Material::Param::FLOAT && + pTf->type == miniSG::Material::Param::FLOAT_3 && + pTr->type == miniSG::Material::Param::FLOAT) { + float d(pd->f[0]); + vec3f Tf(pTf->f[0], pTf->f[1], pTf->f[2]); + float Tr(pTr->f[0]); + if (reduce_max(abs(Tf + Tr - 1.0f)) < 1e-6f && + reduce_max(abs(Tf - d)) < 1e-6f) { + workaround3DMax = true; + Tf_prime = 1.f - d; + } } } } } - } - for (auto it = mat->params.begin(); it != mat->params.end(); ++it) { - const char *name = it->first.c_str(); - const miniSG::Material::Param *p = it->second.ptr; - - switch(p->type) { - case miniSG::Material::Param::INT: - ospMat.set(name, p->i[0]); - break; - case miniSG::Material::Param::FLOAT: { - float f = p->f[0]; - /* many mtl materials of obj models wrongly store the phong exponent - 'Ns' in range [0..1], whereas OSPRay's material implementations - correctly interpret it to be in [0..inf), thus we map ranges here */ - if (isOBJMaterial && - (!strcmp(name, "Ns") || !strcmp(name, "ns")) && - f < 1.f) { - f = 1.f/(1.f - f) - 1.f; - } - if (workaround3DMax && !strcmp(name, "d")) - f = 1.0f; - ospMat.set(name, f); - } break; - case miniSG::Material::Param::FLOAT_3: - if (workaround3DMax && !strcmp(name, "Tf")) - ospMat.set(name, Tf_prime, Tf_prime, Tf_prime); - else - ospMat.set(name, p->f[0], p->f[1], p->f[2]); - break; - case miniSG::Material::Param::STRING: - ospMat.set(name, p->s); - break; - case miniSG::Material::Param::TEXTURE: - { - miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; - if (tex) { - OSPTexture2D ospTex = miniSG::createTexture2D(tex); - assert(ospTex); - ospCommit(ospTex); - ospMat.set(name, ospTex); - } - break; + for (auto it = mat->params.begin(); it != mat->params.end(); ++it) { + const char *name = it->first.c_str(); + const miniSG::Material::Param *p = it->second.ptr; + + switch(p->type) { + case miniSG::Material::Param::INT: + ospMat.set(name, p->i[0]); + break; + case miniSG::Material::Param::FLOAT: { + float f = p->f[0]; + /* many mtl materials of obj models wrongly store the phong exponent + 'Ns' in range [0..1], whereas OSPRay's material implementations + correctly interpret it to be in [0..inf), thus we map ranges here */ + if (isOBJMaterial && + (!strcmp(name, "Ns") || !strcmp(name, "ns")) && + f < 1.f) { + f = 1.f/(1.f - f) - 1.f; + } + if (workaround3DMax && !strcmp(name, "d")) + f = 1.0f; + ospMat.set(name, f); + } break; + case miniSG::Material::Param::FLOAT_3: + if (workaround3DMax && !strcmp(name, "Tf")) + ospMat.set(name, Tf_prime, Tf_prime, Tf_prime); + else + ospMat.set(name, p->f[0], p->f[1], p->f[2]); + break; + case miniSG::Material::Param::STRING: + ospMat.set(name, p->s); + break; + case miniSG::Material::Param::TEXTURE: + { + miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; + if (tex) { + OSPTexture2D ospTex = miniSG::createTexture2D(tex); + assert(ospTex); + ospCommit(ospTex); + ospMat.set(name, ospTex); + } + break; + } + default: + throw std::runtime_error("unknown material parameter type"); + }; } - default: - throw std::runtime_error("unknown material parameter type"); - }; - } - ospMat.commit(); - return ospMat; -} - -cpp::Geometry -TriangleMeshSceneParser::createOSPRayGeometry(miniSG::Model *msgModel, - miniSG::Mesh *msgMesh) -{ - // create ospray mesh - auto ospMesh = alpha ? cpp::Geometry("alpha_aware_triangle_mesh") : - cpp::Geometry(geometryType); - - // add position array to mesh - OSPData position = ospNewData(msgMesh->position.size(), - OSP_FLOAT3A, - &msgMesh->position[0]); - ospMesh.set("position", position); - - // add triangle index array to mesh - if (!msgMesh->triangleMaterialId.empty()) { - OSPData primMatID = ospNewData(msgMesh->triangleMaterialId.size(), - OSP_INT, - &msgMesh->triangleMaterialId[0]); - ospMesh.set("prim.materialID", primMatID); + ospMat.commit(); + return ospMat; } - // add triangle index array to mesh - OSPData index = ospNewData(msgMesh->triangle.size(), - OSP_INT3, - &msgMesh->triangle[0]); - assert(msgMesh->triangle.size() > 0); - ospMesh.set("index", index); - - // add normal array to mesh - if (!msgMesh->normal.empty()) { - OSPData normal = ospNewData(msgMesh->normal.size(), - OSP_FLOAT3A, - &msgMesh->normal[0]); - assert(msgMesh->normal.size() > 0); - ospMesh.set("vertex.normal", normal); - } + cpp::Geometry + TriangleMeshSceneParser::createOSPRayGeometry(miniSG::Model *msgModel, + miniSG::Mesh *msgMesh) + { + // create ospray mesh + auto ospMesh = alpha ? cpp::Geometry("alpha_aware_triangle_mesh") : + cpp::Geometry(geometryType); + + // add position array to mesh + OSPData position = ospNewData(msgMesh->position.size(), + OSP_FLOAT3A, + &msgMesh->position[0]); + ospMesh.set("position", position); + + // add triangle index array to mesh + if (!msgMesh->triangleMaterialId.empty()) { + OSPData primMatID = ospNewData(msgMesh->triangleMaterialId.size(), + OSP_INT, + &msgMesh->triangleMaterialId[0]); + ospMesh.set("prim.materialID", primMatID); + } - // add color array to mesh - if (!msgMesh->color.empty()) { - OSPData color = ospNewData(msgMesh->color.size(), - OSP_FLOAT4, - &msgMesh->color[0]); - assert(msgMesh->color.size() > 0); - ospMesh.set("vertex.color", color); - } + // add triangle index array to mesh + OSPData index = ospNewData(msgMesh->triangle.size(), + OSP_INT3, + &msgMesh->triangle[0]); + assert(msgMesh->triangle.size() > 0); + ospMesh.set("index", index); + + // add normal array to mesh + if (!msgMesh->normal.empty()) { + OSPData normal = ospNewData(msgMesh->normal.size(), + OSP_FLOAT3A, + &msgMesh->normal[0]); + assert(msgMesh->normal.size() > 0); + ospMesh.set("vertex.normal", normal); + } - // add texcoord array to mesh - if (!msgMesh->texcoord.empty()) { - OSPData texcoord = ospNewData(msgMesh->texcoord.size(), - OSP_FLOAT2, - &msgMesh->texcoord[0]); - assert(msgMesh->texcoord.size() > 0); - ospMesh.set("vertex.texcoord", texcoord); - } + // add color array to mesh + if (!msgMesh->color.empty()) { + OSPData color = ospNewData(msgMesh->color.size(), + OSP_FLOAT4, + &msgMesh->color[0]); + assert(msgMesh->color.size() > 0); + ospMesh.set("vertex.color", color); + } - ospMesh.set("alpha_type", 0); - ospMesh.set("alpha_component", 4); - - // add triangle material id array to mesh - if (msgMesh->materialList.empty()) { - // we have a single material for this mesh... - auto singleMaterial = createMaterial(renderer, msgMesh->material.ptr); - ospMesh.setMaterial(singleMaterial); - } else { - // we have an entire material list, assign that list - std::vector materialList; - std::vector alphaMaps; - std::vector alphas; - for (size_t i = 0; i < msgMesh->materialList.size(); i++) { - auto m = createMaterial(renderer, msgMesh->materialList[i].ptr); - auto handle = m.handle(); - materialList.push_back(handle); - - for (auto it = msgMesh->materialList[i]->params.begin(); - it != msgMesh->materialList[i]->params.end(); - it++) { - const char *name = it->first.c_str(); - const miniSG::Material::Param *p = it->second.ptr; - if(p->type == miniSG::Material::Param::TEXTURE) { - if(!strcmp(name, "map_kd") || !strcmp(name, "map_Kd")) { - miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; - OSPTexture2D ospTex = createTexture2D(tex); - ospCommit(ospTex); - alphaMaps.push_back(ospTex); + // add texcoord array to mesh + if (!msgMesh->texcoord.empty()) { + OSPData texcoord = ospNewData(msgMesh->texcoord.size(), + OSP_FLOAT2, + &msgMesh->texcoord[0]); + assert(msgMesh->texcoord.size() > 0); + ospMesh.set("vertex.texcoord", texcoord); + } + + ospMesh.set("alpha_type", 0); + ospMesh.set("alpha_component", 4); + + // add triangle material id array to mesh + if (msgMesh->materialList.empty()) { + // we have a single material for this mesh... + auto singleMaterial = createMaterial(renderer, msgMesh->material.ptr); + ospMesh.setMaterial(singleMaterial); + } else { + // we have an entire material list, assign that list + std::vector materialList; + std::vector alphaMaps; + std::vector alphas; + for (size_t i = 0; i < msgMesh->materialList.size(); i++) { + auto m = createMaterial(renderer, msgMesh->materialList[i].ptr); + auto handle = m.handle(); + materialList.push_back(handle); + + for (auto it = msgMesh->materialList[i]->params.begin(); + it != msgMesh->materialList[i]->params.end(); + it++) { + const char *name = it->first.c_str(); + const miniSG::Material::Param *p = it->second.ptr; + if(p->type == miniSG::Material::Param::TEXTURE) { + if(!strcmp(name, "map_kd") || !strcmp(name, "map_Kd")) { + miniSG::Texture2D *tex = (miniSG::Texture2D*)p->ptr; + OSPTexture2D ospTex = createTexture2D(tex); + ospCommit(ospTex); + alphaMaps.push_back(ospTex); + } + } else if(p->type == miniSG::Material::Param::FLOAT) { + if(!strcmp(name, "d")) alphas.push_back(p->f[0]); } - } else if(p->type == miniSG::Material::Param::FLOAT) { - if(!strcmp(name, "d")) alphas.push_back(p->f[0]); } - } - while(materialList.size() > alphaMaps.size()) { - alphaMaps.push_back(nullptr); - } - while(materialList.size() > alphas.size()) { - alphas.push_back(0.f); + while(materialList.size() > alphaMaps.size()) { + alphaMaps.push_back(nullptr); + } + while(materialList.size() > alphas.size()) { + alphas.push_back(0.f); + } } - } - auto ospMaterialList = cpp::Data(materialList.size(), - OSP_OBJECT, - &materialList[0]); - ospMesh.set("materialList", ospMaterialList); - - if(alpha) { - auto ospAlphaMapList = cpp::Data(alphaMaps.size(), + auto ospMaterialList = cpp::Data(materialList.size(), OSP_OBJECT, - &alphaMaps[0]); - ospMesh.set("alpha_maps", ospAlphaMapList); - - auto ospAlphaList = cpp::Data(alphas.size(), - OSP_OBJECT, - &alphas[0]); - ospMesh.set("alphas", ospAlphaList); + &materialList[0]); + ospMesh.set("materialList", ospMaterialList); + + if(alpha) { + auto ospAlphaMapList = cpp::Data(alphaMaps.size(), + OSP_OBJECT, + &alphaMaps[0]); + ospMesh.set("alpha_maps", ospAlphaMapList); + + auto ospAlphaList = cpp::Data(alphas.size(), + OSP_OBJECT, + &alphas[0]); + ospMesh.set("alphas", ospAlphaList); + } } - } - - ospMesh.commit(); - return ospMesh; -} + ospMesh.commit(); -void TriangleMeshSceneParser::finalize() -{ - if (forceInstancing && forceNoInstancing) { - throw std::runtime_error("You can't force BOTH instancing and" - " no-instancing!"); + return ospMesh; } - for (size_t modeli = 0; modeli < msgModels.size(); modeli++) + void TriangleMeshSceneParser::finalize() { - ospcommon::Ref msgModel = msgModels[modeli]; - ospray::cpp::Model* sceneModel = &sceneModels[modeli]; - // code does not yet do instancing ... check that the model doesn't - // contain instances - bool doesInstancing = forceInstancing || - (msgModel->instance.size() > msgModel->mesh.size() - && !forceNoInstancing); - - if (msgModel->instance.size() > maxObjectsToConsider) - msgModel->instance.resize(maxObjectsToConsider); - - if (!doesInstancing) { - for (size_t i = 0; i < msgModel->instance.size(); i++) { - auto &msgInstance = msgModel->instance[i]; - auto &msgMesh = msgModel->mesh[msgInstance.meshID]; - - // check if we have to transform the vertices: - if (msgInstance != miniSG::Instance(i)) { - auto positionCopy = msgMesh->position; - - for (size_t vID = 0; vID < msgMesh->position.size(); vID++) { - msgMesh->position[vID] = xfmPoint(msgModel->instance[i].xfm, - msgMesh->position[vID]); - } - - sceneModel->addGeometry(createOSPRayGeometry(msgModel.ptr, - msgMesh.ptr).handle()); + if (forceInstancing && forceNoInstancing) { + throw std::runtime_error("You can't force BOTH instancing and" + " no-instancing!"); + } - msgMesh->position = positionCopy; + for (size_t modeli = 0; modeli < msgModels.size(); modeli++) + { + ospcommon::Ref msgModel = msgModels[modeli]; + ospray::cpp::Model* sceneModel = &sceneModels[modeli]; + // code does not yet do instancing ... check that the model doesn't + // contain instances + bool doesInstancing = forceInstancing || + (msgModel->instance.size() > msgModel->mesh.size() + && !forceNoInstancing); + + if (msgModel->instance.size() > maxObjectsToConsider) + msgModel->instance.resize(maxObjectsToConsider); + + if (!doesInstancing) { + for (size_t i = 0; i < msgModel->instance.size(); i++) { + auto &msgInstance = msgModel->instance[i]; + auto &msgMesh = msgModel->mesh[msgInstance.meshID]; + + // check if we have to transform the vertices: + if (msgInstance != miniSG::Instance(i)) { + auto positionCopy = msgMesh->position; + + for (size_t vID = 0; vID < msgMesh->position.size(); vID++) { + msgMesh->position[vID] = xfmPoint(msgModel->instance[i].xfm, + msgMesh->position[vID]); + } + + sceneModel->addGeometry(createOSPRayGeometry(msgModel.ptr, + msgMesh.ptr).handle()); + + msgMesh->position = positionCopy; + } else { + sceneModel->addGeometry(createOSPRayGeometry(msgModel.ptr, + msgMesh.ptr).handle()); + } + } } else { - sceneModel->addGeometry(createOSPRayGeometry(msgModel.ptr, - msgMesh.ptr).handle()); - } - } - } else { - std::vector instanceModels; + std::vector instanceModels; - for (size_t i = 0; i < msgModel->mesh.size(); i++) { - Ref msgMesh = msgModel->mesh[i]; + for (size_t i = 0; i < msgModel->mesh.size(); i++) { + Ref msgMesh = msgModel->mesh[i]; - auto ospMesh = createOSPRayGeometry(msgModel.ptr, msgMesh.ptr); + auto ospMesh = createOSPRayGeometry(msgModel.ptr, msgMesh.ptr); - cpp::Model model_i; - model_i.addGeometry(ospMesh); - model_i.commit(); - instanceModels.push_back(model_i.handle()); - } + cpp::Model model_i; + model_i.addGeometry(ospMesh); + model_i.commit(); + instanceModels.push_back(model_i.handle()); + } - for (size_t i = 0; i < msgModel->instance.size(); i++) { - OSPGeometry inst = - ospNewInstance(instanceModels[msgModel->instance[i].meshID], - reinterpret_cast(msgModel->instance[i].xfm)); - sceneModel->addGeometry(inst); - } - } + for (size_t i = 0; i < msgModel->instance.size(); i++) { + OSPGeometry inst = + ospNewInstance(instanceModels[msgModel->instance[i].meshID], + reinterpret_cast(msgModel->instance[i].xfm)); + sceneModel->addGeometry(inst); + } + } - sceneModel->commit(); + sceneModel->commit(); + } } -} + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h index de30bd22e5..e5e199b840 100644 --- a/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h +++ b/apps/common/commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h @@ -21,43 +21,47 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE TriangleMeshSceneParser : public SceneParser -{ -public: - TriangleMeshSceneParser(ospray::cpp::Renderer renderer, - std::string geometryType = "triangles"); +namespace commandline { - bool parse(int ac, const char **&av) override; + class OSPRAY_COMMANDLINE_INTERFACE TriangleMeshSceneParser : public SceneParser + { + public: + TriangleMeshSceneParser(ospray::cpp::Renderer renderer, + std::string geometryType = "triangles"); - std::deque model() const override; - std::deque bbox() const override; - ospray::cpp::Geometry createOSPRayGeometry(ospray::miniSG::Model *msgModel, - ospray::miniSG::Mesh *msgMesh); + bool parse(int ac, const char **&av) override; -private: + std::deque model() const override; + std::deque bbox() const override; + ospray::cpp::Geometry createOSPRayGeometry(ospray::miniSG::Model *msgModel, + ospray::miniSG::Mesh *msgMesh); - ospray::cpp::Material createDefaultMaterial(ospray::cpp::Renderer renderer); - ospray::cpp::Material createMaterial(ospray::cpp::Renderer renderer, - ospray::miniSG::Material *mat); + private: - ospray::cpp::Renderer renderer; + ospray::cpp::Material createDefaultMaterial(ospray::cpp::Renderer renderer); + ospray::cpp::Material createMaterial(ospray::cpp::Renderer renderer, + ospray::miniSG::Material *mat); - std::deque sceneModels; + ospray::cpp::Renderer renderer; - std::string geometryType; + std::deque sceneModels; - bool alpha; - bool shouldCreateDefaultMaterial; - unsigned int maxObjectsToConsider; + std::string geometryType; - // if turned on, we'll put each triangle mesh into its own instance, - // no matter what - bool forceInstancing; - bool forceNoInstancing; + bool alpha; + bool shouldCreateDefaultMaterial; + unsigned int maxObjectsToConsider; - ospcommon::Ref msgModel; - std::deque > msgModels; - std::vector msgAnimation; + // if turned on, we'll put each triangle mesh into its own instance, + // no matter what + bool forceInstancing; + bool forceNoInstancing; - void finalize(); -}; + ospcommon::Ref msgModel; + std::deque > msgModels; + std::vector msgAnimation; + + void finalize(); + }; + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp index 1db1be8e4b..7f2facba1e 100644 --- a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp +++ b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.cpp @@ -18,250 +18,254 @@ #include -using namespace ospray; -using namespace ospcommon; - #include "common/importer/Importer.h" #include "common/tfn_lib/tfn_lib.h" #include -using std::cerr; -using std::endl; - #include -// SceneParser definitions //////////////////////////////////////////////////// - -VolumeSceneParser::VolumeSceneParser(cpp::Renderer renderer) : - renderer(renderer) -{ -} - -bool VolumeSceneParser::parse(int ac, const char **&av) -{ - bool loadedScene = false; - bool loadedTransferFunction = false; - - FileName scene; - - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "-s" || arg == "--sampling-rate") { - samplingRate = atof(av[++i]); - } else if (arg == "-tfc" || arg == "--tf-color") { - ospcommon::vec4f color; - color.x = atof(av[++i]); - color.y = atof(av[++i]); - color.z = atof(av[++i]); - color.w = atof(av[++i]); - tf_colors.push_back(color); - } else if (arg == "-tfs" || arg == "--tf-scale") { - tf_scale = atof(av[++i]); - } else if (arg == "-tff" || arg == "--tf-file") { - importTransferFunction(std::string(av[++i])); - loadedTransferFunction = true; - } else if (arg == "-is" || arg == "--surface") { - isosurfaces.push_back(atof(av[++i])); - } else { - FileName fn = arg; - if (fn.ext() == "osp") { - scene = arg; - loadedScene = true; +namespace commandline { + + using namespace ospray; + using namespace ospcommon; + + using std::cerr; + using std::endl; + + // SceneParser definitions //////////////////////////////////////////////////// + + VolumeSceneParser::VolumeSceneParser(cpp::Renderer renderer) : + renderer(renderer) + { + } + + bool VolumeSceneParser::parse(int ac, const char **&av) + { + bool loadedScene = false; + bool loadedTransferFunction = false; + + FileName scene; + + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "-s" || arg == "--sampling-rate") { + samplingRate = atof(av[++i]); + } else if (arg == "-tfc" || arg == "--tf-color") { + ospcommon::vec4f color; + color.x = atof(av[++i]); + color.y = atof(av[++i]); + color.z = atof(av[++i]); + color.w = atof(av[++i]); + tf_colors.push_back(color); + } else if (arg == "-tfs" || arg == "--tf-scale") { + tf_scale = atof(av[++i]); + } else if (arg == "-tff" || arg == "--tf-file") { + importTransferFunction(std::string(av[++i])); + loadedTransferFunction = true; + } else if (arg == "-is" || arg == "--surface") { + isosurfaces.push_back(atof(av[++i])); + } else { + FileName fn = arg; + if (fn.ext() == "osp") { + scene = arg; + loadedScene = true; + } } } - } - if (loadedScene) { - sceneModel = make_unique(); - if (!loadedTransferFunction) { - createDefaultTransferFunction(); + if (loadedScene) { + sceneModel = make_unique(); + if (!loadedTransferFunction) { + createDefaultTransferFunction(); + } + importObjectsFromFile(scene, loadedTransferFunction); } - importObjectsFromFile(scene, loadedTransferFunction); + + return loadedScene; + } + + std::deque VolumeSceneParser::model() const + { + std::deque models; + models.push_back(sceneModel == nullptr ? cpp::Model() : *sceneModel); + return models; + // return sceneModel.get() == nullptr ? cpp::Model() : *sceneModel; } - return loadedScene; -} - -std::deque VolumeSceneParser::model() const -{ - std::deque models; - models.push_back(sceneModel == nullptr ? cpp::Model() : *sceneModel); - return models; - // return sceneModel.get() == nullptr ? cpp::Model() : *sceneModel; -} - -std::deque VolumeSceneParser::bbox() const -{ - std::deque boxes; - boxes.push_back(sceneBbox); - return boxes; -} - -void VolumeSceneParser::importObjectsFromFile(const std::string &filename, - bool loadedTransferFunction) -{ - auto &model = *sceneModel; - - // Load OSPRay objects from a file. - ospray::importer::Group *imported = ospray::importer::import(filename); - - // Iterate over geometries - for (size_t i = 0; i < imported->geometry.size(); i++) { - auto geometry = ospray::cpp::Geometry(imported->geometry[i]->handle); - geometry.commit(); - model.addGeometry(geometry); + std::deque VolumeSceneParser::bbox() const + { + std::deque boxes; + boxes.push_back(sceneBbox); + return boxes; } - // Iterate over volumes - for (size_t i = 0 ; i < imported->volume.size(); i++) { - ospray::importer::Volume *vol = imported->volume[i]; - auto volume = ospray::cpp::Volume(vol->handle); - - // For now we set the same transfer function on all volumes. - volume.set("transferFunction", transferFunction); - volume.set("samplingRate", samplingRate); - volume.commit(); - - // Add the loaded volume(s) to the model. - model.addVolume(volume); - - // Set the minimum and maximum values in the domain for both color and - // opacity components of the transfer function if we didn't load a transfer - // function for a file (in that case this is already set) - if (!loadedTransferFunction) { - transferFunction.set("valueRange", vol->voxelRange.x, vol->voxelRange.y); - transferFunction.commit(); + void VolumeSceneParser::importObjectsFromFile(const std::string &filename, + bool loadedTransferFunction) + { + auto &model = *sceneModel; + + // Load OSPRay objects from a file. + ospray::importer::Group *imported = ospray::importer::import(filename); + + // Iterate over geometries + for (size_t i = 0; i < imported->geometry.size(); i++) { + auto geometry = ospray::cpp::Geometry(imported->geometry[i]->handle); + geometry.commit(); + model.addGeometry(geometry); } - //sceneBbox.extend(vol->bounds); - sceneBbox = vol->bounds; + // Iterate over volumes + for (size_t i = 0 ; i < imported->volume.size(); i++) { + ospray::importer::Volume *vol = imported->volume[i]; + auto volume = ospray::cpp::Volume(vol->handle); + + // For now we set the same transfer function on all volumes. + volume.set("transferFunction", transferFunction); + volume.set("samplingRate", samplingRate); + volume.commit(); + + // Add the loaded volume(s) to the model. + model.addVolume(volume); + + // Set the minimum and maximum values in the domain for both color and + // opacity components of the transfer function if we didn't load a transfer + // function for a file (in that case this is already set) + if (!loadedTransferFunction) { + transferFunction.set("valueRange", vol->voxelRange.x, vol->voxelRange.y); + transferFunction.commit(); + } + + //sceneBbox.extend(vol->bounds); + sceneBbox = vol->bounds; - // Create any specified isosurfaces - if (!isosurfaces.empty()) { - auto isoValueData = ospray::cpp::Data(isosurfaces.size(), OSP_FLOAT, - isosurfaces.data()); - auto isoGeometry = ospray::cpp::Geometry("isosurfaces"); + // Create any specified isosurfaces + if (!isosurfaces.empty()) { + auto isoValueData = ospray::cpp::Data(isosurfaces.size(), OSP_FLOAT, + isosurfaces.data()); + auto isoGeometry = ospray::cpp::Geometry("isosurfaces"); - isoGeometry.set("isovalues", isoValueData); - isoGeometry.set("volume", volume); - isoGeometry.commit(); + isoGeometry.set("isovalues", isoValueData); + isoGeometry.set("volume", volume); + isoGeometry.commit(); - model.addGeometry(isoGeometry); + model.addGeometry(isoGeometry); + } } - } - model.commit(); -} + model.commit(); + } -void VolumeSceneParser::importTransferFunction(const std::string &filename) -{ - tfn::TransferFunction fcn(filename); - auto colorsData = ospray::cpp::Data(fcn.rgbValues.size(), OSP_FLOAT3, - fcn.rgbValues.data()); - auto tfFromEnv = getEnvVar("OSPRAY_USE_TF_TYPE"); + void VolumeSceneParser::importTransferFunction(const std::string &filename) + { + tfn::TransferFunction fcn(filename); + auto colorsData = ospray::cpp::Data(fcn.rgbValues.size(), OSP_FLOAT3, + fcn.rgbValues.data()); + auto tfFromEnv = getEnvVar("OSPRAY_USE_TF_TYPE"); - if (tfFromEnv.first) { - transferFunction = cpp::TransferFunction(tfFromEnv.second); - } else { - transferFunction = cpp::TransferFunction("piecewise_linear"); - } - transferFunction.set("colors", colorsData); - - tf_scale = fcn.opacityScaling; - // Sample the opacity values, taking 256 samples to match the volume viewer - // the volume viewer does the sampling a bit differently so we match that - // instead of what's done in createDefault - std::vector opacityValues; - const int N_OPACITIES = 256; - size_t lo = 0; - size_t hi = 1; - for (int i = 0; i < N_OPACITIES; ++i) { - const float x = float(i) / float(N_OPACITIES - 1); - float opacity = 0; - if (i == 0) { - opacity = fcn.opacityValues[0].y; - } else if (i == N_OPACITIES - 1) { - opacity = fcn.opacityValues.back().y; + if (tfFromEnv.first) { + transferFunction = cpp::TransferFunction(tfFromEnv.second); } else { - // If we're over this val, find the next segment - if (x > fcn.opacityValues[lo].x) { - for (size_t j = lo; j < fcn.opacityValues.size() - 1; ++j) { - if (x <= fcn.opacityValues[j + 1].x) { - lo = j; - hi = j + 1; - break; + transferFunction = cpp::TransferFunction("piecewise_linear"); + } + transferFunction.set("colors", colorsData); + + tf_scale = fcn.opacityScaling; + // Sample the opacity values, taking 256 samples to match the volume viewer + // the volume viewer does the sampling a bit differently so we match that + // instead of what's done in createDefault + std::vector opacityValues; + const int N_OPACITIES = 256; + size_t lo = 0; + size_t hi = 1; + for (int i = 0; i < N_OPACITIES; ++i) { + const float x = float(i) / float(N_OPACITIES - 1); + float opacity = 0; + if (i == 0) { + opacity = fcn.opacityValues[0].y; + } else if (i == N_OPACITIES - 1) { + opacity = fcn.opacityValues.back().y; + } else { + // If we're over this val, find the next segment + if (x > fcn.opacityValues[lo].x) { + for (size_t j = lo; j < fcn.opacityValues.size() - 1; ++j) { + if (x <= fcn.opacityValues[j + 1].x) { + lo = j; + hi = j + 1; + break; + } } } + const float delta = x - fcn.opacityValues[lo].x; + const float interval = fcn.opacityValues[hi].x - fcn.opacityValues[lo].x; + if (delta == 0 || interval == 0) { + opacity = fcn.opacityValues[lo].y; + } else { + opacity = fcn.opacityValues[lo].y + delta / interval + * (fcn.opacityValues[hi].y - fcn.opacityValues[lo].y); + } } - const float delta = x - fcn.opacityValues[lo].x; - const float interval = fcn.opacityValues[hi].x - fcn.opacityValues[lo].x; - if (delta == 0 || interval == 0) { - opacity = fcn.opacityValues[lo].y; - } else { - opacity = fcn.opacityValues[lo].y + delta / interval - * (fcn.opacityValues[hi].y - fcn.opacityValues[lo].y); - } + opacityValues.push_back(tf_scale * opacity); } - opacityValues.push_back(tf_scale * opacity); - } - auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), - OSP_FLOAT, - opacityValues.data()); - transferFunction.set("opacities", opacityValuesData); - transferFunction.set("valueRange", vec2f(fcn.dataValueMin, fcn.dataValueMax)); - - // Commit transfer function - transferFunction.commit(); -} -void VolumeSceneParser::createDefaultTransferFunction() -{ - auto tfFromEnv = getEnvVar("OSPRAY_USE_TF_TYPE"); - - if (tfFromEnv.first) { - transferFunction = cpp::TransferFunction(tfFromEnv.second); - } else { - transferFunction = cpp::TransferFunction("piecewise_linear"); - } + auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), + OSP_FLOAT, + opacityValues.data()); + transferFunction.set("opacities", opacityValuesData); + transferFunction.set("valueRange", vec2f(fcn.dataValueMin, fcn.dataValueMax)); - // Add colors - std::vector colors; - if (tf_colors.empty()) { - colors.emplace_back(0.f, 0.f, 0.f, 0.f); - colors.emplace_back(0.9f, 0.9f, 0.9f, 1.f); - } else { - colors = tf_colors; + // Commit transfer function + transferFunction.commit(); } - std::vector colorsAsVec3; - for (auto &c : colors) colorsAsVec3.emplace_back(c.x, c.y, c.z); - auto colorsData = ospray::cpp::Data(colors.size(), OSP_FLOAT3, - colorsAsVec3.data()); - transferFunction.set("colors", colorsData); - - // Add opacities - std::vector opacityValues; - - const int N_OPACITIES = 64;//NOTE(jda) - This affects image quality and - // performance! - const int N_INTERVALS = colors.size() - 1; - const float OPACITIES_PER_INTERVAL = N_OPACITIES / float(N_INTERVALS); - for (int i = 0; i < N_OPACITIES; ++i) { - int lcolor = static_cast(i/OPACITIES_PER_INTERVAL); - int hcolor = lcolor + 1; - - float v0 = colors[lcolor].w; - float v1 = colors[hcolor].w; - float t = (i / OPACITIES_PER_INTERVAL) - lcolor; - - float opacity = (1-t)*v0 + t*v1; - if (opacity > 1.f) opacity = 1.f; - opacityValues.push_back(tf_scale*opacity); + void VolumeSceneParser::createDefaultTransferFunction() + { + auto tfFromEnv = getEnvVar("OSPRAY_USE_TF_TYPE"); + + if (tfFromEnv.first) { + transferFunction = cpp::TransferFunction(tfFromEnv.second); + } else { + transferFunction = cpp::TransferFunction("piecewise_linear"); + } + + // Add colors + std::vector colors; + if (tf_colors.empty()) { + colors.emplace_back(0.f, 0.f, 0.f, 0.f); + colors.emplace_back(0.9f, 0.9f, 0.9f, 1.f); + } else { + colors = tf_colors; + } + std::vector colorsAsVec3; + for (auto &c : colors) colorsAsVec3.emplace_back(c.x, c.y, c.z); + auto colorsData = ospray::cpp::Data(colors.size(), OSP_FLOAT3, + colorsAsVec3.data()); + transferFunction.set("colors", colorsData); + + // Add opacities + std::vector opacityValues; + + const int N_OPACITIES = 64;//NOTE(jda) - This affects image quality and + // performance! + const int N_INTERVALS = colors.size() - 1; + const float OPACITIES_PER_INTERVAL = N_OPACITIES / float(N_INTERVALS); + for (int i = 0; i < N_OPACITIES; ++i) { + int lcolor = static_cast(i/OPACITIES_PER_INTERVAL); + int hcolor = lcolor + 1; + + float v0 = colors[lcolor].w; + float v1 = colors[hcolor].w; + float t = (i / OPACITIES_PER_INTERVAL) - lcolor; + + float opacity = (1-t)*v0 + t*v1; + if (opacity > 1.f) opacity = 1.f; + opacityValues.push_back(tf_scale*opacity); + } + auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), + OSP_FLOAT, + opacityValues.data()); + transferFunction.set("opacities", opacityValuesData); + + // Commit transfer function + transferFunction.commit(); } - auto opacityValuesData = ospray::cpp::Data(opacityValues.size(), - OSP_FLOAT, - opacityValues.data()); - transferFunction.set("opacities", opacityValuesData); - - // Commit transfer function - transferFunction.commit(); -} + +} // ::commandline diff --git a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h index 278203706a..1630be817e 100644 --- a/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h +++ b/apps/common/commandline/SceneParser/volume/VolumeSceneParser.h @@ -26,37 +26,41 @@ #include #include -class OSPRAY_COMMANDLINE_INTERFACE VolumeSceneParser : public SceneParser -{ -public: - VolumeSceneParser(ospray::cpp::Renderer); +namespace commandline { - bool parse(int ac, const char **&av) override; + class OSPRAY_COMMANDLINE_INTERFACE VolumeSceneParser : public SceneParser + { + public: + VolumeSceneParser(ospray::cpp::Renderer); - std::deque model() const override; - std::deque bbox() const override; + bool parse(int ac, const char **&av) override; -private: + std::deque model() const override; + std::deque bbox() const override; - // Helper functions // + private: - void importObjectsFromFile(const std::string &filename, - bool loadedTransferFunction); - void importTransferFunction(const std::string &filename); - void createDefaultTransferFunction(); + // Helper functions // - // Data // + void importObjectsFromFile(const std::string &filename, + bool loadedTransferFunction); + void importTransferFunction(const std::string &filename); + void createDefaultTransferFunction(); - ospray::cpp::Renderer renderer; - ospcommon::box3f sceneBbox; + // Data // - std::unique_ptr sceneModel; + ospray::cpp::Renderer renderer; + ospcommon::box3f sceneBbox; - float samplingRate{0.125f}; + std::unique_ptr sceneModel; - float tf_scale{1.f}; - std::vector tf_colors; - std::vector isosurfaces; + float samplingRate{0.125f}; - ospray::cpp::TransferFunction transferFunction; -}; + float tf_scale{1.f}; + std::vector tf_colors; + std::vector isosurfaces; + + ospray::cpp::TransferFunction transferFunction; + }; + +} // ::commandline diff --git a/apps/common/commandline/Utility.h b/apps/common/commandline/Utility.h index ae2f0b7f18..6123907e07 100644 --- a/apps/common/commandline/Utility.h +++ b/apps/common/commandline/Utility.h @@ -28,59 +28,117 @@ #include #include -inline void parseForLoadingModules(int ac, const char**& av) -{ - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--module" || arg == "-m") { - ospLoadModule(av[++i]); +namespace commandline { + + inline void parseForLoadingModules(int ac, const char**& av) + { + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--module" || arg == "-m") { + ospLoadModule(av[++i]); + } } } -} - -using ParsedOSPObjects = std::tuple, - std::deque, - ospray::cpp::Renderer, - ospray::cpp::Camera>; - -template -inline ParsedOSPObjects parseCommandLine(int ac, const char **&av) -{ - static_assert(std::is_base_of::value, - "RendererParser_T is not a subclass of RendererParser."); - static_assert(std::is_base_of::value, - "CameraParser_T is not a subclass of CameraParser."); - static_assert(std::is_base_of::value, - "SceneParser_T is not a subclass of SceneParser."); - static_assert(std::is_base_of::value, - "LightsParser_T is not a subclass of LightsParser."); - - parseForLoadingModules(ac, av); - - CameraParser_T cameraParser; - cameraParser.parse(ac, av); - auto camera = cameraParser.camera(); - - RendererParser_T rendererParser; - rendererParser.parse(ac, av); - auto renderer = rendererParser.renderer(); - - SceneParser_T sceneParser{rendererParser.renderer()}; - sceneParser.parse(ac, av); - auto model = sceneParser.model(); - auto bbox = sceneParser.bbox(); - - LightsParser_T lightsParser(renderer); - lightsParser.parse(ac, av); - - return std::make_tuple(bbox, model, renderer, camera); -} - -inline ParsedOSPObjects parseWithDefaultParsers(int ac, const char**& av) -{ - return parseCommandLine(ac, av); -} + + using ParsedOSPObjects = std::tuple, + std::deque, + ospray::cpp::Renderer, + ospray::cpp::Camera>; + + template + inline ParsedOSPObjects parseCommandLine(int ac, const char **&av) + { + static_assert(std::is_base_of::value, + "RendererParser_T is not a subclass of RendererParser."); + static_assert(std::is_base_of::value, + "CameraParser_T is not a subclass of CameraParser."); + static_assert(std::is_base_of::value, + "SceneParser_T is not a subclass of SceneParser."); + static_assert(std::is_base_of::value, + "LightsParser_T is not a subclass of LightsParser."); + + parseForLoadingModules(ac, av); + + CameraParser_T cameraParser; + cameraParser.parse(ac, av); + auto camera = cameraParser.camera(); + + RendererParser_T rendererParser; + rendererParser.parse(ac, av); + auto renderer = rendererParser.create(); + + SceneParser_T sceneParser{renderer}; + sceneParser.parse(ac, av); + auto model = sceneParser.model(); + auto bbox = sceneParser.bbox(); + + LightsParser_T lightsParser(renderer); + lightsParser.parse(ac, av); + + return std::make_tuple(bbox, model, renderer, camera); + } + + inline ParsedOSPObjects parseWithDefaultParsers(int ac, const char**& av) + { + return parseCommandLine(ac, av); + } + + // Display Wall versions //////////////////////////////////////////////////// + + using ParsedOSPObjectsDW = std::tuple, + std::deque, + ospray::cpp::Renderer, + ospray::cpp::Renderer, + ospray::cpp::Camera>; + + template + inline ParsedOSPObjectsDW parseCommandLineDW(int ac, const char **&av) + { + static_assert(std::is_base_of::value, + "RendererParser_T is not a subclass of RendererParser."); + static_assert(std::is_base_of::value, + "CameraParser_T is not a subclass of CameraParser."); + static_assert(std::is_base_of::value, + "SceneParser_T is not a subclass of SceneParser."); + static_assert(std::is_base_of::value, + "LightsParser_T is not a subclass of LightsParser."); + + parseForLoadingModules(ac, av); + + CameraParser_T cameraParser; + cameraParser.parse(ac, av); + auto camera = cameraParser.camera(); + + RendererParser_T rendererParser; + rendererParser.parse(ac, av); + auto renderer = rendererParser.create(); + auto rendererDW = rendererParser.create(); + + SceneParser_T sceneParser{renderer}; + sceneParser.parse(ac, av); + auto model = sceneParser.model(); + auto bbox = sceneParser.bbox(); + + LightsParser_T lightsParser(renderer); + lightsParser.parse(ac, av); + + LightsParser_T lightsParserDW(rendererDW); + lightsParserDW.parse(ac, av); + + return std::make_tuple(bbox, model, renderer, rendererDW, camera); + } + + inline ParsedOSPObjectsDW parseWithDefaultParsersDW(int ac, const char**& av) + { + return parseCommandLineDW(ac, av); + } + +} // ::commandline diff --git a/apps/common/importer/CMakeLists.txt b/apps/common/importer/CMakeLists.txt index 63254907b1..451e9a6576 100644 --- a/apps/common/importer/CMakeLists.txt +++ b/apps/common/importer/CMakeLists.txt @@ -18,7 +18,6 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/ospray/include ${CMAKE_SOURCE_DIR}/ospray ${CMAKE_SOURCE_DIR} - ${CMAKE_SOURCE_DIR}/apps/qtViewer ) OSPRAY_CREATE_LIBRARY(ospray_importer @@ -28,7 +27,5 @@ OSPRAY_CREATE_LIBRARY(ospray_importer Importer.cpp TinyXML2.cpp LINK - ospray_common - ospray ospray_sg ) diff --git a/apps/common/importer/Importer.cpp b/apps/common/importer/Importer.cpp index c36dc048fe..099d5718af 100644 --- a/apps/common/importer/Importer.cpp +++ b/apps/common/importer/Importer.cpp @@ -38,46 +38,32 @@ namespace ospray { Group *group = existingGroupToAddTo; if (!group) group = new Group; - if (fileName.ext() == "osp") { - importOSP(fn, group); -#if 0 // NOTE(jda) - this can only be re-enabled once the importer stuff is off of Ref<> -//#ifndef _WIN32 - } else if (fileName.ext() == "osg") { - Ref world = new sg::World; - world = sg::loadOSG(fn); - Ref volumeNode; - for (auto node : world.ptr->node) - { - std::cout << "found node: " << node.ptr->toString() << std::endl; - if (node->toString().find("Chombo") != std::string::npos) - volumeNode = Ref((sg::Volume*)node.ptr); + if (fileName.ext() == "osp" || fileName.ext() == "osg") { + std::shared_ptr world;; + std::cout << "loading osp file: \n"; + world = sg::loadOSP(fn); + std::shared_ptr volumeNode; + for (auto &node : world->children()) { + if (node->type().find("Volume") != std::string::npos) + volumeNode = std::dynamic_pointer_cast(node); } - if (!volumeNode) - { - throw std::runtime_error("#ospray:importer: no volume found in osg file"); + if (!volumeNode) { + throw std::runtime_error("#ospray:importer: no volume found " + "in osp file"); } sg::RenderContext ctx; - Ref integrator; - integrator = new sg::Integrator("scivis"); - ctx.integrator = integrator.ptr; - integrator->commit(); - assert(ctx.world); - if (!world) { - std::cout << "#osp:qtv: no world defined. exiting." << std::endl; - exit(1); - } - - world->render(ctx); - assert(world->ospModel); + world->traverse(ctx, "verify"); + world->traverse(ctx, "print"); + world->traverse(ctx, "commit"); OSPVolume volume = volumeNode->volume; - assert(volume); Volume* msgVolume = new Volume; - msgVolume->bounds = volumeNode->getBounds(); + msgVolume->bounds = volumeNode->bounds(); msgVolume->handle = volumeNode->volume; + assert(msgVolume->handle); + msgVolume->voxelRange = volumeNode->child("voxelRange").valueAs(); group->volume.push_back(msgVolume); -#endif } else if (fileName.ext() == "bob") { importRM(fn, group); } else { @@ -88,5 +74,5 @@ namespace ospray { return group; } - } -} + } // ::ospray::importer +} // ::ospray diff --git a/apps/common/importer/Importer.h b/apps/common/importer/Importer.h index fde35a2331..6db34dfaa5 100644 --- a/apps/common/importer/Importer.h +++ b/apps/common/importer/Importer.h @@ -123,7 +123,7 @@ namespace ospray { OSPIMPORTER_INTERFACE Group *import(const std::string &fileName, Group *existingGroupToAddTo=nullptr); - } + } // ::ospray::importer //! Print an error message. inline void emitMessage(const std::string &kind, const std::string &message) @@ -151,4 +151,4 @@ namespace ospray { } #endif -} +} // ::ospray diff --git a/apps/common/importer/TinyXML2.h b/apps/common/importer/TinyXML2.h index 9545deb3b3..fa6ce1a775 100644 --- a/apps/common/importer/TinyXML2.h +++ b/apps/common/importer/TinyXML2.h @@ -351,14 +351,14 @@ class MemPoolT : public MemPool } } - virtual int ItemSize() const { + virtual int ItemSize() const override { return SIZE; } - int CurrentAllocs() const { + int CurrentAllocs() const { return _currentAllocs; } - virtual void* Alloc() { + virtual void* Alloc() override { if ( !_root ) { // Need a new block. Block* block = new Block(); @@ -381,7 +381,7 @@ class MemPoolT : public MemPool _nUntracked++; return result; } - virtual void Free( void* mem ) { + virtual void Free( void* mem ) override { if ( !mem ) { return; } @@ -398,7 +398,7 @@ class MemPoolT : public MemPool name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); } - void SetTracked() { + void SetTracked() override { _nUntracked--; } @@ -883,12 +883,12 @@ class TINYXML2_LIB XMLText : public XMLNode friend class XMLBase; friend class XMLDocument; public: - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - virtual XMLText* ToText() { + virtual XMLText* ToText() override { return this; } - virtual const XMLText* ToText() const { + virtual const XMLText* ToText() const override { return this; } @@ -901,9 +901,9 @@ class TINYXML2_LIB XMLText : public XMLNode return _isCData; } - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + char* ParseDeep( char*, StrPair* endTag ) override; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} @@ -921,18 +921,18 @@ class TINYXML2_LIB XMLComment : public XMLNode { friend class XMLDocument; public: - virtual XMLComment* ToComment() { + virtual XMLComment* ToComment() override { return this; } - virtual const XMLComment* ToComment() const { + virtual const XMLComment* ToComment() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + char* ParseDeep( char*, StrPair* endTag ) override; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: XMLComment( XMLDocument* doc ); @@ -959,18 +959,18 @@ class TINYXML2_LIB XMLDeclaration : public XMLNode { friend class XMLDocument; public: - virtual XMLDeclaration* ToDeclaration() { + virtual XMLDeclaration* ToDeclaration() override { return this; } - virtual const XMLDeclaration* ToDeclaration() const { + virtual const XMLDeclaration* ToDeclaration() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + char* ParseDeep( char*, StrPair* endTag ) override; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: XMLDeclaration( XMLDocument* doc ); @@ -991,18 +991,18 @@ class TINYXML2_LIB XMLUnknown : public XMLNode { friend class XMLDocument; public: - virtual XMLUnknown* ToUnknown() { + virtual XMLUnknown* ToUnknown() override { return this; } - virtual const XMLUnknown* ToUnknown() const { + virtual const XMLUnknown* ToUnknown() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + char* ParseDeep( char*, StrPair* endTag ) override; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; protected: XMLUnknown( XMLDocument* doc ); @@ -1159,13 +1159,13 @@ class TINYXML2_LIB XMLElement : public XMLNode SetValue( str, staticMem ); } - virtual XMLElement* ToElement() { + virtual XMLElement* ToElement() override { return this; } - virtual const XMLElement* ToElement() const { + virtual const XMLElement* ToElement() const override { return this; } - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; /** Given an attribute name, Attribute() returns the value for the attribute of that name, or null if none @@ -1482,9 +1482,9 @@ class TINYXML2_LIB XMLElement : public XMLNode int ClosingType() const { return _closingType; } - char* ParseDeep( char* p, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; + char* ParseDeep( char* p, StrPair* endTag ) override; + virtual XMLNode* ShallowClone( XMLDocument* document ) const override; + virtual bool ShallowEqual( const XMLNode* compare ) const override; private: XMLElement( XMLDocument* doc ); @@ -1525,10 +1525,10 @@ class TINYXML2_LIB XMLDocument : public XMLNode XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); ~XMLDocument(); - virtual XMLDocument* ToDocument() { + virtual XMLDocument* ToDocument() override { return this; } - virtual const XMLDocument* ToDocument() const { + virtual const XMLDocument* ToDocument() const override { return this; } @@ -1620,7 +1620,7 @@ class TINYXML2_LIB XMLDocument : public XMLNode @endverbatim */ void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; + virtual bool Accept( XMLVisitor* visitor ) const override; /** Create a new Element associated with @@ -1694,10 +1694,10 @@ class TINYXML2_LIB XMLDocument : public XMLNode // internal char* Identify( char* p, XMLNode** node ); - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { + virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const override { return 0; } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { + virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const override { return false; } @@ -2012,18 +2012,19 @@ class TINYXML2_LIB XMLPrinter : public XMLVisitor void PushDeclaration( const char* value ); void PushUnknown( const char* value ); - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { + virtual bool VisitEnter( const XMLDocument& /*doc*/ ) override; + virtual bool VisitExit( const XMLDocument& /*doc*/ ) override { return true; } - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); + virtual bool VisitEnter( const XMLElement& element, + const XMLAttribute* attribute ) override; + virtual bool VisitExit( const XMLElement& element ) override; - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); + virtual bool Visit( const XMLText& text ) override; + virtual bool Visit( const XMLComment& comment ) override; + virtual bool Visit( const XMLDeclaration& declaration ) override; + virtual bool Visit( const XMLUnknown& unknown ) override; /** If in print to memory mode, return a pointer to diff --git a/apps/common/miniSG/importRIVL.cpp b/apps/common/miniSG/importRIVL.cpp index 226c987c63..0c5e0f468a 100644 --- a/apps/common/miniSG/importRIVL.cpp +++ b/apps/common/miniSG/importRIVL.cpp @@ -72,42 +72,53 @@ namespace ospray { /*! Abstraction for a 'material' that a renderer can query from any geometry (it's up to the renderer to know what to do with the respective mateiral type) */ - struct RIVLMaterial : public miniSG::Node { - virtual string toString() const { return "ospray::miniSG::RIVLMaterial"; } + struct RIVLMaterial : public miniSG::Node + { + virtual string toString() const override + { return "ospray::miniSG::RIVLMaterial"; } Ref general; }; - struct RIVLCamera : public miniSG::Node { - virtual string toString() const { return "ospray::miniSG::RIVLCamera"; } + struct RIVLCamera : public miniSG::Node + { + virtual string toString() const override + { return "ospray::miniSG::RIVLCamera"; } vec3f from, at, up; }; /*! Scene graph grouping node */ - struct Group : public miniSG::Node { - virtual string toString() const; + struct Group : public miniSG::Node + { + virtual string toString() const override; std::vector > child; }; /*! scene graph node that contains an ospray geometry type (i.e., anything that defines some sort of geometric surface */ - struct Geometry : public miniSG::Node { - std::vector > material; - virtual string toString() const { return "ospray::miniSG::Geometry"; } + struct Geometry : public miniSG::Node + { + std::vector> material; + virtual string toString() const override + { return "ospray::miniSG::Geometry"; } }; /*! scene graph node that contains an ospray geometry type (i.e., anything that defines some sort of geometric surface */ - struct RIVLTexture : public miniSG::Node { - virtual string toString() const { return "ospray::miniSG::Texture"; } + struct RIVLTexture : public miniSG::Node + { + virtual string toString() const override + { return "ospray::miniSG::Texture"; } Ref texData; }; /*! scene graph node that contains an ospray geometry type (i.e., anything that defines some sort of geometric surface */ - struct Transform : public miniSG::Node { + struct Transform : public miniSG::Node + { Ref child; affine3f xfm; - virtual string toString() const { return "ospray::miniSG::Transform"; } + virtual string toString() const override + { return "ospray::miniSG::Transform"; } }; /*! a triangle mesh with 3 floats and 3 ints for vertex and index @@ -116,8 +127,9 @@ namespace ospray { // typename vtx_t=ospray::vec3f, // typename nor_t=ospray::vec3f, // typename txt_t=ospray::vec2f> - struct TriangleMesh : public miniSG::Geometry { - virtual string toString() const; + struct TriangleMesh : public miniSG::Geometry + { + virtual string toString() const override; TriangleMesh(); // /*! \brief data handle to vertex data array. @@ -177,7 +189,6 @@ namespace ospray { return ss.str(); } - std::string Group::toString() const { std::stringstream ss; @@ -206,11 +217,11 @@ namespace ospray { txt.ptr->texData = new miniSG::Texture2D; nodeList.push_back(txt.ptr); - int height = std::stol(node.getProp("height")); - int width = std::stol(node.getProp("width")); - int ofs = std::stol(node.getProp("ofs")); - int channels = std::stol(node.getProp("channels")); - int depth = std::stol(node.getProp("depth")); + size_t height = std::stoll(node.getProp("height")); + size_t width = std::stoll(node.getProp("width")); + size_t ofs = std::stoll(node.getProp("ofs")); + size_t channels = std::stoll(node.getProp("channels")); + size_t depth = std::stoll(node.getProp("depth")); std::string format = node.getProp("format"); txt.ptr->texData->channels = channels; @@ -248,6 +259,7 @@ namespace ospray { std::string name = node.getProp("name",""); std::string type = node.getProp("type"); + mat->setParam("type",strdup(type.c_str())); xml::for_each_child_of(node,[&](const xml::Node &child){ std::string childNodeType = child.name; @@ -312,14 +324,17 @@ namespace ospray { s = NEXT_TOK; int32_t w = atol(s); mat->setParam(childName.c_str(), vec4i(x,y,z,w)); + } else if (!childType.compare("texture")) { + std::cout << "not yet handling 'texture' fields in rivl files" << std::endl; + } else if (!childType.compare("material")) { + std::cout << "not yet handling 'material' fields in rivl files" << std::endl; } else { //error! throw std::runtime_error("unknown parameter type '" + childType + "' when parsing RIVL materials."); } free(value); } else if (!childNodeType.compare("textures")) { - int num = std::stoll(child.getProp("num","-1")); - + size_t num = std::stoll(child.getProp("num","-1")); if (child.content == "") { } else { char *tokenBuffer = strdup(child.content.c_str()); diff --git a/apps/common/miniSG/importTRI.cpp b/apps/common/miniSG/importTRI.cpp index 1676a56b74..9d517e0524 100644 --- a/apps/common/miniSG/importTRI.cpp +++ b/apps/common/miniSG/importTRI.cpp @@ -22,30 +22,64 @@ namespace ospray { using std::cout; using std::endl; - void importTRI(Model &model, + /*! NASA 'tri' format, with three vec3f vertices per triangle (no + indices in file) */ + void importTRI_xyz(Model &model, const ospcommon::FileName &fileName) { FILE *file = fopen(fileName.c_str(),"rb"); if (!file) error("could not open input file"); - int32_t numVertices; - auto rc = fread(&numVertices,1,sizeof(numVertices),file); - Mesh *mesh = new Mesh; + + vec3f tri[3]; + while (fread(&tri,sizeof(vec3f),3,file) == 3) { + mesh->position.push_back(tri[0]); + mesh->position.push_back(tri[1]); + mesh->position.push_back(tri[2]); + } + for (size_t i = 0; i < mesh->position.size()/3; i++) { + mesh->triangle.push_back(Triangle(vec3i(3*i)+vec3i(0,1,2))); + } + mesh->material = new Material; + mesh->material->name = "OBJ"; + mesh->material->setParam("Kd", vec3f(.7f)); + + model.instance.push_back(Instance(model.mesh.size())); model.mesh.push_back(mesh); + std::cout << "#msg: loaded .tri file of " << mesh->triangle.size() + << " triangles" << std::endl; + } + + /*! NASA 'tri' format, with three vec3fa vertices per triangle (no + indices in file); fourth component per vertex is typically a + scalar used for coloring - we ignore that for now */ + void importTRI_xyzs(Model &model, + const ospcommon::FileName &fileName) + { + FILE *file = fopen(fileName.c_str(),"rb"); + if (!file) error("could not open input file"); + + Mesh *mesh = new Mesh; - mesh->position.resize(numVertices); - mesh->normal.resize(numVertices); - mesh->triangle.resize(numVertices/3); - rc = fread(&mesh->position[0],numVertices,4*sizeof(float),file); - rc = fread(&mesh->normal[0],numVertices,4*sizeof(float),file); - (void)rc; - for (int i=0;itriangle[i].v0 = 3*i+0; - mesh->triangle[i].v1 = 3*i+1; - mesh->triangle[i].v2 = 3*i+2; + vec3fa tri[3]; + while (fread(&tri,sizeof(vec3f),3,file) == 3) { + mesh->position.push_back(tri[0]); + mesh->position.push_back(tri[1]); + mesh->position.push_back(tri[2]); } - model.instance.push_back(Instance(0)); + for (size_t i = 0; i < mesh->position.size()/3; i++) { + mesh->triangle.push_back(Triangle(vec3i(3*i)+vec3i(0,1,2))); + } + + mesh->material = new Material; + mesh->material->name = "OBJ"; + mesh->material->setParam("Kd", vec3f(.7f)); + + model.instance.push_back(Instance(model.mesh.size())); + model.mesh.push_back(mesh); + std::cout << "#msg: loaded .tri file of " << mesh->triangle.size() + << " triangles" << std::endl; } } // ::ospray::minisg diff --git a/apps/common/miniSG/miniSG.h b/apps/common/miniSG/miniSG.h index 508b2ac37f..a36ad8f652 100644 --- a/apps/common/miniSG/miniSG.h +++ b/apps/common/miniSG/miniSG.h @@ -187,6 +187,13 @@ namespace ospray { }; struct Triangle { + Triangle() {} + Triangle(uint32_t v0, uint32_t v1, uint32_t v2) + : v0(v0), v1(v1), v2(v2) + {} + Triangle(const vec3i &idx) + : v0(idx.x), v1(idx.y), v2(idx.z) + {} uint32_t v0, v1, v2; }; @@ -261,7 +268,9 @@ namespace ospray { OSPMINISG_INTERFACE void importHBP(Model &model, const FileName &fileName); /*! import a TRI file (format:vec3fa[3][numTris]), and add it to the specified model */ - OSPMINISG_INTERFACE void importTRI(Model &model, const FileName &fileName); + OSPMINISG_INTERFACE void importTRI_xyz(Model &model, const FileName &fileName); + /*! import a TRI file (format:vec3fa[3][numTris]), and add it to the specified model */ + OSPMINISG_INTERFACE void importTRI_xyzs(Model &model, const FileName &fileName); /*! import a wavefront OBJ file, and add it to the specified model */ OSPMINISG_INTERFACE void importRIVL(Model &model, const FileName &fileName); diff --git a/apps/common/script/chaiscript/chaiscript_defines.hpp b/apps/common/script/chaiscript/chaiscript_defines.hpp index f558847646..edf21dd345 100644 --- a/apps/common/script/chaiscript/chaiscript_defines.hpp +++ b/apps/common/script/chaiscript/chaiscript_defines.hpp @@ -75,12 +75,12 @@ #ifdef CHAISCRIPT_HAS_DECLSPEC #define CHAISCRIPT_MODULE_EXPORT extern "C" __declspec(dllexport) #else -#define CHAISCRIPT_MODULE_EXPORT extern "C" +#define CHAISCRIPT_MODULE_EXPORT extern "C" #endif #ifdef CHAISCRIPT_MSVC_12 #define CHAISCRIPT_NOEXCEPT throw() -#define CHAISCRIPT_CONSTEXPR +#define CHAISCRIPT_CONSTEXPR #else #define CHAISCRIPT_NOEXCEPT noexcept #define CHAISCRIPT_CONSTEXPR constexpr diff --git a/apps/common/sg/CMakeLists.txt b/apps/common/sg/CMakeLists.txt index a3043c7aa5..1e6d8582c1 100644 --- a/apps/common/sg/CMakeLists.txt +++ b/apps/common/sg/CMakeLists.txt @@ -21,21 +21,26 @@ OSPRAY_CREATE_LIBRARY(ospray_sg SHARED SceneGraph.cpp Renderer.cpp + geometry/Geometry.h geometry/Spheres.cpp geometry/TriangleMesh.cpp + camera/Camera.h camera/PerspectiveCamera.cpp - common/Serialization.cpp common/Common.cpp + common/Data.h + common/FrameBuffer.cpp + common/Light.cpp + common/Material.cpp common/Node.cpp - common/Integrator.cpp - common/World.cpp + common/RenderContext.h + common/Serialization.cpp + common/TimeStamp.cpp common/Texture2D.cpp - common/Material.cpp common/Transform.cpp - common/FrameBuffer.cpp - + common/World.cpp + transferFunction/TransferFunction.cpp volume/Volume.cpp @@ -45,8 +50,9 @@ OSPRAY_CREATE_LIBRARY(ospray_sg SHARED # scene graph importers importer/Importer.cpp + importer/importPoints.cpp +# all these should be lower-case: they're functions, not classes importer/ImportOSP.cpp -# importer/ImportOSG.cpp importer/ImportOBJ.cpp importer/ImportPLY.cpp importer/ImportRIVL.cpp @@ -54,4 +60,5 @@ LINK ospray ospray_common ospray_xml + ospray_minisg ) diff --git a/apps/common/sg/Renderer.cpp b/apps/common/sg/Renderer.cpp index ed81b83cba..fd5c4e0891 100644 --- a/apps/common/sg/Renderer.cpp +++ b/apps/common/sg/Renderer.cpp @@ -20,150 +20,168 @@ namespace ospray { namespace sg { - using std::cout; - using std::endl; Renderer::Renderer() - // : ospFrameBuffer(NULL) - {} - - int Renderer::renderFrame() - { - if (!integrator) return 1; - if (!frameBuffer) return 2; - if (!camera) return 3; - if (!world) return 4; - - assert(integrator->ospRenderer); - - if (!world->ospModel) { - RenderContext rootContext; - // geometries need the integrator to create materials - rootContext.integrator = integrator; - world->render(rootContext); - assert(world->ospModel); - } - - integrator->setWorld(world); - integrator->setCamera(camera); - integrator->commit(); - camera->commit(); - - ospSet1f(camera->ospCamera,"aspect",frameBuffer->getSize().x/float(frameBuffer->getSize().y)); - ospCommit(camera->ospCamera); - ospRenderFrame(frameBuffer->getOSPHandle(), - integrator->getOSPHandle(), - OSP_FB_COLOR|OSP_FB_ACCUM); - accumID++; - - return 0; - } - - /*! re-start accumulation (for progressive rendering). make sure - that this function gets called at lesat once every time that - anything changes that might change the appearance of the - converged image (e.g., camera position, scene, frame size, - etc) */ - void Renderer::resetAccumulation() { - if (accumID == 0) { - // cout << "accumID already 0..." << endl; - } else { - accumID = 0; - // cout << "resetting accum" << endl; - if (frameBuffer) - frameBuffer->clear(); - } + createChild("bounds", "box3f"); + createChild("rendererType", "string", std::string("scivis"), + NodeFlags::required | + NodeFlags::valid_whitelist | + NodeFlags::gui_combo, + "scivis: standard whitted style ray tracer. " + "pathtracer/pt: photo-realistic path tracer"); + child("rendererType").setWhiteList({std::string("scivis"), + std::string("sv"), + std::string("raytracer"), + std::string("rt"), + std::string("ao"), + std::string("ao1"), + std::string("ao2"), + std::string("ao4"), + std::string("ao8"), + std::string("ao16"), + std::string("dvr"), + std::string("pathtracer"), + std::string("pt")}); + createChild("world", + "World").setDocumentation("model containing scene objects"); + createChild("camera", "PerspectiveCamera"); + createChild("frameBuffer", "FrameBuffer"); + createChild("lights"); + + createChild("bgColor", "vec3f", vec3f(0.9f, 0.9f, 0.9f), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color); + + createChild("spp", "int", 1, + NodeFlags::required | NodeFlags::gui_slider, + "the number of samples rendered per pixel. The higher " + "the number, the smoother the resulting image."); + child("spp").setMinMax(-8,128); + + createChild("varianceThreshold", "float", 0.f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider, + "the percent (%) threshold of pixel difference to enable" + " tile rendering early termination."); + child("varianceThreshold").setMinMax(0.f, 25.f); + + //TODO: move these to seperate SciVisRenderer + createChild("shadowsEnabled", "bool", true); + createChild("maxDepth", "int", 5, + NodeFlags::required | NodeFlags::valid_min_max, + "maximum number of ray bounces").setMinMax(0,999); + createChild("aoSamples", "int", 1, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider, + "AO samples per frame.").setMinMax(0,128); + + createChild("aoDistance", "float", 10000.f, + NodeFlags::required | NodeFlags::valid_min_max, + "maximum distance ao rays will trace to." + " Useful if you do not want a large interior of a" + " building to be completely black from occlusion."); + child("aoDistance").setMinMax(1e-20f, 1e20f); + + createChild("oneSidedLighting", "bool", true, NodeFlags::required); + createChild("aoTransparency", "bool", true, NodeFlags::required); } - //! create a default camera - std::shared_ptr Renderer::createDefaultCamera(vec3f up) + void Renderer::traverse(RenderContext &ctx, const std::string& operation) { - // create a default camera - std::shared_ptr camera = std::make_shared(); - if (world) { - - // now, determine world bounds to automatically focus the camera - box3f worldBounds = world->getBounds(); - if (worldBounds == box3f(empty)) { - cout << "#osp:qtv: world bounding box is empty, using default camera pose" << endl; - } else { - cout << "#osp:qtv: found world bounds " << worldBounds << endl; - cout << "#osp:qtv: focussing default camera on world bounds" << endl; - - camera->setAt(center(worldBounds)); - if (up == vec3f(0,0,0)) - up = vec3f(0,1,0); - camera->setUp(up); - camera->setFrom(center(worldBounds) + .3f*vec3f(-1,+3,+1.5)*worldBounds.size()); - } + if (operation == "render") + { + preRender(ctx); + postRender(ctx); } - camera->commit(); - return std::dynamic_pointer_cast(camera); + else + Node::traverse(ctx,operation); } - void Renderer::setCamera(const std::shared_ptr &camera) + void Renderer::postRender(RenderContext &ctx) { - this->camera = camera; - if (camera) - this->camera->commit(); - // if (this->camera) { - // this->camera->commit(); - // } - if (integrator) - integrator->setCamera(camera); -// camera && integrator && integrator->ospRenderer) { -// ospSetObject(integrator->ospRenderer,"camera",camera->ospCamera); -// ospCommit(integrator->ospRenderer); -// } - resetAccumulation(); - } - - void Renderer::setIntegrator(const std::shared_ptr &integrator) - { - this->integrator = integrator; - if (integrator) { - integrator->commit(); - } - resetAccumulation(); + auto fb = (OSPFrameBuffer)child("frameBuffer").valueAs(); + ospRenderFrame(fb, + ospRenderer, + OSP_FB_COLOR | OSP_FB_ACCUM); } - void Renderer::setWorld(const std::shared_ptr &world) + void Renderer::preRender(RenderContext& ctx) { - this->world = world; - allNodes.clear(); - uniqueNodes.clear(); - if (world) { - allNodes.serialize(world,sg::Serialization::DONT_FOLLOW_INSTANCES); - uniqueNodes.serialize(world,sg::Serialization::DO_FOLLOW_INSTANCES); - } else - std::cout << "#osp:sg:renderer: no world defined, yet\n#ospQTV: (did you forget to pass a scene file name on the command line?)" << std::endl; - - resetAccumulation(); - std::cout << "#osp:sg:renderer: new world with " << world->nodes.size() << " nodes" << endl; + ctx.ospRenderer = ospRenderer; } - //! find the last camera in the scene graph - std::shared_ptr Renderer::getLastDefinedCamera() const + void Renderer::preCommit(RenderContext &ctx) { - std::shared_ptr lastCamera; - for (std::shared_ptr obj : uniqueNodes.object) { - std::shared_ptr asCamera = std::dynamic_pointer_cast(obj->node); - if (asCamera) lastCamera = asCamera; + if (child("frameBuffer")["size"].lastModified() > + child("camera")["aspect"].lastCommitted()) { + + child("camera")["aspect"].setValue( + child("frameBuffer")["size"].valueAs().x / + float(child("frameBuffer")["size"].valueAs().y) + ); } - return lastCamera; + auto rendererType = child("rendererType").valueAs(); + if (!ospRenderer || rendererType != createdType) { + traverse(ctx, "modified"); + ospRenderer = ospNewRenderer(rendererType.c_str()); + assert(ospRenderer); + createdType = rendererType; + ospCommit(ospRenderer); + setValue((OSPObject)ospRenderer); + } + ctx.ospRenderer = ospRenderer; } - - //! find the last integrator in the scene graph - std::shared_ptr Renderer::getLastDefinedIntegrator() const + + void Renderer::postCommit(RenderContext &ctx) { - std::shared_ptr lastIntegrator; - for (std::shared_ptr obj : uniqueNodes.object) { - std::shared_ptr asIntegrator = std::dynamic_pointer_cast(obj->node); - if (asIntegrator) lastIntegrator = asIntegrator; + if (child("camera").childrenLastModified() > frameMTime + || child("lights").childrenLastModified() > frameMTime + || lastModified() > frameMTime + || child("shadowsEnabled").lastModified() > frameMTime + || child("aoSamples").lastModified() > frameMTime + || child("spp").lastModified() > frameMTime + || child("world").childrenLastModified() > frameMTime + ) + { + ospFrameBufferClear( + (OSPFrameBuffer)child("frameBuffer").valueAs(), + OSP_FB_COLOR | OSP_FB_ACCUM + ); + + if (lightsData == nullptr || + lightsBuildTime < child("lights").childrenLastModified()) + { + // create and setup light for Ambient Occlusion + std::vector lights; + for(auto &lightNode : child("lights").children()) + lights.push_back((OSPLight)lightNode->valueAs()); + + if (lightsData) + ospRelease(lightsData); + lightsData = ospNewData(lights.size(), OSP_LIGHT, &lights[0]); + ospCommit(lightsData); + lightsBuildTime = TimeStamp(); + } + + // complete setup of renderer + ospSetObject(ospRenderer,"camera", child("camera").valueAs()); + ospSetObject(ospRenderer, "lights", lightsData); + if (child("world").childrenLastModified() > frameMTime) + { + child("world").traverse(ctx, "render"); + ospSetObject(ospRenderer, "model", child("world").valueAs()); + } + ospCommit(ospRenderer); + frameMTime = TimeStamp(); } - return lastIntegrator; + } - - } -} + + OSP_REGISTER_SG_NODE(Renderer); + + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/Renderer.h b/apps/common/sg/Renderer.h index 31471f21be..90f4ab0d2a 100644 --- a/apps/common/sg/Renderer.h +++ b/apps/common/sg/Renderer.h @@ -20,59 +20,32 @@ namespace ospray { namespace sg { - class FrameBuffer; - struct Renderer { - Renderer(); - - /*! re-start accumulation (for progressive rendering). make sure - that this function gets called at lesat once every time that - anything changes that might change the appearance of the - converged image (e.g., camera position, scene, frame size, - etc) */ - void resetAccumulation(); - - void setWorld(const std::shared_ptr &world); - void setCamera(const std::shared_ptr &camera); - void setIntegrator(const std::shared_ptr &integrator); - // ------------------------------------------------------- - // query functions - // ------------------------------------------------------- - - //! find the last camera in the scene graph - std::shared_ptr getLastDefinedCamera() const; - //! find the last integrator in the scene graph - std::shared_ptr getLastDefinedIntegrator() const; - - //! create a default camera - std::shared_ptr createDefaultCamera(vec3f up=vec3f(0,1,0)); + class FrameBuffer; - // //! set a default camera - // void setDefaultCamera() { setCamera(createDefaultCamera()); } + struct Renderer : public Renderable + { + Renderer(); - /*! render a frame. return 0 if successful, any non-zero number if not */ - virtual int renderFrame(); + // renderer renders the scene into the framebuffer on render call. + // It will call render on model when commit when model modified + virtual void traverse(RenderContext &ctx, const std::string& operation) override; + void preRender(RenderContext &ctx) override; + void postRender(RenderContext &ctx) override; + void preCommit(RenderContext &ctx) override; + void postCommit(RenderContext &ctx) override; - // ======================================================= - // state variables - // ======================================================= - std::shared_ptr world; - std::shared_ptr camera; - std::shared_ptr frameBuffer; - std::shared_ptr integrator; - // std::shared_ptr frame; + private: - // state variables - /*! all _unique_ nodes (i.e, even instanced nodes are listed - only once */ - Serialization uniqueNodes; - /*! _all_ nodes (i.e, instanced nodes are listed once for each - time they are instanced */ - Serialization allNodes; + // Data members // - //! accumulation ID - size_t accumID; + OSPRenderer ospRenderer {nullptr}; + OSPData lightsData {nullptr}; + TimeStamp lightsBuildTime; + TimeStamp frameMTime; + std::string createdType = "none"; }; - } -} + + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/SceneGraph.cpp b/apps/common/sg/SceneGraph.cpp index 04e24a89e4..1d852dcc14 100644 --- a/apps/common/sg/SceneGraph.cpp +++ b/apps/common/sg/SceneGraph.cpp @@ -25,28 +25,32 @@ namespace ospray { namespace sg { /*! 'render' the nodes */ - void Group::render(RenderContext &ctx) + std::string Group::toString() const { - for (auto child : children) { - assert(child); - child->render(ctx); - } + return "ospray::sg::Group"; } - - box3f Group::getBounds() + + std::string Info::toString() const { - box3f bounds = empty; - for (auto child : children) { - assert(child); - bounds.extend(child->getBounds()); - } - return bounds; + return "ospray::sg::Info"; } - void Node::serialize(sg::Serialization::State &state) - { - state.serialization->object.push_back(std::make_shared(shared_from_this(),state.instantiation)); + GenericGeometry::GenericGeometry(const std::string &type) + : Geometry(type) + { + } + + std::string GenericGeometry::toString() const + { + return "ospray::sg::GenericGeometry"; } + box3f GenericGeometry::bounds() const + { + return _bounds; + } + + OSP_REGISTER_SG_NODE(Group); + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/SceneGraph.h b/apps/common/sg/SceneGraph.h index 09a3350bf1..66a922a9f4 100644 --- a/apps/common/sg/SceneGraph.h +++ b/apps/common/sg/SceneGraph.h @@ -16,12 +16,18 @@ #pragma once -// std -#include +#ifdef _WIN32 +# ifdef ospray_sg_EXPORTS +# define OSPSG_INTERFACE __declspec(dllexport) +# else +# define OSPSG_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPSG_INTERFACE +#endif // sg components #include "sg/common/Node.h" -#include "sg/common/Integrator.h" #include "sg/common/Data.h" #include "sg/common/Transform.h" @@ -35,90 +41,65 @@ // ospcommon #include "ospcommon/FileName.h" -#ifdef _WIN32 -# ifdef ospray_sg_EXPORTS -# define OSPSG_INTERFACE __declspec(dllexport) -# else -# define OSPSG_INTERFACE __declspec(dllimport) -# endif -#else -# define OSPSG_INTERFACE -#endif - namespace ospray { namespace sg { + using ospcommon::FileName; /*! \brief allows for adding semantical info to a model/scene graph. \note will not do anything by itself. */ - struct Info : public sg::Node { + struct Info : public sg::Node + { /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::Info"; }; + std::string toString() const override; std::string permissions; std::string acks; std::string description; }; - struct Group : public sg::Node { + struct Group : public sg::Renderable + { /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::Group"; }; + std::string toString() const override; - /*! 'render' the nodes */ - virtual void render(RenderContext &ctx) override; - virtual box3f getBounds() override; - - std::vector > children; + std::vector> children; }; /*! a geometry node - the generic geometry node */ - struct GenericGeometry : public sg::Geometry { - GenericGeometry(const std::string &type) : Geometry(type) {}; + struct GenericGeometry : public sg::Geometry + { + GenericGeometry(const std::string &type); /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::GenericGeometry"; } - virtual box3f getBounds() override { return bounds; }; + std::string toString() const override; + box3f bounds() const override; /*! geometry type, i.e., 'spheres', 'cylinders', 'trianglemesh', ... */ const std::string type; - box3f bounds; - }; - - /*! a instance of another model */ - struct Instance : public sg::Geometry { - Instance() : Geometry("Instance") {}; - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::Instance"; } - - //! the model we're instancing - std::shared_ptr world; - }; - - /*! a light node - the generic light node */ - struct Light : public sg::Node { - //! \brief constructor - Light(const std::string &type) : type(type) {}; - - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::Light"; } - - /*! \brief light type, i.e., 'DirectionalLight', 'PointLight', ... */ - const std::string type; + box3f _bounds; }; /*! import an OBJ wavefront model, and add its contents to the given world */ - OSPSG_INTERFACE void importOBJ(const std::shared_ptr &world, const FileName &fileName); + OSPSG_INTERFACE void importOBJ(const std::shared_ptr &world, + const FileName &fileName); /*! import an PLY model, and add its contents to the given world */ - OSPSG_INTERFACE void importPLY(std::shared_ptr &world, const FileName &fileName); + OSPSG_INTERFACE void importPLY(std::shared_ptr &world, + const FileName &fileName); /*! import an X3D-format model, and add its contents to the given world */ - OSPSG_INTERFACE void importX3D(const std::shared_ptr &world, const FileName &fileName); + OSPSG_INTERFACE void importX3D(const std::shared_ptr &world, + const FileName &fileName); + + OSPSG_INTERFACE std::shared_ptr loadOSP(const std::string &fileName); + OSPSG_INTERFACE void loadOSP(std::shared_ptr world, const std::string &fileName); + OSPSG_INTERFACE std::shared_ptr readXML(const std::string &fileName); + OSPSG_INTERFACE void importRIVL(std::shared_ptr world, const std::string &fileName); + OSPSG_INTERFACE std::shared_ptr loadOSG(const std::string &fileName); - OSPSG_INTERFACE std::shared_ptr loadOSP(const std::string &fileName); - OSPSG_INTERFACE std::shared_ptr readXML(const std::string &fileName); - OSPSG_INTERFACE std::shared_ptr importRIVL(const std::string &fileName); - OSPSG_INTERFACE std::shared_ptr loadOSG(const std::string &fileName); + OSPSG_INTERFACE void loadOSG(const std::string &fileName, + std::shared_ptr world); /*! @} */ } // ::ospray::sg diff --git a/apps/common/sg/camera/Camera.h b/apps/common/sg/camera/Camera.h index e1d4212c53..54fc79b526 100644 --- a/apps/common/sg/camera/Camera.h +++ b/apps/common/sg/camera/Camera.h @@ -23,28 +23,57 @@ namespace ospray { namespace sg { /*! a camera node - the generic camera node */ - struct Camera : public sg::Node { - Camera(const std::string &type) : type(type), ospCamera(NULL) {}; - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const { return "ospray::sg::Camera"; } + struct OSPSG_INTERFACE Camera : public sg::Node + { + Camera(const std::string &type); + + virtual std::string toString() const override; + + virtual void create(); + virtual void destroy(); + virtual void postCommit(RenderContext &ctx) override; + + protected: + + // Data members // + /*! camera type, i.e., 'ao', 'obj', 'pathtracer', ... */ - const std::string type; - - virtual void create() { - if (ospCamera) destroy(); - ospCamera = ospNewCamera(type.c_str()); - commit(); - }; - virtual void commit() {} - virtual void destroy() { - if (!ospCamera) return; - ospRelease(ospCamera); - ospCamera = 0; - } - - OSPCamera ospCamera; + const std::string type; + OSPCamera ospCamera {nullptr}; }; + // Inlined Camera definitions ///////////////////////////////////////////// + + inline Camera::Camera(const std::string &type) : type(type) + { + } + + inline std::string Camera::toString() const + { + return "ospray::sg::Camera"; + } + + inline void Camera::create() + { + if (ospCamera) destroy(); + ospCamera = ospNewCamera(type.c_str()); + setValue((OSPObject)ospCamera); + } + + inline void Camera::destroy() + { + if (!ospCamera) return; + ospRelease(ospCamera); + ospCamera = 0; + } + + inline void Camera::postCommit(RenderContext &ctx) + { + if (!ospCamera) + create(); + ospCommit(ospCamera); + } + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/camera/PerspectiveCamera.cpp b/apps/common/sg/camera/PerspectiveCamera.cpp index 3b51b2e71e..3f783de61d 100644 --- a/apps/common/sg/camera/PerspectiveCamera.cpp +++ b/apps/common/sg/camera/PerspectiveCamera.cpp @@ -20,25 +20,36 @@ namespace ospray { namespace sg { PerspectiveCamera::PerspectiveCamera() - : Camera("perspective"), - from(0,-1,0), at(0,0,0), up(0,0,1), aspect(1), - fovy(60) + : Camera("perspective") { - create(); + createChild("pos", "vec3f", vec3f(0, -1, 0)); + createChild("dir", "vec3f", vec3f(0, 0, 0), + NodeFlags::required | NodeFlags::valid_min_max | + NodeFlags::required | NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(vec3f(-1), vec3f(1)); + createChild("up", "vec3f", vec3f(0, 0, 1),NodeFlags::required); + createChild("aspect", "float", 1.f, + NodeFlags::required | + NodeFlags::valid_min_max).setMinMax(1e-31f, 1e31f); + createChild("fovy", "float", 60.f, + NodeFlags::required | NodeFlags::valid_min_max | + NodeFlags::required | NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(.1f, 360.f); } - void PerspectiveCamera::commit() + void PerspectiveCamera::postCommit(RenderContext &ctx) { - if (!ospCamera) create(); + if (!ospCamera) create(); - ospSetVec3f(ospCamera,"pos",(const osp::vec3f&)from); - vec3f dir = (at - from); - ospSetVec3f(ospCamera,"dir",(const osp::vec3f&)dir); - ospSetVec3f(ospCamera,"up",(const osp::vec3f&)up); - ospSetf(ospCamera,"aspect",aspect); - ospSetf(ospCamera,"fovy",fovy); - ospCommit(ospCamera); + ospSetVec3f(ospCamera,"pos",(const osp::vec3f&)child("pos").valueAs()); + ospSetVec3f(ospCamera,"dir",(const osp::vec3f&)child("dir").valueAs()); + ospSetVec3f(ospCamera,"up",(const osp::vec3f&)child("up").valueAs()); + ospSetf(ospCamera,"aspect",child("aspect").valueAs()); + ospSetf(ospCamera,"fovy",child("fovy").valueAs()); + ospCommit(ospCamera); } + + OSP_REGISTER_SG_NODE(PerspectiveCamera); } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/camera/PerspectiveCamera.h b/apps/common/sg/camera/PerspectiveCamera.h index 4e1d896813..421baf3a27 100644 --- a/apps/common/sg/camera/PerspectiveCamera.h +++ b/apps/common/sg/camera/PerspectiveCamera.h @@ -21,19 +21,12 @@ namespace ospray { namespace sg { - struct PerspectiveCamera : public sg::Camera { + struct OSPSG_INTERFACE PerspectiveCamera : public sg::Camera + { PerspectiveCamera(); - virtual void commit(); - - SG_NODE_DECLARE_MEMBER(vec3f,from,From); - SG_NODE_DECLARE_MEMBER(vec3f,at,At); - SG_NODE_DECLARE_MEMBER(vec3f,up,Up); - SG_NODE_DECLARE_MEMBER(float,aspect,Aspect); - SG_NODE_DECLARE_MEMBER(float,fovy,Fovy); + void postCommit(RenderContext &ctx) override; }; } // ::ospray::sg } // ::ospray - - diff --git a/apps/common/sg/common/Common.cpp b/apps/common/sg/common/Common.cpp index 80ef8bf14b..abeeaba036 100644 --- a/apps/common/sg/common/Common.cpp +++ b/apps/common/sg/common/Common.cpp @@ -32,6 +32,9 @@ #define O_LARGEFILE 0 #endif +#include +#include + namespace ospray { namespace sg { diff --git a/apps/common/sg/common/Common.h b/apps/common/sg/common/Common.h index f606bc51a4..11759d0780 100644 --- a/apps/common/sg/common/Common.h +++ b/apps/common/sg/common/Common.h @@ -16,31 +16,31 @@ #pragma once -// use ospcommon vector types in ospray.h -//#define OSPRAY_EXTERNAL_VECTOR_TYPES 1 +#ifndef OSPSG_INTERFACE +#ifdef _WIN32 +# ifdef ospray_sg_EXPORTS +# define OSPSG_INTERFACE __declspec(dllexport) +# else +# define OSPSG_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPSG_INTERFACE +#endif +#endif + // ospcommon #include "ospcommon/AffineSpace.h" -// namespace osp { -// using ospcommon::vec2i; -// using ospcommon::vec2f; -// using ospcommon::vec3i; -// using ospcommon::vec3f; -// using ospcommon::vec4f; -// using ospcommon::affine3f; -// } - - // ospray API #include "ospray/ospray.h" +// core ospray +#include "ospray/common/OSPCommon.h" + namespace ospray { namespace sg { using namespace ospcommon; - typedef AffineSpace3f affine3f; - typedef LinearSpace3f linear3f; - #define THROW_SG_ERROR(err) \ throw std::runtime_error("in "+std::string(__PRETTY_FUNCTION__)+":"+std::string(err)) @@ -63,8 +63,10 @@ namespace ospray { 'integrator' */ struct Integrator; + struct RenderContext; + //! map the given file to memory and return that pointer - const unsigned char * mapFile(const std::string &fileName); + const unsigned char* mapFile(const std::string &fileName); } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/common/Data.h b/apps/common/sg/common/Data.h index ad7873a039..bae8d17e4e 100644 --- a/apps/common/sg/common/Data.h +++ b/apps/common/sg/common/Data.h @@ -31,13 +31,16 @@ namespace ospray { #define INVALID_DATA_ERROR throw RuntimeError("invalid data format ") - struct DataBuffer : public Node { + struct OSPSG_INTERFACE DataBuffer : public Node + { DataBuffer(OSPDataType type) : type(type), data(nullptr) {} - virtual ~DataBuffer() {} - virtual std::string toString() const { return "DataBuffer"; } + virtual ~DataBuffer() = default; + + virtual std::string toString() const override + { return "DataBuffer"; } virtual float get1f(index_t idx) const { INVALID_DATA_ERROR; } virtual vec2f get2f(index_t idx) const { INVALID_DATA_ERROR; } @@ -79,8 +82,8 @@ namespace ospray { : DataBuffer((OSPDataType)TID), size(size), mine(mine), base(base) { } - virtual void *getBase() const { return (void*)base; } - virtual size_t getSize() const { return size; } + virtual void *getBase() const override { return (void*)base; } + virtual size_t getSize() const override { return size; } virtual ~DataArrayT() { if (mine && base) delete base; } typedef T ElementType; @@ -90,84 +93,119 @@ namespace ospray { T *base; }; - struct DataArray1uc : public DataArrayT { + struct DataArray1uc : public DataArrayT + { DataArray1uc(unsigned char *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} }; - struct DataArray2f : public DataArrayT { + + struct DataArray2f : public DataArrayT + { DataArray2f(vec2f *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec2f get2f(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec2f get2f(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray3f : public DataArrayT { + + struct DataArray3f : public DataArrayT + { DataArray3f(vec3f *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec3f get3f(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec3f get3f(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray3fa : public DataArrayT { + + struct DataArray3fa : public DataArrayT + { DataArray3fa(vec3fa *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec3fa get3fa(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec3fa get3fa(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray4f : public DataArrayT { + + struct DataArray4f : public DataArrayT + { DataArray4f(vec4f *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec4f get4f(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec4f get4f(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray1i : public DataArrayT { + struct DataArray1i : public DataArrayT + { DataArray1i(int32_t *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual int32_t get1i(index_t idx) const { assert(base); return this->base[idx]; } + virtual int32_t get1i(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray3i : public DataArrayT { + struct DataArray3i : public DataArrayT + { DataArray3i(vec3i *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec3i get3i(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec3i get3i(index_t idx) const override + { assert(base); return this->base[idx]; } }; - struct DataArray4i : public DataArrayT { + + struct DataArray4i : public DataArrayT + { DataArray4i(vec4i *base, size_t size, bool mine=true) : DataArrayT(base,size,mine) {} - virtual vec4i get4i(index_t idx) const { assert(base); return this->base[idx]; } + virtual vec4i get4i(index_t idx) const override + { assert(base); return this->base[idx]; } }; // ------------------------------------------------------- // data *VECTORS* // ------------------------------------------------------- template - struct DataVectorT : public DataBuffer { + struct DataVectorT : public DataBuffer + { DataVectorT() : DataBuffer((OSPDataType)TID) {} - virtual void *getBase() const { return (void*)&v[0]; } - virtual size_t getSize() const { return v.size(); } + virtual void *getBase() const override { return (void*)&v[0]; } + virtual size_t getSize() const override { return v.size(); } inline void push_back(const T &t) { v.push_back(t); } std::vector v; }; - struct DataVector2f : public DataVectorT { - virtual vec2f get2f(index_t idx) const { assert(idxv[idx]; } + struct DataVector2f : public DataVectorT + { + virtual vec2f get2f(index_t idx) const override + { assert(idxv[idx]; } }; - struct DataVector3f : public DataVectorT { - virtual vec3f get3f(index_t idx) const { assert(idxv[idx]; } + + struct DataVector3f : public DataVectorT + { + virtual vec3f get3f(index_t idx) const override + { assert(idxv[idx]; } }; - struct DataVector3fa : public DataVectorT { - virtual vec3fa get3fa(index_t idx) const { assert(idxv[idx]; } + + struct DataVector3fa : public DataVectorT + { + virtual vec3fa get3fa(index_t idx) const override + { assert(idxv[idx]; } }; - struct DataVector4f : public DataVectorT { - virtual vec4f get4f(index_t idx) const { assert(idxv[idx]; } + + struct DataVector4f : public DataVectorT + { + virtual vec4f get4f(index_t idx) const override + { assert(idxv[idx]; } }; - struct DataVector3i : public DataVectorT { - virtual vec3i get3i(index_t idx) const { assert(idxv[idx]; } + + struct DataVector3i : public DataVectorT + { + virtual vec3i get3i(index_t idx) const override + { assert(idxv[idx]; } }; template diff --git a/apps/common/sg/common/FrameBuffer.cpp b/apps/common/sg/common/FrameBuffer.cpp index c0300f16d9..adeee2a214 100644 --- a/apps/common/sg/common/FrameBuffer.cpp +++ b/apps/common/sg/common/FrameBuffer.cpp @@ -19,24 +19,52 @@ namespace ospray { namespace sg { - FrameBuffer::FrameBuffer(const vec2i &size) - : size(size), - ospFrameBuffer(NULL) + FrameBuffer::FrameBuffer(vec2i size) { + createChild("size", "vec2i", size); + createChild("displayWall", "string", std::string("")); createFB(); - }; - + } + FrameBuffer::~FrameBuffer() { destroyFB(); } + void FrameBuffer::postCommit(RenderContext &ctx) + { + std::string displayWall = child("displayWall").valueAs(); + this->displayWallStream = displayWall; + + destroyFB(); + createFB(); + + if (displayWall != "") { + ospLoadModule("displayWald"); + OSPPixelOp pixelOp = ospNewPixelOp("display_wald"); + ospSetString(pixelOp,"streamName",displayWall.c_str()); + ospCommit(pixelOp); + ospSetPixelOp(ospFrameBuffer,pixelOp); + std::cout << "-------------------------------------------------------" + << std::endl; + std::cout << "this is the display wall frma ebuferr .. size is " + << size() << std::endl; + std::cout << "added display wall pixel op ..." << std::endl; + + std::cout << "created display wall pixelop, and assigned to frame buffer!" + << std::endl; + } + + + ospCommit(ospFrameBuffer); + } + unsigned char *FrameBuffer::map() { return (unsigned char *)ospMapFrameBuffer(ospFrameBuffer, OSP_FB_COLOR); } - void FrameBuffer::unmap(unsigned char *mem) + void FrameBuffer::unmap(void *mem) { ospUnmapFrameBuffer(mem,ospFrameBuffer); } @@ -51,9 +79,9 @@ namespace ospray { ospFrameBufferClear(ospFrameBuffer,OSP_FB_ACCUM); } - vec2i FrameBuffer::getSize() const + vec2i FrameBuffer::size() const { - return size; + return child("size").valueAs(); } /*! \brief returns a std::string with the c++ name of this class */ @@ -62,16 +90,21 @@ namespace ospray { return "ospray::sg::FrameBuffer"; } - OSPFrameBuffer FrameBuffer::getOSPHandle() const + OSPFrameBuffer FrameBuffer::handle() const { return ospFrameBuffer; } void ospray::sg::FrameBuffer::createFB() { - ospFrameBuffer = - ospNewFrameBuffer((const osp::vec2i &)size, OSP_FB_SRGBA, - OSP_FB_COLOR | OSP_FB_ACCUM); + auto fbsize = size(); + ospFrameBuffer = ospNewFrameBuffer((osp::vec2i&)fbsize, + (displayWallStream=="") + ? OSP_FB_SRGBA + : OSP_FB_NONE, + OSP_FB_COLOR | OSP_FB_ACCUM | + OSP_FB_VARIANCE); + setValue((OSPObject)ospFrameBuffer); } void ospray::sg::FrameBuffer::destroyFB() @@ -79,5 +112,7 @@ namespace ospray { ospFreeFrameBuffer(ospFrameBuffer); } + OSP_REGISTER_SG_NODE(FrameBuffer); + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/common/FrameBuffer.h b/apps/common/sg/common/FrameBuffer.h index 9e56265fef..bdf3b5e2b9 100644 --- a/apps/common/sg/common/FrameBuffer.h +++ b/apps/common/sg/common/FrameBuffer.h @@ -22,30 +22,30 @@ namespace ospray { namespace sg { - struct FrameBuffer : public sg::Node { - + struct OSPSG_INTERFACE FrameBuffer : public sg::Node + { /*! constructor allocates an OSP frame buffer object */ - FrameBuffer(const vec2i &size); + FrameBuffer(vec2i size = vec2i(300,300)); /*! destructor - relasess the OSP frame buffer object */ virtual ~FrameBuffer(); unsigned char *map(); - void unmap(unsigned char *mem); + void unmap(void *mem); void clear(); void clearAccum(); - vec2i getSize() const; + vec2i size() const; + + virtual void postCommit(RenderContext &ctx) override; /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const; + virtual std::string toString() const override; - OSPFrameBuffer getOSPHandle() const; + OSPFrameBuffer handle() const; - // private: - // create the ospray framebuffer for this class void createFB(); @@ -53,7 +53,7 @@ namespace ospray { void destroyFB(); OSPFrameBuffer ospFrameBuffer {nullptr}; - const vec2i size; + std::string displayWallStream; }; } // ::ospray::sg diff --git a/apps/common/sg/common/Integrator.cpp b/apps/common/sg/common/Integrator.cpp deleted file mode 100644 index 6dfefcb66e..0000000000 --- a/apps/common/sg/common/Integrator.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#undef NDEBUG - -// scene graph -#include "Integrator.h" - -namespace ospray { - namespace sg { - - /*! \brief returns a std::string with the c++ name of this class */ - std::string Integrator::toString() const - { - return "ospray::sg::Integrator(type='"+type+"')"; - } - - void Integrator::commit() - { - if (!ospRenderer) { - ospRenderer = ospNewRenderer(type.c_str()); - - // Set renderer defaults (if not using 'aoX' renderers) - if (type[0] != 'a' && type[1] != 'o') - { - ospSet1i(ospRenderer, "aoSamples", 1); - ospSet1i(ospRenderer, "shadowsEnabled", 1); - ospSet1i(ospRenderer, "aoTransparencyEnabled", 1); - } - - if (!ospRenderer) - throw std::runtime_error("#osp:sg:SceneGraph: could not create renderer (of type '"+type+"')"); - } - if (lastCommitted >= lastModified) return; - - ospSet1i(ospRenderer,"spp",spp); - - // set world, camera, ... - if (world ) { - world->commit(); - ospSetObject(ospRenderer,"world", world->ospModel); - ospSetObject(ospRenderer,"model", world->ospModel); - } - if (camera) { - camera->commit(); - ospSetObject(ospRenderer,"camera",camera->ospCamera); - } - - lastCommitted = rdtsc(); - ospCommit(ospRenderer); - assert(ospRenderer); - } - - void Integrator::setSPP(size_t spp) { - this->spp = spp; - if (ospRenderer) { - ospSet1i(ospRenderer,"spp",spp); - } - } - - } // ::ospray::sg -} // ::ospray diff --git a/apps/common/sg/common/Light.cpp b/apps/common/sg/common/Light.cpp new file mode 100644 index 0000000000..f597a7eea9 --- /dev/null +++ b/apps/common/sg/common/Light.cpp @@ -0,0 +1,105 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "sg/common/Light.h" + +namespace ospray { + namespace sg { + + void Light::preCommit(RenderContext &ctx) + { + if (!ospLight) + ospLight = ospNewLight(ctx.ospRenderer, type.c_str()); + ospCommit(ospLight); + setValue((OSPObject)ospLight); + } + + void Light::postCommit(RenderContext &ctx) + { + ospCommit(ospLight); + } + + std::string Light::toString() const + { + return "ospray::sg::Light"; + } + + AmbientLight::AmbientLight() + : Light("AmbientLight") + { + createChild("color", "vec3f", vec3f(.7f,.8f,1.f), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + createChild("intensity", "float", 0.2f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,4.f); + } + + DirectionalLight::DirectionalLight() + : Light("DirectionalLight") + { + createChild("direction", "vec3f", vec3f(-.3,.2,.4), + NodeFlags::required | + NodeFlags::gui_slider).setMinMax(vec3f(-1), vec3f(1)); + + createChild("color", "vec3f", vec3f(1.f), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + + createChild("intensity", "float", 3.f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,4.f); + + createChild("angularDiameter", "float", 0.01f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,4.f); + } + + PointLight::PointLight() + : Light("PointLight") + { + createChild("color", "vec3f", vec3f(1.f), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + + createChild("position", "vec3f", vec3f(0.f), + NodeFlags::required | NodeFlags::valid_min_max); + + createChild("intensity", "float", 3.f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,4.f); + + createChild("radius", "float", 1.0f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,4.f); + } + + OSP_REGISTER_SG_NODE(Light); + OSP_REGISTER_SG_NODE(DirectionalLight); + OSP_REGISTER_SG_NODE(AmbientLight); + OSP_REGISTER_SG_NODE(PointLight); + + } // ::ospray::sg +} // ::ospray + diff --git a/apps/common/sg/common/Integrator.h b/apps/common/sg/common/Light.h similarity index 54% rename from apps/common/sg/common/Integrator.h rename to apps/common/sg/common/Light.h index 16ae7ab348..ac9cdd1097 100644 --- a/apps/common/sg/common/Integrator.h +++ b/apps/common/sg/common/Light.h @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2017 Intel Corporation // +// Copyright 2009-2016 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -18,43 +18,45 @@ #include "sg/common/Node.h" #include "sg/common/Serialization.h" -#include "sg/common/World.h" +#include "sg/camera/Camera.h" + +#include namespace ospray { namespace sg { - struct Camera; - struct World; - - /*! a renderer node - the generic renderer node */ - struct Integrator : public sg::Node { - - /*! constructor */ - Integrator(const std::string &type) : type(type), ospRenderer(NULL), spp(1) {}; - - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; - - /*! update the current node's fields to ospray - the node must - already have been 'render'ed once before this can be called */ - virtual void commit() override; - - void setSPP(size_t spp); - - OSPRenderer getOSPHandle() const { return ospRenderer; } - - SG_NODE_DECLARE_MEMBER(std::shared_ptr,camera,Camera); - SG_NODE_DECLARE_MEMBER(std::shared_ptr,world,World); - - /*! renderer type, i.e., 'ao', 'obj', 'pathtracer', ... */ - const std::string type; - public: - OSPRenderer ospRenderer; - size_t spp; + /*! a light node - the generic light node */ + struct OSPSG_INTERFACE Light : public sg::Node + { + //! \brief constructor + Light() = default; + Light(const std::string &type) : type(type) {} + + virtual void preCommit(RenderContext &ctx) override; + virtual void postCommit(RenderContext &ctx) override; + + //! \brief returns a std::string with the c++ name of this class + virtual std::string toString() const override; + + /*! \brief light type, i.e., 'DirectionalLight', 'PointLight', ... */ + const std::string type = "none"; + OSPLight ospLight {nullptr}; }; - - } // ::ospray::sg -} // ::ospray + struct OSPSG_INTERFACE AmbientLight : public Light + { + AmbientLight(); + }; + + struct OSPSG_INTERFACE DirectionalLight : public Light + { + DirectionalLight(); + }; + struct OSPSG_INTERFACE PointLight : public Light + { + PointLight(); + }; + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/common/Material.cpp b/apps/common/sg/common/Material.cpp index ab7d98305c..270a8a0b76 100644 --- a/apps/common/sg/common/Material.cpp +++ b/apps/common/sg/common/Material.cpp @@ -16,33 +16,53 @@ #include "sg/common/Material.h" #include "sg/common/World.h" -#include "sg/common/Integrator.h" #include "ospray/ospray.h" namespace ospray { namespace sg { - /*! constructor */ Material::Material() - : ospMaterial(NULL), - name(""), - type("") { + createChild("type", "string", std::string("OBJMaterial")); + vec3f kd(10.f/255.f,68.f/255.f,117.f/255.f); + vec3f ks(208.f/255.f,140.f/255.f,82.f/255.f); + createChild("Ka", "vec3f",vec3f(1), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + createChild("Kd", "vec3f",kd, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + createChild("Ks", "vec3f",ks, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_color).setMinMax(vec3f(0), vec3f(1)); + createChild("Ns", "float",10.f, + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f, 100.f); + setValue((OSPObject)nullptr); } - void Material::render(RenderContext &ctx) + std::string Material::toString() const { - if (ospMaterial) return; - - ospMaterial = ospNewMaterial(ctx.integrator->getOSPHandle(), type.c_str()); + return "ospray::viewer::sg::Material"; + } - //We failed to create a material of the given type, handle it - if (!ospMaterial) { - std::cerr << "Warning: Could not create material type '" << type << "'. Replacing with default material." << std::endl; - //Replace with default - static OSPMaterial defaultMaterial = NULL; + void Material::preCommit(RenderContext &ctx) + { + assert(ctx.ospRenderer); + if (ospMaterial != nullptr && ospRenderer == ctx.ospRenderer) return; + auto mat = ospNewMaterial(ctx.ospRenderer, + child("type").valueAs().c_str()); + if (!mat) + { + std::cerr << "Warning: Could not create material type '" + << type << "'. Replacing with default material." << std::endl; + static OSPMaterial defaultMaterial = nullptr; if (!defaultMaterial) { - defaultMaterial = ospNewMaterial(ctx.integrator->getOSPHandle(), "default"); + defaultMaterial = ospNewMaterial(ctx.ospRenderer, "OBJ"); vec3f kd(.7f); vec3f ks(.3f); ospSet3fv(defaultMaterial, "Kd", &kd.x); @@ -50,76 +70,35 @@ namespace ospray { ospSet1f(defaultMaterial, "Ns", 99.f); ospCommit(defaultMaterial); } - ospMaterial = defaultMaterial; - return; + mat = defaultMaterial; } - for(size_t i = 0; i < textures.size(); i++) { - textures[i]->render(ctx); - } - - //Forward all params on to the ospMaterial... - for_each_param([&](const std::shared_ptr ¶m){ - switch(param->getOSPDataType()) { - case OSP_INT: - case OSP_UINT: - { - ParamT *p = (ParamT*)param.get(); - if(param->getName().find("map_") != std::string::npos) { - //Handle textures! - assert(textures[p->value]->ospTexture != NULL && "Texture should not be null at this point."); - ospSetObject(ospMaterial, param->getName().c_str(), textures[p->value]->ospTexture); - } else { - ospSet1i(ospMaterial, param->getName().c_str(), p->value); - } - } - break; - case OSP_INT3: - case OSP_UINT3: - { - ParamT *p = (ParamT*)param.get(); - ospSet3i(ospMaterial, param->getName().c_str(), p->value.x, p->value.y, p->value.z); - } - break; - case OSP_FLOAT: - { - ParamT *p = (ParamT*)param.get(); - ospSet1f(ospMaterial, param->getName().c_str(), p->value); - } - break; - case OSP_FLOAT2: - { - ParamT *p = (ParamT*)param.get(); - ospSet2fv(ospMaterial, param->getName().c_str(), &p->value.x); - } - break; - case OSP_FLOAT3: - { - ParamT *p = (ParamT*)param.get(); - ospSet3fv(ospMaterial, param->getName().c_str(), &p->value.x); - } - break; - case OSP_TEXTURE: - { - ParamT> *p = (ParamT> *)param.get(); - std::shared_ptr tex = p->value; - if (tex) { - tex->render(ctx); - if (tex->ospTexture) { - std::cout << "setting texture " << tex->toString() << " to mat value " << param->getName() << std::endl; - ospSetObject(ospMaterial, param->getName().c_str(), tex->ospTexture); - } - } - } - break; - default: //Catch not yet implemented data types - PRINT(param->getOSPDataType()); - std::cerr << "Warning: parameter '" << param->getName() << "' of material '" << name << "' had an invalid data type and will be ignored." << std::endl; - } - }); + setValue((OSPObject)mat); + ospMaterial = mat; + ospRenderer = ctx.ospRenderer; + } + void Material::postCommit(RenderContext &ctx) + { + if (hasChild("map_Kd")) + ospSetObject(valueAs(), "map_Kd", + child("map_Kd").valueAs()); + if (hasChild("map_Ks")) + ospSetObject(valueAs(), "map_Ks", + child("map_Ks").valueAs()); + if (hasChild("map_Ns")) + ospSetObject(valueAs(), "map_Ns", + child("map_Ns").valueAs()); + if (hasChild("map_d")) + ospSetObject(valueAs(), "map_d", + child("map_d").valueAs()); + if (hasChild("map_Bump")) + ospSetObject(valueAs(), "map_Bump", + child("map_Bump").valueAs()); ospCommit(ospMaterial); } - } -} + OSP_REGISTER_SG_NODE(Material); + + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/common/Material.h b/apps/common/sg/common/Material.h index 37f303c76a..35e4b66447 100644 --- a/apps/common/sg/common/Material.h +++ b/apps/common/sg/common/Material.h @@ -23,24 +23,28 @@ namespace ospray { namespace sg { /*! \brief Base class for all Material Types */ - struct Material : public Node { - /*! constructor */ + struct OSPSG_INTERFACE Material : public Node + { Material(); /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const { return "ospray::viewer::sg::Material"; }; - - //! 'render' the nodes - virtual void render(RenderContext &ctx); + virtual std::string toString() const override; + + virtual void preCommit(RenderContext &ctx) override; + virtual void postCommit(RenderContext &ctx) override; //! a logical name, of no other useful meaning whatsoever std::string name; - //! indicates the type of material/shader the renderer should use for these parameters + //! indicates the type of material/shader the renderer should use for + // these parameters std::string type; //! vector of textures used by the material + // Carson: what is this? seems to be used by RIVL. Is this supposed to be map_Kd? + // how do I use a vector of textures? std::vector> textures; - OSPMaterial ospMaterial; + OSPMaterial ospMaterial {nullptr}; + OSPRenderer ospRenderer {nullptr}; }; } // ::ospray::sg diff --git a/apps/common/sg/common/Node.cpp b/apps/common/sg/common/Node.cpp index bac7ba4c03..58170e605e 100644 --- a/apps/common/sg/common/Node.cpp +++ b/apps/common/sg/common/Node.cpp @@ -17,81 +17,496 @@ #include "sg/common/Node.h" #include "sg/common/Data.h" #include "sg/common/Texture2D.h" +#include "sg/common/RenderContext.h" namespace ospray { namespace sg { - - // ================================================================== - // parameter type specializations - // ================================================================== - template<> OSPDataType ParamT >::getOSPDataType() const - { return OSP_DATA; } - template<> OSPDataType ParamT >::getOSPDataType() const - { return OSP_OBJECT; } - - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_FLOAT; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_FLOAT2; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_FLOAT3; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_FLOAT4; } - - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_INT; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_INT2; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_INT3; } - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_INT4; } - - template<> OSPDataType ParamT::getOSPDataType() const - { return OSP_STRING; } - template<> OSPDataType ParamT >::getOSPDataType() const - { return OSP_TEXTURE; } - // ================================================================== // sg node implementations // ================================================================== - // void Node::addParam(sg::Param *p) - // { - // assert(p); - // assert(p->getName() != ""); - // param[p->getName()] = p; - // } - - void Node::setFromXML(const xml::Node &node, - const unsigned char *binBasePtr) - { - throw std::runtime_error(toString()+":setFromXML() not implemented for XML node type " - +node.name); - }; + + // NOTE(jda) - can't do default member initializers due to MSVC... + Node::Node() + { + properties.name = "NULL"; + properties.type = "Node"; + // MSVC 2013 is buggy and ignores {}-initialization of anonymous structs +#if _MSC_VER <= 1800 + properties.parent = nullptr; + properties.valid = false; +#endif + properties.flags = sg::NodeFlags::none; + markAsModified(); + } + + std::string Node::toString() const + { + return "ospray::sg::Node"; + } + + void Node::setFromXML(const xml::Node &node, const unsigned char *binBasePtr) + { + throw std::runtime_error(toString() + + ":setFromXML() not implemented for XML node type " + + node.name); + } + + void Node::serialize(sg::Serialization::State &state) + { + } + + // Properties ///////////////////////////////////////////////////////////// + + std::string Node::name() const + { + return properties.name; + } + + std::string Node::type() const + { + return properties.type; + } + + SGVar Node::min() const + { + return properties.minmax[0]; + } + + SGVar Node::max() const + { + return properties.minmax[1]; + } + + NodeFlags Node::flags() const + { + return properties.flags; + } + + std::string Node::documentation() const + { + return properties.documentation; + } + + void Node::setName(const std::string &v) + { + properties.name = v; + } + + void Node::setType(const std::string &v) + { + properties.type = v; + } + + void Node::setMinMax(const SGVar &minv, const SGVar &maxv) + { + properties.minmax.resize(2); + properties.minmax[0] = minv; + properties.minmax[1] = maxv; + } + + void Node::setFlags(NodeFlags f) + { + properties.flags = f; + } + + void Node::setFlags(int f) + { + setFlags(static_cast(f)); + } + + void Node::setDocumentation(const std::string &s) + { + properties.documentation = s; + } + + void Node::setWhiteList(const std::vector &values) + { + properties.whitelist = values; + } + + void Node::setBlackList(const std::vector &values) + { + properties.blacklist = values; + } + + bool Node::isValid() const + { + return properties.valid; + } + + bool Node::computeValid() + { +#ifndef _WIN32 +# warning "Are validation node flags mutually exclusive?" +#endif + + if ((flags() & NodeFlags::valid_min_max) && + properties.minmax.size() > 1) { + if (!computeValidMinMax()) + return false; + } + + if (flags() & NodeFlags::valid_blacklist) { + return std::find(properties.blacklist.begin(), + properties.blacklist.end(), + value()) == properties.blacklist.end(); + } + + if (flags() & NodeFlags::valid_whitelist) { + return std::find(properties.whitelist.begin(), + properties.whitelist.end(), + value()) != properties.whitelist.end(); + } + + return true; + } + + bool Node::computeValidMinMax() + { + return true; + } + + box3f Node::bounds() const + { + return empty; + } + + // Node stored value (data) interface ///////////////////////////////////// + + SGVar Node::value() + { + std::lock_guard lock{mutex}; + return properties.value; + } + + void Node::setValue(SGVar val) + { + { + std::lock_guard lock{mutex}; + if (val != properties.value) + properties.value = val; + } + + markAsModified(); + } + + // Update detection interface ///////////////////////////////////////////// + + TimeStamp Node::lastModified() const + { + return properties.lastModified; + } + + TimeStamp Node::lastCommitted() const + { + return properties.lastCommitted; + } + + TimeStamp Node::childrenLastModified() const + { + return properties.childrenMTime; + } + + void Node::markAsCommitted() + { + properties.lastCommitted = TimeStamp(); + } + + void Node::markAsModified() + { + properties.lastModified = TimeStamp(); + if (hasParent()) + parent().setChildrenModified(properties.lastModified); + } + + void Node::setChildrenModified(TimeStamp t) + { + if (t > properties.childrenMTime) { + properties.childrenMTime = t; + if (hasParent()) + parent().setChildrenModified(properties.childrenMTime); + } + } + + // Parent-child structual interface /////////////////////////////////////// + + bool Node::hasChild(const std::string &name) const + { + std::string lower=name; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + std::lock_guard lock{mutex}; + auto itr = properties.children.find(lower); + return itr != properties.children.end(); + } + + Node& Node::child(const std::string &name) const + { + std::string lower=name; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + std::lock_guard lock{mutex}; + auto itr = properties.children.find(lower); + if (itr == properties.children.end()) { + throw std::runtime_error("in node " + toString() + + " : could not find sg child node with name '" + + name + "'"); + } else { + return *itr->second; + } + } + + Node& Node::operator[](const std::string &c) const + { + return child(c); + } + + Node& Node::childRecursive(const std::string &name) + { + mutex.lock(); + Node* n = this; + auto f = n->properties.children.find(name); + if (f != n->properties.children.end()) { + mutex.unlock(); + return *f->second; + } + + for (auto &child : properties.children) { + mutex.unlock(); + try { + return child.second->childRecursive(name); + } + catch (const std::runtime_error &) {} + + mutex.lock(); + } + + mutex.unlock(); + throw std::runtime_error("error finding node in Node::childRecursive"); + } + + std::vector> Node::children() const + { + std::lock_guard lock{mutex}; + std::vector> result; + for (auto &child : properties.children) + result.push_back(child.second); + return result; + } + + std::map>& Node::childrenMap() + { + return properties.children; + } + + void Node::add(std::shared_ptr node) + { + std::lock_guard lock{mutex}; + const std::string& name = node->name(); + std::string lower=name; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + properties.children[lower] = node; + + node->setParent(*this); + } + + Node& Node::operator+=(std::shared_ptr n) + { + add(n); + return *this; + } + + Node& Node::createChild(std::string name, + std::string type, + SGVar var, + int flags, + std::string documentation) + { + auto child = createNode(name, type, var, flags, documentation); + add(child); + return *child; + } + + void Node::setChild(const std::string &name, + const std::shared_ptr &node) + { + std::lock_guard lock{mutex}; + std::string lower=name; + std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower); + properties.children[lower] = node; +#ifndef _WIN32 +# warning "TODO: child node parent needs to be set, which requires multi-parent support" +#endif + } + + bool Node::hasParent() const + { + return properties.parent != nullptr; + } + + Node& Node::parent() const + { + return *properties.parent; + } + + void Node::setParent(Node &p) + { + std::lock_guard lock{mutex}; + properties.parent = &p; + } + + void Node::setParent(const std::shared_ptr &p) + { + std::lock_guard lock{mutex}; + properties.parent = p.get(); + } + + // Traversal interface //////////////////////////////////////////////////// + + void Node::traverse(RenderContext &ctx, const std::string& operation) + { + //TODO: make child m time propagate + if (operation != "verify" && operation != "print" && !isValid()) + return; + + ctx._childMTime = TimeStamp(); + bool traverseChildren = true; + preTraverse(ctx, operation, traverseChildren); + ctx.level++; + + if (traverseChildren) + { + for (auto &child : properties.children) + child.second->traverse(ctx, operation); + } + + ctx.level--; + ctx._childMTime = childrenLastModified(); + postTraverse(ctx, operation); + } + + void Node::traverse(const std::string &operation) + { + RenderContext ctx; + traverse(ctx, operation); + } + + void Node::preTraverse(RenderContext &ctx, const std::string& operation, bool& traverseChildren) + { + if (operation == "print") { + for (int i=0;i= lastCommitted() || + childrenLastModified() >= lastCommitted()) + preCommit(ctx); + else + traverseChildren = false; + } else if (operation == "verify") { + if (properties.valid && childrenLastModified() < properties.lastVerified) + traverseChildren = false; + properties.valid = computeValid(); + if (!properties.valid) + std::cout << name() << " marked invalid\n"; + properties.lastVerified = TimeStamp(); + } else if (operation == "modified") { + markAsModified(); + } + } + + void Node::postTraverse(RenderContext &ctx, const std::string& operation) + { + if (operation == "commit" && + (lastModified() >= lastCommitted() || + childrenLastModified() >= lastCommitted())) { + postCommit(ctx); + markAsCommitted(); + } else if (operation == "verify") { + for (const auto &child : properties.children) { + if (child.second->flags() & NodeFlags::required) + properties.valid &= child.second->isValid(); + } + } + } + + void Node::preCommit(RenderContext &ctx) + { + } + + void Node::postCommit(RenderContext &ctx) + { + } // ================================================================== - // global struff + // Renderable // ================================================================== - - // list of all named nodes - for now use this as a global - // variable, but eventually we'll need tofind a better way for - // storing this - std::map > namedNodes; + void Renderable::preTraverse(RenderContext &ctx, + const std::string& operation, bool& traverseChildren) + { + Node::preTraverse(ctx,operation, traverseChildren); + if (operation == "render") + preRender(ctx); + } - std::shared_ptr findNamedNode(const std::string &name) + void Renderable::postTraverse(RenderContext &ctx, + const std::string& operation) { - auto it = namedNodes.find(name); - if (it != namedNodes.end()) - return it->second; - return {}; + Node::postTraverse(ctx,operation); + if (operation == "render") + postRender(ctx); } - void registerNamedNode(const std::string &name, const std::shared_ptr &node) + // ================================================================== + // global stuff + // ================================================================== + + using CreatorFct = sg::Node*(*)(); + + std::map nodeRegistry; + + std::shared_ptr createNode(std::string name, + std::string type, + SGVar var, + int flags, + std::string documentation) { - namedNodes[name] = node; + std::map::iterator it = nodeRegistry.find(type); + CreatorFct creator = nullptr; + + if (it == nodeRegistry.end()) { + std::string creatorName = "ospray_create_sg_node__"+std::string(type); + creator = (CreatorFct)getSymbol(creatorName); + + if (!creator) + throw std::runtime_error("unknown ospray scene graph node '"+type+"'"); + + nodeRegistry[type] = creator; + } else { + creator = it->second; + } + + std::shared_ptr newNode(creator()); + newNode->setName(name); + newNode->setType(type); + newNode->setFlags(flags); + newNode->setDocumentation(documentation); + + if (var.valid()) newNode->setValue(var); + + return newNode; } - } -} + OSP_REGISTER_SG_NODE(Node); + OSP_REGISTER_SG_NODE_NAME(NodeParam, vec3f); + OSP_REGISTER_SG_NODE_NAME(NodeParam, vec2f); + OSP_REGISTER_SG_NODE_NAME(NodeParam, vec2i); + OSP_REGISTER_SG_NODE_NAME(NodeParam, float); + OSP_REGISTER_SG_NODE_NAME(NodeParam, int); + OSP_REGISTER_SG_NODE_NAME(NodeParam, bool); + OSP_REGISTER_SG_NODE_NAME(NodeParam, string); + OSP_REGISTER_SG_NODE_NAME(NodeParam, box3f); + OSP_REGISTER_SG_NODE_NAME(NodeParam, OSPObject); + + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/common/Node.h b/apps/common/sg/common/Node.h index 3e00680247..043ab108a9 100644 --- a/apps/common/sg/common/Node.h +++ b/apps/common/sg/common/Node.h @@ -18,6 +18,7 @@ #include "sg/common/TimeStamp.h" #include "sg/common/Serialization.h" +#include "sg/common/RenderContext.h" #include "sg/common/RuntimeError.h" // stl #include @@ -25,12 +26,16 @@ // xml #include "../../../common/xml/XML.h" // ospcommon +#include "ospcommon/utility/Any.h" #include "ospcommon/vec.h" -namespace ospray { +#include +namespace ospray { namespace sg { + using SGVar = ospcommon::utility::Any; + /*! forward decl of entity that nodes can write to when writing XML files */ struct XMLWriter; @@ -38,66 +43,35 @@ namespace ospray { automatically defines all accessor functions for said member. */ #define SG_NODE_DECLARE_MEMBER(type,name,capName) \ - public: \ - inline type get##capName() const { return name; } \ public: \ - inline void set##capName(const type &name) { \ - this->name = name; \ - this->lastModified = TimeStamp::now(); \ - }; \ + inline type get##capName() const { return name; } \ + inline void set##capName(const type &name) { \ + this->name = name; \ + this->properties.lastModified = TimeStamp(); \ + }; \ protected: \ - type name; \ - - /*! \brief a parameter to a node (is not in itself a node). - - \note This is only the abstract base class, actual instantiations are - the in the 'ParamT' template. */ - struct Param { - /*! constructor. the passed name alwasys remains constant */ - Param(const std::string &name) : name(name) {}; - /*! return name of this parameter. the value is in the derived class */ - inline const std::string getName() const { return name; } - virtual void write(XMLWriter &) { NOTIMPLEMENTED; }; - /*! returns the ospray data type that this node corresponds to */ - virtual OSPDataType getOSPDataType() const = 0; - protected: - /*! name of this node */ - const std::string name; - }; + type name \ - /*! \brief a concrete parameter to a scene graph node */ - template - struct ParamT : public sg::Param { - ParamT(const std::string &name, const T &t) : Param(name), value(t) {}; - virtual OSPDataType getOSPDataType() const override; - virtual void write(XMLWriter &) { NOTIMPLEMENTED; }; - T value; + enum NodeFlags + { + none = 0 << 0, + required = 1 << 1, //! this node is required to be valid by its parent + valid_min_max = 1 << 2, //! validity determined by minmax range + valid_whitelist = 1 << 3, //! validity determined by whitelist + valid_blacklist = 1 << 4, //! validity determined by blacklist + gui_slider = 1 << 5, + gui_color = 1<<6, + gui_combo = 1<<7 }; - /*! class that encapsulate all the context/state required for - rendering any object. note we INTENTIONALLY do not use - shared_ptrs here because certain nodes want to set these values - to 'this', which isn't valid for shared_ptrs */ - struct RenderContext { - std::shared_ptr world; //!< world we're rendering into - std::shared_ptr integrator; //!< integrator used to create materials etc - const affine3f xfm; //!< affine geometry transform matrix - - //! create a new context - RenderContext() : xfm(one) {}; - - //! create a new context with new transformation matrix - RenderContext(const RenderContext &other, const affine3f &newXfm) - : world(other.world), integrator(other.integrator), xfm(newXfm) - {} - }; + // Base Node class definition ///////////////////////////////////////////// /*! \brief base node of all scene graph nodes */ - struct Node : public std::enable_shared_from_this + struct OSPSG_INTERFACE Node : public std::enable_shared_from_this { - Node() : lastModified(1), lastCommitted(0) {}; + Node(); - virtual std::string toString() const = 0; + virtual std::string toString() const; //! \brief Initialize this node's value from given XML node /*! @@ -115,31 +89,35 @@ namespace ospray { existant) that contains additional binary data that the xml node fields may point into */ - virtual void setFromXML(const xml::Node &node, + virtual void setFromXML(const xml::Node &node, const unsigned char *binBasePtr); - //! just for convenience; add a typed 'setParam' function - template - inline void setParam(const std::string &name, const T &t) - { params[name] = std::make_shared>(name,t); } - - // /*! query given parameter */ - // std::shared_ptr getParam(const std::string &name) const; - - template - inline void for_each_param(const Lambda &functor) - { for (auto it=params.begin(); it!=params.end(); ++it) functor(it->second); } - - /*! serialize the scene graph - add object to the serialization, but don't do anything else to the node(s) */ virtual void serialize(sg::Serialization::State &state); - /*! \brief 'render' the object for the first time */ - virtual void render(RenderContext &ctx) {}; + // Properties /////////////////////////////////////////////////////////// + + std::string name() const; + std::string type() const; + SGVar min() const; + SGVar max() const; + NodeFlags flags() const; + std::string documentation() const; + + void setName(const std::string &v); + void setType(const std::string &v); + void setMinMax(const SGVar& minv, const SGVar& maxv); + void setFlags(NodeFlags f); + void setFlags(int f); + void setDocumentation(const std::string &s); + void setWhiteList(const std::vector &values); + void setBlackList(const std::vector &values); + + bool isValid() const; - /*! \brief 'commit' updates */ - virtual void commit() {}; + virtual bool computeValid(); + virtual bool computeValidMinMax(); /*! \brief return bounding box in world coordinates. @@ -147,32 +125,318 @@ namespace ospray { camera motion, setting default camera position, etc. Nodes for which that does not apply can simpy return box3f(empty) */ - virtual box3f getBounds() { return box3f(empty); }; + virtual box3f bounds() const; + + // Node stored value (data) interface /////////////////////////////////// + + //! get the value of the node, whithout template conversion + SGVar value(); + + //! returns the value of the node in the desired type + template + T& valueAs(); + + //! returns the value of the node in the desired type + template + const T& valueAs() const; + + //! set the value of the node. Requires strict typecast + void setValue(SGVar val); + + // Update detection interface /////////////////////////////////////////// + + TimeStamp lastModified() const; + TimeStamp lastCommitted() const; + TimeStamp childrenLastModified() const; + + void markAsCommitted(); + virtual void markAsModified(); + virtual void setChildrenModified(TimeStamp t); - //! return when this node was last modified - inline TimeStamp getLastModified() const { return lastModified; }; + // Parent-child structual interface ///////////////////////////////////// - //! return when this node was last committed - inline TimeStamp getLastCommitted() const { return lastCommitted; }; + // Children // + + bool hasChild(const std::string &name) const; + + /*! return named child node. */ + Node& child(const std::string &name) const; + Node& operator[] (const std::string &c) const; + + Node& childRecursive(const std::string &name); + + std::vector> children() const; + + std::map>& childrenMap(); + + //! add node as child of this one + void add(std::shared_ptr node); + Node& operator+=(std::shared_ptr node); + + //! just for convenience; add a typed 'setParam' function + template + void createChildWithValue(const std::string &name, const std::string& type, const T &t); + + Node& createChild(std::string name, + std::string type = "Node", + SGVar var = SGVar(), + int flags = sg::NodeFlags::none, + std::string documentation=""); + + void setChild(const std::string &name, + const std::shared_ptr &node); + + // Parent // + + bool hasParent() const; + + Node& parent() const; + void setParent(Node &p); + void setParent(const std::shared_ptr& p); + + // Traversal interface ////////////////////////////////////////////////// + + //! traverse this node and childrend with given operation, such as + // print,commit,render or custom operations + virtual void traverse(RenderContext &ctx, const std::string& operation); + + //! Helper overload to use a default constructed RenderContext for root + // level traversal + void traverse(const std::string& operation); + + //! called before traversing children + virtual void preTraverse(RenderContext &ctx, + const std::string& operation, bool& traverseChildren); + + //! called after traversing children + virtual void postTraverse(RenderContext &ctx, + const std::string& operation); + + //! called before committing children during traversal + virtual void preCommit(RenderContext &ctx); + + //! called after committing children during traversal + virtual void postCommit(RenderContext &ctx); - std::string name; protected: - TimeStamp lastModified; - TimeStamp lastCommitted; - std::map> params; + + struct + { + std::string name; + std::string type; + std::vector minmax; + std::vector whitelist; + std::vector blacklist; + std::map> children; + SGVar value; + TimeStamp lastModified; + TimeStamp childrenMTime; + TimeStamp lastCommitted; + TimeStamp lastVerified; + Node* parent {nullptr}; + NodeFlags flags; + bool valid {false}; + std::string documentation; + } properties; + + // NOTE(jda) - The mutex is 'mutable' because const methods still need + // to be able to lock the mutex + mutable std::mutex mutex; }; - /*! read a given scene graph node from its correspondoing xml node represenation */ - sg::Node *parseNode(xml::Node *node); + // Inlined Node definitions /////////////////////////////////////////////// + //! just for convenience; add a typed 'setParam' function + template + inline void Node::createChildWithValue(const std::string &name, const std::string& type, const T &t) + { + if (hasChild(name)) + child(name).setValue(t); + else { + auto node = std::make_shared(); + node->setType(type); + node->setValue(t); + node->setName(name); + add(node); + } + } - // list of all named nodes - for now use this as a global - // variable, but eventually we'll need tofind a better way for - // storing this ... maybe in the world!? - extern std::map > namedNodes; - std::shared_ptr findNamedNode(const std::string &name); - void registerNamedNode(const std::string &name, const std::shared_ptr &node); + template + inline T& Node::valueAs() + { + std::lock_guard lock{mutex}; + return properties.value.get(); + } + + template + inline const T& Node::valueAs() const + { + std::lock_guard lock{mutex}; + return properties.value.get(); + } + + OSPSG_INTERFACE std::shared_ptr createNode(std::string name, + std::string type = "Node", + SGVar var = SGVar(), + int flags = sg::NodeFlags::none, + std::string documentation=""); + + // Helper functions /////////////////////////////////////////////////////// + + // Compare // + + template + inline bool compare(const SGVar& min, const SGVar& max, const SGVar& value) + { + if (value.get() < min.get() || value.get() > max.get()) + return false; + return true; + } + + template <> + inline bool compare(const SGVar& min, + const SGVar& max, + const SGVar& value) + { + const vec2f &v1 = min.get(); + const vec2f &v2 = max.get(); + const vec2f &v = value.get(); + return !(v1.x > v.x || v2.x < v.x || + v1.y > v.y || v2.y < v.y); + } + + template <> + inline bool compare(const SGVar& min, + const SGVar& max, + const SGVar& value) + { + const vec2i &v1 = min.get(); + const vec2i &v2 = max.get(); + const vec2i &v = value.get(); + return !(v1.x > v.x || v2.x < v.x || + v1.y > v.y || v2.y < v.y); + } + + template <> + inline bool compare(const SGVar& min, + const SGVar& max, + const SGVar& value) + { + const vec3f &v1 = min.get(); + const vec3f &v2 = max.get(); + const vec3f &v = value.get(); + return !(v1.x > v.x || v2.x < v.x || + v1.y > v.y || v2.y < v.y || + v1.z > v.z || v2.z < v.z); + } + + template <> + inline bool compare(const SGVar& min, + const SGVar& max, + const SGVar& value) + { + return true;// NOTE(jda) - this is wrong, was incorrect before refactoring + } + + // Commit // + + template + inline void commitNodeValue(Node &) + { + } + template <> + inline void commitNodeValue(Node &n) + { + ospSet1f(n.parent().valueAs(), + n.name().c_str(), n.valueAs()); + } + + template <> + inline void commitNodeValue(Node &n) + { + ospSet1i(n.parent().valueAs(), + n.name().c_str(), n.valueAs()); + } + + template <> + inline void commitNodeValue(Node &n) + { + ospSet1i(n.parent().valueAs(), + n.name().c_str(), n.valueAs()); + } + + template <> + inline void commitNodeValue(Node &n) + { + ospSet3fv(n.parent().valueAs(), + n.name().c_str(), &n.valueAs().x); + } + + + template <> + inline void commitNodeValue(Node &n) + { + ospSet3fv(n.parent().valueAs(), + n.name().c_str(), &n.valueAs().x); + } + + // Helper parameter node wrapper ////////////////////////////////////////// + + template + struct NodeParam : public Node + { + NodeParam() : Node() { setValue(T()); } + virtual void postCommit(RenderContext &ctx) override + { + if (hasParent()) { + //TODO: generalize to other types of ManagedObject + + //NOTE(jda) - OMG the syntax for the 'if' is strange... + if (parent().value().template is()) + commitNodeValue(*this); + } + } + + virtual bool computeValidMinMax() override + { + if (properties.minmax.size() < 2 || + !(flags() & NodeFlags::valid_min_max)) + return true; + + return compare(min(), max(), value()); + } + }; + + // Base Node for all renderables ////////////////////////////////////////// + + //! a Node with bounds and a render operation + struct OSPSG_INTERFACE Renderable : public Node + { + Renderable() { createChild("bounds", "box3f"); } + virtual ~Renderable() = default; + + virtual box3f bounds() const override { return child("bounds").valueAs(); } + virtual box3f computeBounds() const + { + box3f cbounds = empty; + for (const auto &child : properties.children) + { + auto tbounds = child.second->bounds(); + cbounds.extend(tbounds); + } + return cbounds; + } + // virtual box3f extendBounds(box3f b) { bbox.extend(b); return bbox; } + virtual void preTraverse(RenderContext &ctx, + const std::string& operation, bool& traverseChildren) override; + virtual void postTraverse(RenderContext &ctx, + const std::string& operation) override; + virtual void postCommit(RenderContext &ctx) override { + child("bounds").setValue(computeBounds()); } + virtual void preRender(RenderContext &ctx) {} + virtual void postRender(RenderContext &ctx) {} + }; /*! \brief registers a internal ospray:: renderer under the externally accessible name "external_name" @@ -184,13 +448,22 @@ namespace ospray { of this renderer. */ #define OSP_REGISTER_SG_NODE(InternalClassName) \ - extern "C" std::shared_ptr \ + extern "C" OSPSG_INTERFACE ospray::sg::Node* \ ospray_create_sg_node__##InternalClassName() \ { \ - return std::make_shared(); \ - } + return new ospray::sg::InternalClassName; \ + } \ + /* Extra declaration to avoid "extra ;" pedantic warnings */ \ + ospray::sg::Node* ospray_create_sg_node__##InternalClassName() + +#define OSP_REGISTER_SG_NODE_NAME(InternalClassName,Name) \ + extern "C" OSPSG_INTERFACE ospray::sg::Node* \ + ospray_create_sg_node__##Name() \ + { \ + return new ospray::sg::InternalClassName; \ + } \ + /* Extra declaration to avoid "extra ;" pedantic warnings */ \ + ospray::sg::Node* ospray_create_sg_node__##Name() } // ::ospray::sg } // ::ospray - - diff --git a/apps/common/sg/common/RenderContext.h b/apps/common/sg/common/RenderContext.h new file mode 100644 index 0000000000..237638e8f4 --- /dev/null +++ b/apps/common/sg/common/RenderContext.h @@ -0,0 +1,75 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "sg/common/Common.h" +#include "sg/common/TimeStamp.h" + +namespace ospray { + namespace sg { + + /*! class that encapsulate all the context/state required for + rendering any object. note we INTENTIONALLY do not use + shared_ptrs here because certain nodes want to set these values + to 'this', which isn't valid for shared_ptrs */ + struct OSPSG_INTERFACE RenderContext + { + RenderContext() = default; + + //! create a new context with new transformation matrix + RenderContext(const RenderContext &other, const affine3f &newXfm); + + TimeStamp MTime(); + TimeStamp childMTime(); + + // Data members // + + std::shared_ptr world; //!< world we're rendering into + OSPModel currentOSPModel{nullptr}; + affine3f currentTransform{ospcommon::one}; + + OSPRenderer ospRenderer {nullptr}; + int level {0}; + + + TimeStamp _MTime; + TimeStamp _childMTime; + }; + + // Inlined RenderContext definitions ////////////////////////////////////// + + inline RenderContext::RenderContext(const RenderContext &other, + const affine3f &newXfm) + : world(other.world), + currentOSPModel(nullptr), + currentTransform(newXfm), + ospRenderer(nullptr), + level(0) + {} + + inline TimeStamp RenderContext::MTime() + { + return _MTime; + } + + inline TimeStamp RenderContext::childMTime() + { + return _childMTime; + } + + }// ::ospray::sg +}// ::ospray diff --git a/apps/common/sg/common/RuntimeError.h b/apps/common/sg/common/RuntimeError.h index a27bbe337d..8119624623 100644 --- a/apps/common/sg/common/RuntimeError.h +++ b/apps/common/sg/common/RuntimeError.h @@ -29,7 +29,7 @@ namespace ospray { \detailed we derive from std::runtime_error to allow apps to just catch those errors if they're not explicitly interested in where it came from */ - struct RuntimeError : public std::runtime_error { + struct OSPSG_INTERFACE RuntimeError : public std::runtime_error { RuntimeError(const std::string &err) : std::runtime_error("#osp:sg: "+err) {} }; diff --git a/apps/common/sg/common/Serialization.cpp b/apps/common/sg/common/Serialization.cpp index ea8c661f2b..26e146a5a8 100644 --- a/apps/common/sg/common/Serialization.cpp +++ b/apps/common/sg/common/Serialization.cpp @@ -20,7 +20,8 @@ namespace ospray { namespace sg { - void Serialization::serialize(std::shared_ptr world, Serialization::Mode mode) + void Serialization::serialize(std::shared_ptr world, + Serialization::Mode mode) { clear(); Serialization::State state; diff --git a/apps/common/sg/common/Serialization.h b/apps/common/sg/common/Serialization.h index 4df6460aae..6f396bfc65 100644 --- a/apps/common/sg/common/Serialization.h +++ b/apps/common/sg/common/Serialization.h @@ -24,11 +24,12 @@ namespace ospray { namespace sg { - typedef ospcommon::AffineSpace3f affine3f; /*! class one can use to serialize all nodes in the scene graph */ - struct Serialization { - typedef enum { + struct OSPSG_INTERFACE Serialization + { + enum Mode + { /*! when serializing the scene graph, traverse through all instances and record each and every occurrence of any object (ie, an instanced object will appear multiple times in the @@ -38,17 +39,17 @@ namespace ospray { object only ONCE, and list all its instantiations in its instantiation vector */ DONT_FOLLOW_INSTANCES - } Mode; + }; - struct Instantiation { + struct OSPSG_INTERFACE Instantiation + { std::shared_ptr parentWorld; - affine3f xfm; - - Instantiation() : xfm(one) {} + affine3f xfm {one}; }; /*! describes one object that we encountered */ - struct Object { + struct OSPSG_INTERFACE Object + { /*! the node itself - this is intentionally NOT a shared_ptr since nodes call this with 'this', for which we have no shared_ptr info */ @@ -58,23 +59,24 @@ namespace ospray { NULL if object isn't instanced (or only instanced once) */ std::shared_ptr instantiation; - Object(const std::shared_ptr &node, //const sg::Node *node=nullptr, + Object(const std::shared_ptr &node, std::shared_ptr inst) - : node(node), instantiation(inst) - {}; + : node(node), instantiation(inst) {} }; /*! the node that maintains all the traversal state when traversing the scene graph */ - struct State { + struct OSPSG_INTERFACE State + { std::shared_ptr instantiation; Serialization *serialization; }; - void serialize(std::shared_ptr world, Serialization::Mode mode); + void serialize(std::shared_ptr world, + Serialization::Mode mode); /*! clear all old objects */ - void clear() { object.clear(); } + void clear() { object.clear(); } size_t size() const { return object.size(); } diff --git a/apps/common/sg/common/Texture2D.cpp b/apps/common/sg/common/Texture2D.cpp index 93312cfdaa..5528181e0f 100644 --- a/apps/common/sg/common/Texture2D.cpp +++ b/apps/common/sg/common/Texture2D.cpp @@ -14,29 +14,41 @@ // limitations under the License. // // ======================================================================== // +#define STB_IMAGE_IMPLEMENTATION +#include "miniSG/stb_image.h" + +#ifdef USE_IMAGEMAGICK +#define MAGICKCORE_QUANTUM_DEPTH 16 +#define MAGICKCORE_HDRI_ENABLE 1 +# include +using namespace Magick; +# ifndef MaxRGB +# define MaxRGB QuantumRange +# endif +#endif + #include "sg/common/Texture2D.h" +#include "ospray/common/OSPCommon.h" namespace ospray { namespace sg { - /*! constructor */ - Texture2D::Texture2D() - : size(-1), - texelType(OSP_TEXTURE_FORMAT_INVALID), - ospTexture(NULL) - { - } - - Texture2D::~Texture2D() + std::string Texture2D::toString() const { + return "ospray::viewer::sg::Texture2D"; } - - //! \brief load texture from given file. + + //! \brief load texture from given file. /*! \detailed if file does not exist, or cannot be loaded for some reason, return NULL. Multiple loads from the same file will return the *same* texture object */ - std::shared_ptr Texture2D::load(const FileName &fileName, const bool prefereLinear) + std::shared_ptr Texture2D::load(const FileName &fileNameAbs, + const bool preferLinear) { + FileName fileName = fileNameAbs; + std::string fileNameBase = fileNameAbs; + std::string path = fileNameAbs.path(); + /* WARNING: this cache means that every texture ever loaded will forever keep at least one refcount - ie, no texture will ever auto-die!!! (to fix this we'd have to add a dedicated @@ -44,109 +56,410 @@ namespace ospray { a CachedTextureLoader objec that each parser creates and destroys) */ static std::map > textureCache; - if (textureCache.find(fileName.str()) != textureCache.end()) + if (textureCache.find(fileName.str()) != textureCache.end()) return textureCache[fileName.str()]; - std::shared_ptr tex; + std::shared_ptr tex = std::static_pointer_cast( + createNode(fileName.name(),"Texture2D")); const std::string ext = fileName.ext(); -#ifdef OSPRAY_SG_LIBPNG - if (ext == "png") { - } -#endif - if (ext == "ppm") { try { + #ifdef USE_IMAGEMAGICK + if (1) + { + Magick::Image image(fileName.str().c_str()); + tex->size.x = image.columns(); + tex->size.y = image.rows(); + tex->channels = image.matte() ? 4 : 3; + const bool hdr = image.depth() > 8; + tex->depth = hdr ? 4 : 1; + tex->preferLinear = preferLinear; + float rcpMaxRGB = 1.0f/float(MaxRGB); + const Magick::PixelPacket* pixels = image.getConstPixels(0,0,tex->size.x,tex->size.y); + if (!pixels) { + std::cerr << "#osp:minisg: failed to load texture '"+fileName.str()+"'" << std::endl; + } else { + tex->data = new unsigned char[tex->size.x*tex->size.y*tex->channels*tex->depth]; + // convert pixels and flip image (because OSPRay's textures have the origin at the lower left corner) + for (size_t y=0; ysize.y; y++) { + for (size_t x=0; xsize.x; x++) { + const Magick::PixelPacket &pixel = pixels[y*tex->size.x+x]; + if (hdr) { + float *dst = &((float*)tex->data)[(x+(tex->size.y-1-y)*tex->size.x)*tex->channels]; + *dst++ = pixel.red * rcpMaxRGB; + *dst++ = pixel.green * rcpMaxRGB; + *dst++ = pixel.blue * rcpMaxRGB; + if (tex->channels == 4) + *dst++ = pixel.opacity * rcpMaxRGB; + } else { + unsigned char *dst = &((unsigned char*)tex->data)[(x+(tex->size.y-1-y)*tex->size.x)*tex->channels]; + *dst++ = pixel.red; + *dst++ = pixel.green; + *dst++ = pixel.blue; + if (tex->channels == 4) + *dst++ = pixel.opacity; + } + } + } + } + } else + #endif + { int rc, peekchar; // open file FILE *file = fopen(fileName.str().c_str(),"rb"); + if (!file) + { + if (fileNameBase.size() > 0) { + if (fileNameBase.substr(0,1) == "/") {// Absolute path. + fileName = fileNameBase; + path = ""; + } + } + } const int LINESZ=10000; - char lineBuf[LINESZ+1]; + char lineBuf[LINESZ+1]; + + if (!file) + throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); - if (!file) - throw std::runtime_error("#osp:sg: could not open texture file '"+fileName.str()+"'."); - // read format specifier: int format=0; - if (fscanf(file,"P%i\n",&format) != 1) - throw std::runtime_error("#osp:sg: could not parse PPM type"); - if (format != 6) - throw std::runtime_error("#osp:sg: can currently load only binary P6 subformats for PPM texture files. " + rc = fscanf(file,"P%i\n",&format); + if (format != 6) + throw std::runtime_error("#osp:miniSG: can currently load only binary P6 subformats for PPM texture files. " "Please report this bug at ospray.github.io."); // skip all comment lines peekchar = getc(file); while (peekchar == '#') { - if (!fgets(lineBuf,LINESZ,file)) - throw std::runtime_error("could not fgets"); + auto tmp = fgets(lineBuf,LINESZ,file); + (void)tmp; peekchar = getc(file); } ungetc(peekchar,file); - + // read width and height from first non-comment line int width=-1,height=-1; rc = fscanf(file,"%i %i\n",&width,&height); - if (rc != 2) - throw std::runtime_error("#osp:sg: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " + if (rc != 2) + throw std::runtime_error("#osp:miniSG: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " "Please report this bug at ospray.github.io, and include named file to reproduce the error."); - + // skip all comment lines peekchar = getc(file); while (peekchar == '#') { - if (!fgets(lineBuf,LINESZ,file)) - throw std::runtime_error("could not fgets"); + auto tmp = fgets(lineBuf,LINESZ,file); + (void)tmp; peekchar = getc(file); } ungetc(peekchar,file); - + // read maxval int maxVal=-1; rc = fscanf(file,"%i",&maxVal); - peekchar = getc(file); + peekchar = getc(file); - if (rc != 1) - throw std::runtime_error("#osp:sg: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " + if (rc != 1) + throw std::runtime_error("#osp:miniSG: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " "Please report this bug at ospray.github.io, and include named file to reproduce the error."); if (maxVal != 255) - throw std::runtime_error("#osp:sg: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." + throw std::runtime_error("#osp:miniSG: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." "Please report this bug at ospray.github.io, and include named file to reproduce the error."); - - tex = std::make_shared(); - tex->size = vec2i(width,height); - tex->texelType = prefereLinear ? OSP_TEXTURE_RGB8 : OSP_TEXTURE_SRGB; - unsigned char *texel = new unsigned char[width*height*3]; - if (!fread(texel,width*height*3,1,file)) { - delete[] texel; - throw std::runtime_error("could not fread"); - } + + // tex = new Texture2D; + tex->size.x = width; + tex->size.y = height; + tex->channels = 3; + tex->depth = 1; + tex->preferLinear = preferLinear; + tex->data = new unsigned char[width*height*3]; + rc = fread(tex->data,width*height*3,1,file); // flip in y, because OSPRay's textures have the origin at the lower left corner - for (int y=0; y < height/2; y++) - for (int x=0; x < width*3; x++) - std::swap(texel[y*width*3+x], texel[(height-1-y)*width*3+x]); + unsigned char *texels = (unsigned char *)tex->data; + for (int y = 0; y < height/2; y++) + for (int x = 0; x < width*3; x++) + std::swap(texels[y*width*3+x], texels[(height-1-y)*width*3+x]); + } + } catch(std::runtime_error e) { + std::cerr << e.what() << std::endl; + } + } else if (ext == "pfm") { + try { + // Note: the PFM file specification does not support comments thus we don't skip any + // http://netpbm.sourceforge.net/doc/pfm.html + int rc = 0; + FILE *file = fopen(fileName.str().c_str(), "rb"); + if (!file) { + throw std::runtime_error("#osp:miniSG: could not open texture file '"+fileName.str()+"'."); + } + // read format specifier: + // PF: color floating point image + // Pf: grayscae floating point image + char format[2] = {0}; + if (fscanf(file, "%c%c\n", &format[0], &format[1]) != 2) + throw std::runtime_error("could not fscanf"); + + if (format[0] != 'P' || (format[1] != 'F' && format[1] != 'f')){ + throw std::runtime_error("#osp:miniSG: invalid pfm texture file, header is not PF or Pf"); + } + int numChannels = 3; + if (format[1] == 'f') { + numChannels = 1; + } + + // read width and height + int width = -1; + int height = -1; + rc = fscanf(file, "%i %i\n", &width, &height); + if (rc != 2) { + throw std::runtime_error("#osp:miniSG: could not parse width and height in PF PFM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + + // read scale factor/endiannes + float scaleEndian = 0.0; + rc = fscanf(file, "%f\n", &scaleEndian); + + if (rc != 1) { + throw std::runtime_error("#osp:miniSG: could not parse scale factor/endianness in PF PFM file '"+fileName.str()+"'. " + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + if (scaleEndian == 0.0) { + throw std::runtime_error("#osp:miniSG: scale factor/endianness in PF PFM file can not be 0"); + } + if (scaleEndian > 0.0) { + throw std::runtime_error("#osp:miniSG: could not parse PF PFM file '"+fileName.str()+"': currently supporting only little endian formats" + "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + } + float scaleFactor = std::abs(scaleEndian); - // create data array that now 'owns' the texels - tex->texelData = std::make_shared(texel,width*height*3,true); + tex->size.x = width; + tex->size.y = height; + tex->channels = numChannels; + tex->depth = sizeof(float); + tex->preferLinear = preferLinear; + tex->data = new float[width * height * numChannels]; + if (fread(tex->data, sizeof(float), width * height * numChannels, file) + != width * height * numChannels) + throw std::runtime_error("could not fread"); + // flip in y, because OSPRay's textures have the origin at the lower left corner + float *texels = (float *)tex->data; + for (int y = 0; y < height / 2; ++y) { + for (int x = 0; x < width * numChannels; ++x) { + // Scale the pixels by the scale factor + texels[y * width * numChannels + x] = texels[y * width * numChannels + x] * scaleFactor; + texels[(height - 1 - y) * width * numChannels + x] = texels[(height - 1 - y) * width * numChannels + x] * scaleFactor; + std::swap(texels[y * width * numChannels + x], texels[(height - 1 - y) * width * numChannels + x]); + } + } } catch(std::runtime_error e) { std::cerr << e.what() << std::endl; } - } + } + else { +#ifdef USE_IMAGEMAGICK + Magick::Image image(fileName.str().c_str()); + tex->size.x = image.columns(); + tex->size.y = image.rows(); + tex->channels = image.matte() ? 4 : 3; + const bool hdr = image.depth() > 8; + tex->depth = hdr ? 4 : 1; + tex->preferLinear = preferLinear; + float rcpMaxRGB = 1.0f/float(MaxRGB); + const Magick::PixelPacket* pixels = image.getConstPixels(0,0,tex->size.x,tex->size.y); + if (!pixels) { + std::cerr << "#osp:minisg: failed to load texture '"+fileName.str()+"'" << std::endl; + } else { + tex->data = new unsigned char[tex->size.x*tex->size.y*tex->channels*tex->depth]; + // convert pixels and flip image (because OSPRay's textures have the origin at the lower left corner) + for (size_t y=0; ysize.y; y++) { + for (size_t x=0; xsize.x; x++) { + const Magick::PixelPacket &pixel = pixels[y*tex->size.x+x]; + if (hdr) { + float *dst = &((float*)tex->data)[(x+(tex->size.y-1-y)*tex->size.x)*tex->channels]; + *dst++ = pixel.red * rcpMaxRGB; + *dst++ = pixel.green * rcpMaxRGB; + *dst++ = pixel.blue * rcpMaxRGB; + if (tex->channels == 4) + *dst++ = pixel.opacity * rcpMaxRGB; + } else { + unsigned char *dst = &((unsigned char*)tex->data)[(x+(tex->size.y-1-y)*tex->size.x)*tex->channels]; + *dst++ = pixel.red; + *dst++ = pixel.green; + *dst++ = pixel.blue; + if (tex->channels == 4) + *dst++ = pixel.opacity; + } + } + } + } +#else + int width,height,n; + const bool hdr = stbi_is_hdr(fileName.str().c_str()); + unsigned char* pixels = nullptr; + if (hdr) + pixels = (unsigned char*)stbi_loadf(fileName.str().c_str(), &width, &height, &n, 0); + else + pixels = stbi_load(fileName.str().c_str(),&width,&height,&n,0); + tex->size.x = width; + tex->size.y = height; + tex->channels = n; + tex->depth = hdr ? 4 : 1; + tex->preferLinear = preferLinear; + unsigned char MaxRGB = 1; + float rcpMaxRGB = 1.0f/float(MaxRGB); + if (!pixels) { + std::cerr << "#osp:minisg: failed to load texture '"+fileName.str()+"'" << std::endl; + } else { + tex->data = new unsigned char[tex->size.x*tex->size.y*tex->channels*tex->depth]; + // convert pixels and flip image (because OSPRay's textures have the origin at the lower left corner) + for (size_t y=0; ysize.y; y++) { + for (size_t x=0; xsize.x; x++) { + const unsigned char *pixel = &pixels[(y*tex->size.x+x)*tex->channels]; + if (hdr) { + printf("loading hdr image\n"); + float *dst = &((float*)tex->data)[(x+(tex->size.y-1-y)*tex->size.x)*tex->channels]; + *dst++ = pixel[0] * rcpMaxRGB; + *dst++ = pixel[1] * rcpMaxRGB; + *dst++ = pixel[2] * rcpMaxRGB; + if (tex->channels == 4) + *dst++ = pixel[3] * rcpMaxRGB; + } else { + unsigned char *dst = &((unsigned char*)tex->data)[((tex->size.y-1-y)*tex->size.x+x)*tex->channels]; + *dst++ = pixel[0]; + *dst++ = pixel[1]; + *dst++ = pixel[2]; + if (tex->channels == 4) + *dst++ = pixel[3]; + } + } + } + } +#endif + } textureCache[fileName.str()] = tex; return tex; + + // if (ext == "ppm") { + // try { + // int rc, peekchar; + + // // open file + // FILE *file = fopen(fileName.str().c_str(),"rb"); + // const int LINESZ=10000; + // char lineBuf[LINESZ+1]; + + // if (!file) + // throw std::runtime_error("#osp:sg: could not open texture file '"+fileName.str()+"'."); + + // // read format specifier: + // int format=0; + // if (fscanf(file,"P%i\n",&format) != 1) + // throw std::runtime_error("#osp:sg: could not parse PPM type"); + // if (format != 6) + // throw std::runtime_error("#osp:sg: can currently load only binary P6 subformats for PPM texture files. " + // "Please report this bug at ospray.github.io."); + + // // skip all comment lines + // peekchar = getc(file); + // while (peekchar == '#') { + // if (!fgets(lineBuf,LINESZ,file)) + // throw std::runtime_error("could not fgets"); + // peekchar = getc(file); + // } ungetc(peekchar,file); + + // // read width and height from first non-comment line + // int width=-1,height=-1; + // rc = fscanf(file,"%i %i\n",&width,&height); + // if (rc != 2) + // throw std::runtime_error("#osp:sg: could not parse width and height in P6 PPM file '"+fileName.str()+"'. " + // "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + + // // skip all comment lines + // peekchar = getc(file); + // while (peekchar == '#') { + // if (!fgets(lineBuf,LINESZ,file)) + // throw std::runtime_error("could not fgets"); + // peekchar = getc(file); + // } ungetc(peekchar,file); + + // // read maxval + // int maxVal=-1; + // rc = fscanf(file,"%i",&maxVal); + // peekchar = getc(file); + + // if (rc != 1) + // throw std::runtime_error("#osp:sg: could not parse maxval in P6 PPM file '"+fileName.str()+"'. " + // "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + // if (maxVal != 255) + // throw std::runtime_error("#osp:sg: could not parse P6 PPM file '"+fileName.str()+"': currently supporting only maxVal=255 formats." + // "Please report this bug at ospray.github.io, and include named file to reproduce the error."); + + // // tex = std::make_shared(); + // tex->size = vec2i(width,height); + // tex->texelType = preferLinear ? OSP_TEXTURE_RGB8 : OSP_TEXTURE_SRGB; + // unsigned char *texel = new unsigned char[width*height*3]; + // if (!fread(texel,width*height*3,1,file)) { + // delete[] texel; + // throw std::runtime_error("could not fread"); + // } + // // flip in y, because OSPRay's textures have the origin at the lower left corner + // for (int y=0; y < height/2; y++) + // for (int x=0; x < width*3; x++) + // std::swap(texel[y*width*3+x], texel[(height-1-y)*width*3+x]); + + // // create data array that now 'owns' the texels + // tex->texelData = std::make_shared(texel,width*height*3,true); + // } catch(std::runtime_error e) { + // std::cerr << e.what() << std::endl; + // } + // } + // textureCache[fileName.str()] = tex; + // return tex; + } + + Texture2D::Texture2D() + : data(nullptr), texelData(nullptr), ospTexture2D(nullptr) + { + setValue((OSPObject)nullptr); } - - void Texture2D::render(RenderContext &ctx) + + void Texture2D::preCommit(RenderContext &ctx) { - if (ospTexture) + OSPTextureFormat type = OSP_TEXTURE_R8; + + if (depth == 1) { + if( channels == 1 ) type = OSP_TEXTURE_R8; + if( channels == 3 ) + type = preferLinear ? OSP_TEXTURE_RGB8 : OSP_TEXTURE_SRGB; + if( channels == 4 ) + type = preferLinear ? OSP_TEXTURE_RGBA8 : OSP_TEXTURE_SRGBA; + } else if (depth == 4) { + if( channels == 1 ) type = OSP_TEXTURE_R32F; + if( channels == 3 ) type = OSP_TEXTURE_RGB32F; + if( channels == 4 ) type = OSP_TEXTURE_RGBA32F; + } + + void* dat = data; + if (!dat && texelData) + dat = texelData->getBase(); + if (!dat) + { + ospTexture2D = nullptr; return; - - ospTexture = ospNewTexture2D((osp::vec2i&)size, - texelType, - texelData->getBase(), - 0); - if(!ospTexture) - std::cerr << "Warning: Could not create Texture2D\n"; - else - ospCommit(ospTexture); + } + + ospTexture2D = ospNewTexture2D( (osp::vec2i&)size, + type, + dat, + 0); + setValue((OSPObject)ospTexture2D); + ospCommit(ospTexture2D); } + OSP_REGISTER_SG_NODE(Texture2D); + + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/common/Texture2D.h b/apps/common/sg/common/Texture2D.h index a9344c0bb7..369274b7ae 100644 --- a/apps/common/sg/common/Texture2D.h +++ b/apps/common/sg/common/Texture2D.h @@ -24,34 +24,38 @@ namespace ospray { namespace sg { - using ospcommon::FileName; /*! \brief C++ wrapper for a 2D Texture */ - struct Texture2D : public Node { + struct OSPSG_INTERFACE Texture2D : public Node + { /*! constructor */ Texture2D(); - virtual ~Texture2D(); - + + virtual void preCommit(RenderContext &ctx) override; + /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::viewer::sg::Texture2D"; }; + std::string toString() const override; - //! \brief load texture from given file. + //! \brief load texture from given file. /*! \detailed if file does not exist, or cannot be loaded for some reason, return NULL. Multiple loads from the same file will return the *same* texture object */ - static std::shared_ptr load(const FileName &fileName, const bool prefereLinear = false); - virtual void render(RenderContext &ctx) override; + static std::shared_ptr load(const FileName &fileName, + const bool prefereLinear = false); //! texture size, in pixels - vec2i size; + vec2i size {-1}; + int channels{0}; + int depth{0}; + bool preferLinear{false}; + //! format of each texel - OSPTextureFormat texelType; - - OSPTexture2D ospTexture; + OSPTextureFormat texelType {OSP_TEXTURE_FORMAT_INVALID}; + + OSPTexture2D ospTexture {nullptr}; std::shared_ptr texelData; - // private: - // //! pixel data, in whatever format specified in 'texelType' - // unsigned char *texel; + void* data{nullptr}; + OSPTexture2D ospTexture2D; }; } // ::ospray::sg diff --git a/apps/volumeViewer/OpenGLAnnotationRenderer.h b/apps/common/sg/common/TimeStamp.cpp similarity index 77% rename from apps/volumeViewer/OpenGLAnnotationRenderer.h rename to apps/common/sg/common/TimeStamp.cpp index c38d0a1b0e..3b0f3c097f 100644 --- a/apps/volumeViewer/OpenGLAnnotationRenderer.h +++ b/apps/common/sg/common/TimeStamp.cpp @@ -14,26 +14,23 @@ // limitations under the License. // // ======================================================================== // -#pragma once +#include "sg/common/TimeStamp.h" -#include +namespace ospray { + namespace sg { -class VolumeViewer; + //! \brief the uint64_t that stores the time value + std::atomic TimeStamp::global {0}; -class OpenGLAnnotationRenderer : public QObject -{ -Q_OBJECT + TimeStamp::operator size_t() const + { + return value; + } -public: + size_t TimeStamp::nextValue() + { + return global++; + } - OpenGLAnnotationRenderer(VolumeViewer *volumeViewer) : volumeViewer(volumeViewer) { } - -public slots: - - void render(); - -protected: - - VolumeViewer *volumeViewer; - -}; + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/common/TimeStamp.h b/apps/common/sg/common/TimeStamp.h index f9a56a5a58..63d57f8c24 100644 --- a/apps/common/sg/common/TimeStamp.h +++ b/apps/common/sg/common/TimeStamp.h @@ -17,7 +17,8 @@ #pragma once #include "sg/common/Common.h" -#include "ospcommon/intrinsics.h" + +#include namespace ospray { namespace sg { @@ -25,22 +26,22 @@ namespace ospray { //! \brief Implements an abstraction of Time /*! Abstracts the concept of time to be used for time-stamping node's last 'lastupdated' and /lastmodified' time stamps */ - struct TimeStamp { - //! \brief constructor - TimeStamp(uint64_t t) : t(t) {}; - - //! \brief returns global time(stamp) at time of calling - static inline TimeStamp now() { return ospcommon::rdtsc(); } - - //! \brief Allows ot typecast to a uint64_t (so times can be compared) - inline operator uint64_t () const { return t; } + struct OSPSG_INTERFACE TimeStamp + { + TimeStamp() = default; + operator size_t() const; private: + + static size_t nextValue(); + + // Data members // + + size_t value {nextValue()}; + //! \brief the uint64_t that stores the time value - uint64_t t; + static std::atomic global; }; } // ::ospray::sg } // ::ospray - - diff --git a/apps/common/sg/common/Transform.cpp b/apps/common/sg/common/Transform.cpp index 285ea71916..24154763f5 100644 --- a/apps/common/sg/common/Transform.cpp +++ b/apps/common/sg/common/Transform.cpp @@ -20,41 +20,49 @@ namespace ospray { namespace sg { - /*! \brief returns a std::string with the c++ name of this class */ + + Transform::Transform() + : baseTransform(ospcommon::one), worldTransform(ospcommon::one) + { + createChild("bounds", "box3f"); + createChild("visible", "bool", true); + createChild("position", "vec3f"); + createChild("rotation", "vec3f", vec3f(0), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(-vec3f(2*3.15f), + vec3f(2*3.15f)); + createChild("scale", "vec3f", vec3f(1.f)); + } + + std::string Transform::toString() const - { return "ospray::sg::Transform"; } - - /*! \brief 'render' the object for the first time */ - void Transform::render(RenderContext &ctx) { - RenderContext newCtx(ctx,xfm); - if (node) node->render(newCtx); + return "ospray::sg::Transform"; } - - /*! \brief return bounding box in world coordinates. - - This function can be used by the viewer(s) for calibrating - camera motion, setting default camera position, etc. Nodes - for which that does not apply can simpy return - box3f(empty) */ - box3f Transform::getBounds() + + void Transform::preRender(RenderContext &ctx) { - assert(node); - const box3f nodeBounds = node->getBounds(); - const vec3f lo = nodeBounds.lower; - const vec3f hi = nodeBounds.upper; - box3f bounds = ospcommon::empty; - bounds.extend(xfmPoint(xfm,vec3f(lo.x,lo.y,lo.z))); - bounds.extend(xfmPoint(xfm,vec3f(hi.x,lo.y,lo.z))); - bounds.extend(xfmPoint(xfm,vec3f(lo.x,hi.y,lo.z))); - bounds.extend(xfmPoint(xfm,vec3f(hi.x,hi.y,lo.z))); - bounds.extend(xfmPoint(xfm,vec3f(lo.x,lo.y,hi.z))); - bounds.extend(xfmPoint(xfm,vec3f(hi.x,lo.y,hi.z))); - bounds.extend(xfmPoint(xfm,vec3f(lo.x,hi.y,hi.z))); - bounds.extend(xfmPoint(xfm,vec3f(hi.x,hi.y,hi.z))); - return bounds; + vec3f scale = child("scale").valueAs(); + vec3f rotation = child("rotation").valueAs(); + vec3f translation = child("position").valueAs(); + worldTransform = ctx.currentTransform*baseTransform*ospcommon::affine3f::translate(translation)* + ospcommon::affine3f::rotate(vec3f(1,0,0),rotation.x)* + ospcommon::affine3f::rotate(vec3f(0,1,0),rotation.y)* + ospcommon::affine3f::rotate(vec3f(0,0,1),rotation.z)* + ospcommon::affine3f::scale(scale); + + oldTransform = ctx.currentTransform; + ctx.currentTransform = worldTransform; } + void Transform::postRender(RenderContext &ctx) + { + ctx.currentTransform = oldTransform; + } + + OSP_REGISTER_SG_NODE(Transform); + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/common/Transform.h b/apps/common/sg/common/Transform.h index 4722a0d5ba..e00cde7cbc 100644 --- a/apps/common/sg/common/Transform.h +++ b/apps/common/sg/common/Transform.h @@ -23,31 +23,20 @@ namespace ospray { namespace sg { //! a transformation node - struct Transform : public sg::Node { - //! \brief constructor - Transform(const AffineSpace3f &xfm, const std::shared_ptr &node) - : Node(), xfm(xfm), node(node) - {} + struct OSPSG_INTERFACE Transform : public sg::Renderable + { + Transform(); + std::string toString() const override; - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; - - /*! \brief 'render' the object for the first time */ - virtual void render(RenderContext &ctx) override; - - /*! \brief return bounding box in world coordinates. - - This function can be used by the viewer(s) for calibrating - camera motion, setting default camera position, etc. Nodes - for which that does not apply can simpy return - box3f(empty) */ - virtual box3f getBounds() override; + virtual void preRender(RenderContext &ctx) override; + virtual void postRender(RenderContext &ctx) override; //! \brief the actual (affine) transformation matrix AffineSpace3f xfm; - - //! child node we're transforming - std::shared_ptr node; + ospcommon::affine3f cachedTransform; + ospcommon::affine3f baseTransform; + ospcommon::affine3f worldTransform; + ospcommon::affine3f oldTransform{ospcommon::one}; }; } // ::ospray::sg diff --git a/apps/common/sg/common/World.cpp b/apps/common/sg/common/World.cpp index 0ad4e9c919..d49d395097 100644 --- a/apps/common/sg/common/World.cpp +++ b/apps/common/sg/common/World.cpp @@ -19,37 +19,227 @@ namespace ospray { namespace sg { - box3f World::getBounds() - { - box3f bounds = empty; - for (auto node : nodes) - bounds.extend(node->getBounds()); - return bounds; + Model::Model() + : Renderable() + { + setValue((OSPObject)nullptr); } - //! serialize into given serialization state - void sg::World::serialize(sg::Serialization::State &state) + void Model::traverse(RenderContext &ctx, const std::string& operation) { - sg::Serialization::State savedState = state; + if (operation == "render") { - //state->serialization->object.push_back(Serialization::); - for (auto node: nodes) - node->serialize(state); + preRender(ctx); + postRender(ctx); } - state = savedState; + else + Node::traverse(ctx,operation); + } + + void Model::preCommit(RenderContext &ctx) + { + oldModel = ctx.currentOSPModel; + + if (ospModel) + ospRelease(ospModel); + ospModel = ospNewModel(); + setValue((OSPObject)ospModel); + ctx.currentOSPModel = ospModel; + } + + void Model::postCommit(RenderContext &ctx) + { + ctx.currentOSPModel = ospModel; + + //instancegroup caches render calls in commit. + for (auto child : properties.children) + child.second->traverse(ctx, "render"); + + ospCommit(ospModel); + + ctx.currentOSPModel = oldModel; + child("bounds").setValue(computeBounds()); + } + + World::World() + : Renderable() + { + } + + std::string World::toString() const + { + return "ospray::viewer::sg::World"; } - void World::render(RenderContext &ctx) + void World::traverse(RenderContext &ctx, const std::string& operation) { - ctx.world = std::dynamic_pointer_cast(shared_from_this());//this; + if (operation == "render") + { + preRender(ctx); + postRender(ctx); + } + else + Node::traverse(ctx,operation); + } + void World::preCommit(RenderContext &ctx) + { + oldWorld = ctx.world; + ctx.world = std::static_pointer_cast(shared_from_this()); if (ospModel) - throw std::runtime_error("World::ospModel alrady exists!?"); + ospRelease(ospModel); ospModel = ospNewModel(); - for (auto node: nodes) - node->render(ctx); ospCommit(ospModel); + setValue((OSPObject)ospModel); + oldModel = ctx.currentOSPModel; + ctx.currentOSPModel = ospModel; + } + + void World::postCommit(RenderContext &ctx) + { + //cache render operation + for (auto child : properties.children) + child.second->traverse(ctx, "render"); + ospCommit(ospModel); + ctx.world = oldWorld; + ctx.currentOSPModel = oldModel; + child("bounds").setValue(computeBounds()); + } + + void World::preRender(RenderContext &ctx) + { + // renders are cached in commit + } + + void World::postRender(RenderContext &ctx) + { + // renders are cached in commit + } + + Instance::Instance() + : World() + { + createChild("visible", "bool", true); + createChild("position", "vec3f"); + createChild("rotation", "vec3f", vec3f(0), + NodeFlags::required | + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(-vec3f(2*3.15f), + vec3f(2*3.15f)); + createChild("scale", "vec3f", vec3f(1.f)); + createChild("model", "Model"); + } + + /*! \brief return bounding box in world coordinates. + + This function can be used by the viewer(s) for calibrating + camera motion, setting default camera position, etc. Nodes + for which that does not apply can simpy return + box3f(empty) */ + box3f Instance::computeBounds() const + { + box3f cbounds = child("model").bounds(); + if (cbounds.empty()) + return cbounds; + const vec3f lo = cbounds.lower; + const vec3f hi = cbounds.upper; + box3f bounds = ospcommon::empty; + bounds.extend(xfmPoint(worldTransform,vec3f(lo.x,lo.y,lo.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(hi.x,lo.y,lo.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(lo.x,hi.y,lo.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(hi.x,hi.y,lo.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(lo.x,lo.y,hi.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(hi.x,lo.y,hi.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(lo.x,hi.y,hi.z))); + bounds.extend(xfmPoint(worldTransform,vec3f(hi.x,hi.y,hi.z))); + return bounds; + } + + void Instance::traverse(RenderContext &ctx, const std::string& operation) + { + if (instanced && operation == "render") + { + preRender(ctx); + postRender(ctx); + } + else + Node::traverse(ctx,operation); + } + + void Instance::preCommit(RenderContext &ctx) + { + if (instanced) { + instanceDirty=true; + + oldTransform = ctx.currentTransform; + + updateTransform(ctx); + cachedTransform=ctx.currentTransform; + ctx.currentTransform = worldTransform; + } + } + + void Instance::postCommit(RenderContext &ctx) + { + if (instanced) + ctx.currentTransform = oldTransform; + child("bounds").setValue(computeBounds()); + } + + void Instance::preRender(RenderContext &ctx) + { + if (instanced) { + oldTransform = ctx.currentTransform; + if (cachedTransform != ctx.currentTransform) + instanceDirty=true; + if (instanceDirty) + updateInstance(ctx); + ctx.currentTransform = worldTransform; + } + } + + void Instance::postRender(RenderContext &ctx) + { + if (instanced && child("visible").value() == true + && ctx.world && ctx.world->ospModel && ospInstance) + { + ospAddGeometry(ctx.world->ospModel,ospInstance); + } + ctx.currentTransform = oldTransform; + } + + void Instance::updateTransform(RenderContext &ctx) + { + vec3f scale = child("scale").valueAs(); + vec3f rotation = child("rotation").valueAs(); + vec3f translation = child("position").valueAs(); + worldTransform = ctx.currentTransform*baseTransform*ospcommon::affine3f::translate(translation)* + ospcommon::affine3f::rotate(vec3f(1,0,0),rotation.x)* + ospcommon::affine3f::rotate(vec3f(0,1,0),rotation.y)* + ospcommon::affine3f::rotate(vec3f(0,0,1),rotation.z)* + ospcommon::affine3f::scale(scale); + } + + void Instance::updateInstance(RenderContext &ctx) + { + updateTransform(ctx); + cachedTransform=ctx.currentTransform; + + if (ospInstance) + ospRelease(ospInstance); + ospInstance = nullptr; + + OSPModel model = (OSPModel)child("model").valueAs(); + if (model) + { + ospInstance = ospNewInstance(model,(osp::affine3f&)worldTransform); + ospCommit(ospInstance); + } + instanceDirty=false; } + OSP_REGISTER_SG_NODE(Model); + OSP_REGISTER_SG_NODE(World); + OSP_REGISTER_SG_NODE(Instance); } } diff --git a/apps/common/sg/common/World.h b/apps/common/sg/common/World.h index 44cc237cdd..1a9c55e52d 100644 --- a/apps/common/sg/common/World.h +++ b/apps/common/sg/common/World.h @@ -23,37 +23,85 @@ namespace ospray { namespace sg { + + struct OSPSG_INTERFACE Model : public sg::Renderable + { + Model(); + + //commit caches renders. It will render children during commit, and add + //cached rendered children during render call. + virtual void traverse(RenderContext &ctx, const std::string& operation) override; + virtual void preCommit(RenderContext &ctx) override; + virtual void postCommit(RenderContext &ctx) override; + + OSPModel ospModel {nullptr}; + std::shared_ptr oldWorld; + OSPModel oldModel; + }; + /*! a world node */ - struct World : public sg::Node { - World() : ospModel(NULL) {}; + struct OSPSG_INTERFACE World : public sg::Renderable + { + World(); + virtual ~World() = default; /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::viewer::sg::World"; } + virtual std::string toString() const override; + + /*! \brief return bounding box in world coordinates. + + This function can be used by the viewer(s) for calibrating + camera motion, setting default camera position, etc. Nodes + for which that does not apply can simpy return + box3f(embree::empty) */ + virtual void traverse(RenderContext &ctx, const std::string& operation) override; + virtual void preCommit(RenderContext &ctx) override; + virtual void postCommit(RenderContext &ctx) override; + virtual void preRender(RenderContext &ctx) override; + virtual void postRender(RenderContext &ctx) override; - //! serialize into given serialization state - virtual void serialize(sg::Serialization::State &serialization) override; + OSPModel ospModel {nullptr}; + std::shared_ptr oldWorld; + OSPModel oldModel; + }; - /*! 'render' the object for the first time */ - virtual void render(RenderContext &ctx) override; - /*! \brief return bounding box in world coordinates. + struct OSPSG_INTERFACE Instance : public sg::World + { + Instance(); + + /*! \brief return bounding box in world coordinates. This function can be used by the viewer(s) for calibrating camera motion, setting default camera position, etc. Nodes for which that does not apply can simpy return box3f(embree::empty) */ - virtual box3f getBounds() override; - - template - inline void add(const std::shared_ptr &t) { - assert(t); - std::shared_ptr asNode = std::dynamic_pointer_cast(t); - assert(asNode); - nodes.push_back(asNode); - } - - std::vector > nodes; - OSPModel ospModel; + virtual box3f computeBounds() const override; + + //Instance caches renders. It will render children during commit, and add + //cached rendered children during render call. + virtual void traverse(RenderContext &ctx, const std::string& operation) override; + virtual void preCommit(RenderContext &ctx) override; + virtual void postCommit(RenderContext &ctx) override; + virtual void preRender(RenderContext &ctx) override; + virtual void postRender(RenderContext &ctx) override; + + + OSPGeometry ospInstance {nullptr}; + //currently, nested instances do not appear to work in OSPRay. To get around this, + // instanced can be manually turned off for parent instancegroups. + bool instanced {true}; + ospcommon::affine3f baseTransform{ospcommon::one}; + + protected: + void updateInstance(RenderContext &ctx); + void updateTransform(RenderContext &ctx); + bool instanceDirty{true}; + ospcommon::affine3f cachedTransform{ospcommon::one}; + ospcommon::affine3f worldTransform{ospcommon::one}; + // computed from baseTransform*position*rotation*scale + ospcommon::affine3f oldTransform{ospcommon::one}; + }; } // ::ospray::sg diff --git a/apps/common/sg/geometry/Geometry.h b/apps/common/sg/geometry/Geometry.h index 0dfc180cef..49eb7e3706 100644 --- a/apps/common/sg/geometry/Geometry.h +++ b/apps/common/sg/geometry/Geometry.h @@ -21,22 +21,33 @@ namespace ospray { namespace sg { - /*! a geometry node - the generic geometry node */ - struct Geometry : public sg::Node { - Geometry(const std::string &type) : type(type) {}; + struct Geometry : public sg::Renderable + { + Geometry(const std::string &type); /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override { return "ospray::sg::Geometry"; } - - //! return bounding box of all primitives - virtual box3f getBounds() override = 0; + virtual std::string toString() const override; /*! geometry type, i.e., 'spheres', 'cylinders', 'trianglemesh', ... */ const std::string type; /*! material for this geometry */ - std::shared_ptr material; + // std::shared_ptr material; }; + + // Inlined member definitions ///////////////////////////////////////////// + + inline Geometry::Geometry(const std::string &type) + : type(type) + { + createChild("material", "Material"); + createChild("type", "string"); + } + + inline std::string Geometry::toString() const + { + return "ospray::sg::Geometry"; + } } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/geometry/Spheres.cpp b/apps/common/sg/geometry/Spheres.cpp index e1cf2256dc..dfffd80b4f 100644 --- a/apps/common/sg/geometry/Spheres.cpp +++ b/apps/common/sg/geometry/Spheres.cpp @@ -17,30 +17,48 @@ #undef NDEBUG #include "sg/geometry/Spheres.h" -#include "sg/common/Integrator.h" +#include "sg/common/Data.h" +#include "sg/common/World.h" // xml parser #include "common/xml/XML.h" namespace ospray { namespace sg { - using std::string; - using std::cout; - using std::endl; - - Spheres::Sphere::Sphere(vec3f position, - float radius, + Spheres::Sphere::Sphere(const vec3f &position, + float radius, uint32_t typeID) : position(position), radius(radius), typeID(typeID) - {} + { + } + + box3f Spheres::Sphere::bounds() const + { + return {position - vec3f(radius), position + vec3f(radius)}; + } - box3f Spheres::getBounds() + Spheres::Spheres() + : Geometry("spheres") { + createChild("sphereData"); + createChild("colorData"); + auto &materialNode = createChild("material", "Material"); + materialNode["Kd"].setValue(vec3f(1,1,1)); + materialNode["Ks"].setValue(vec3f(0,0,0)); + materialNode["Ns"].setValue(0.f); + } + + box3f Spheres::bounds() const + { + auto sphereData = child("sphereData").shared_from_this(); + std::shared_ptr> spheres + = std::dynamic_pointer_cast>(sphereData); + box3f bounds = empty; - for (size_t i=0;iv) + bounds.extend(s.bounds()); return bounds; } @@ -60,87 +78,54 @@ namespace ospray { existant) that contains additional binary data that the xml node fields may point into */ - void Spheres::setFromXML(const xml::Node *const node, + void Spheres::setFromXML(const xml::Node &node, const unsigned char *binBasePtr) { - size_t num = std::stoll(node->getProp("num")); - size_t ofs = std::stoll(node->getProp("ofs","-1")); - float rad = atof(node->getProp("radius").c_str()); - - Spheres::Sphere s(vec3f(0.f),rad,0); - if (ofs == (size_t)-1) { - cout << "#osp:qtv: 'Spheres' ofs is '-1', " - << "generating set of random spheres..." << endl; - for (uint32_t i = 0; i < num; i++) { - s.position.x = drand48(); - s.position.y = drand48(); - s.position.z = drand48(); - s.radius = rad; - sphere.push_back(s); - } - } else { - const vec3f *in = (const vec3f*)(binBasePtr+ofs); - for (uint32_t i = 0; i < num; i++) { - memcpy(&s,&in[i],sizeof(*in)); - sphere.push_back(s); - } - } + throw std::runtime_error("setFromXML no longer makes sense with the" + " new scene graph design"); } - - void Spheres::render(RenderContext &ctx) + void Spheres::postCommit(RenderContext &ctx) { - assert(!ospGeometry); - ospGeometry = ospNewGeometry("spheres"); - assert(ospGeometry); - OSPData data = ospNewData(sphere.size()*5,OSP_FLOAT, - &sphere[0],OSP_DATA_SHARED_BUFFER); - ospSetData(ospGeometry,"spheres",data); + auto sphereDataNode = child("sphereData").shared_from_this(); + std::shared_ptr> sphereData + = std::dynamic_pointer_cast>(sphereDataNode); + OSPData ospSphereData = ospNewData(sphereData->v.size()*5,OSP_FLOAT, + sphereData->v.data(), + OSP_DATA_SHARED_BUFFER); + ospCommit(ospSphereData); + + ospSetData(ospGeometry,"spheres",ospSphereData); ospSet1i(ospGeometry,"bytes_per_sphere",sizeof(Spheres::Sphere)); ospSet1i(ospGeometry,"center_offset", 0*sizeof(float)); ospSet1i(ospGeometry,"offset_radius", 3*sizeof(float)); ospSet1i(ospGeometry,"offset_materialID", 4*sizeof(float)); - OSPMaterial mat = - ospNewMaterial(ctx.integrator?ctx.integrator->getOSPHandle():nullptr,"default"); - if (mat) { - vec3f kd = vec3f(.7f); - ospSet3fv(mat,"kd",&kd.x); - ospCommit(mat); + auto colorDataNode = child("colorData").shared_from_this(); + std::shared_ptr> colorData + = std::dynamic_pointer_cast>(colorDataNode); + + if (colorData) { + OSPData ospColorData = ospNewData(colorData->v.size()*5,OSP_FLOAT, + colorData->v.data(), + OSP_DATA_SHARED_BUFFER); + ospCommit(ospColorData); + + ospSetData(ospGeometry,"color",ospColorData); + ospSet1i(ospGeometry,"color_offset", 0*sizeof(float)); + ospSet1i(ospGeometry,"color_stride", 4*sizeof(float)); } - ospSetMaterial(ospGeometry,mat); + + ospSetMaterial(ospGeometry, + (OSPMaterial)child("material").valueAs()); ospCommit(ospGeometry); - ospAddGeometry(ctx.world->ospModel,ospGeometry); - ospCommit(data); + ospAddGeometry(ctx.world->ospModel, ospGeometry); } - struct RandomSpheres : public Spheres { - //! \brief Initialize this node's value from given XML node - void setFromXML(const xml::Node *const node, - const unsigned char *binBasePtr) - { - vec3i dimensions = toVec3i(node->getProp("dimensions").c_str()); - int num = toInt(node->getProp("num").c_str()); - - float max_r = atof(node->getProp("radius").c_str());//std::max(dimensions.x,std::max(dimensions.y,dimensions.z)) / powf(num,.33f); - float f = 0.3f; // overhang around the dimensions - for (int i=0;i sphere; + OSPGeometry ospGeometry {nullptr}; }; } // ::ospray::sg diff --git a/apps/common/sg/geometry/TriangleMesh.cpp b/apps/common/sg/geometry/TriangleMesh.cpp index aba94f3f47..2f99a38e3f 100644 --- a/apps/common/sg/geometry/TriangleMesh.cpp +++ b/apps/common/sg/geometry/TriangleMesh.cpp @@ -16,22 +16,22 @@ #include "sg/geometry/TriangleMesh.h" #include "sg/common/World.h" -#include "sg/common/Integrator.h" namespace ospray { namespace sg { - //! return the bounding box of all primitives - box3f TriangleMesh::getBounds() + TriangleMesh::TriangleMesh() + : Geometry("trianglemesh") { - box3f bounds = empty; - for (uint32_t i = 0; i < vertex->getSize(); i++) - bounds.extend(vertex->get3f(i)); - return bounds; + createChild("material", "Material"); } - //! return the bounding box of all primitives - box3f PTMTriangleMesh::getBounds() + std::string TriangleMesh::toString() const + { + return "ospray::sg::Geometry"; + } + + box3f TriangleMesh::computeBounds() const { box3f bounds = empty; for (uint32_t i = 0; i < vertex->getSize(); i++) @@ -39,7 +39,7 @@ namespace ospray { return bounds; } - //! \brief Initialize this node's value from given XML node + //! \brief Initialize this node's value from given XML node /*! \detailed This allows a plug-and-play concept where a XML file can specify all kind of nodes wihout needing to know @@ -47,39 +47,53 @@ namespace ospray { create a proper C++ instance of the given node type (the OSP_REGISTER_SG_NODE() macro will allow it to do so), and can tell the node to parse itself from the given XML content and - XML children - + XML children + \param node The XML node specifying this node's fields \param binBasePtr A pointer to an accompanying binary file (if existant) that contains additional binary data that the xml node fields may point into */ - void TriangleMesh::setFromXML(const xml::Node *const node, const unsigned char *binBasePtr) + void TriangleMesh::setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) { - xml::for_each_child_of(*node,[&](const xml::Node &child){ - if (child.name == "vertex") { - size_t num = std::stoll(child.getProp("num")); - size_t ofs = std::stoll(child.getProp("ofs")); - vertex = std::make_shared((vec3f*)((char*)binBasePtr+ofs),num,false); - } - else if (child.name == "index") { - size_t num = std::stoll(child.getProp("num")); - size_t ofs = std::stoll(child.getProp("ofs")); - index = std::make_shared((vec3i*)((char*)binBasePtr+ofs),num,false); - } - }); + std::string fileName = node.getProp("fileName"); + if (fileName.empty()) { + xml::for_each_child_of(node, [&](const xml::Node &child){ + if (child.name == "vertex") { + size_t num = std::stoll(child.getProp("num")); + size_t ofs = std::stoll(child.getProp("ofs")); + vertex = std::shared_ptr(new DataArray3f((vec3f*)((char*)binBasePtr+ofs),num,false)); + } + else if (child.name == "index") { + size_t num = std::stoll(child.getProp("num")); + size_t ofs = std::stoll(child.getProp("ofs")); + index = std::shared_ptr(new DataArray3i((vec3i*)((char*)binBasePtr+ofs),num,false)); + } + }); + } } - /*! 'render' the nodes */ - void TriangleMesh::render(RenderContext &ctx) + PTMTriangleMesh::PTMTriangleMesh() : TriangleMesh() { - if (ospGeometry) return; + } - assert(ctx.world); - assert(ctx.world->ospModel); + void TriangleMesh::postCommit(RenderContext &ctx) + { + if (ospGeometry) + { + assert((OSPMaterial)child("material").valueAs()); + ospSetMaterial(ospGeometry, + (OSPMaterial)child("material").valueAs()); + ospCommit(ospGeometry); + return; + } + if (ospGeometry) + ospRelease(ospGeometry); ospGeometry = ospNewGeometry("trianglemesh"); + // set vertex data if (vertex && vertex->notEmpty()) ospSetData(ospGeometry,"vertex",vertex->getOSP()); @@ -91,116 +105,16 @@ namespace ospray { if (index && index->notEmpty()) ospSetData(ospGeometry,"index",index->getOSP()); -#if 1 - OSPMaterial mat = NULL; - // try to generate ospray material from the sg material stored with this object - if (material) { - material->render(ctx); - mat = material->ospMaterial; - } - - // if object couldt generate a valid material, create a default one - if (!mat) { - std::cout << "#osp:sg: no material on object, creating default one" << std::endl; - mat = ospNewMaterial(ctx.integrator?ctx.integrator->getOSPHandle():NULL,"default"); - assert(mat); - vec3f kd(.7f); - vec3f ks(.3f); - ospSet3fv(mat,"kd",&kd.x); - ospSet3fv(mat,"ks",&ks.x); - ospSet1f(mat,"Ns",99.f); - ospCommit(mat); - } - assert(mat); - ospSetMaterial(ospGeometry,mat); -#else - // assign a default material (for now.... eventually we might - // want to do a 'real' material - OSPMaterial mat = ospNewMaterial(ctx.integrator?ctx.integrator->getOSPHandle():NULL,"default"); - if (mat) { - vec3f kd(.7f); - vec3f ks(.3f); - ospSet3fv(mat,"kd",&kd.x); - ospSet3fv(mat,"ks",&ks.x); - ospSet1f(mat,"Ns",99.f); - ospCommit(mat); - } - ospSetMaterial(ospGeometry,mat); -#endif - + assert((OSPMaterial)child("material").valueAs()); + ospSetMaterial(ospGeometry, + (OSPMaterial)child("material").valueAs()); ospCommit(ospGeometry); - ospAddGeometry(ctx.world->ospModel,ospGeometry); + child("bounds").setValue(computeBounds()); } - /*! 'render' the nodes */ - void PTMTriangleMesh::render(RenderContext &ctx) + void TriangleMesh::postRender(RenderContext& ctx) { - if (ospGeometry) return; - - assert(ctx.world); - assert(ctx.world->ospModel); - - ospGeometry = ospNewGeometry("trianglemesh"); - // set vertex arrays - if (vertex && vertex->notEmpty()) { - OSPData data = vertex->getOSP(); - ospSetData(ospGeometry,"vertex",data); - } - if (normal && normal->notEmpty()) - ospSetData(ospGeometry,"vertex.normal",normal->getOSP()); - if (texcoord && texcoord->notEmpty()) - ospSetData(ospGeometry,"vertex.texcoord",texcoord->getOSP()); - // set index array - if (index && index->notEmpty()) - ospSetData(ospGeometry,"index",index->getOSP()); - - Triangle *triangles = (Triangle*)index->getBase(); - for(size_t i = 0; i < index->getSize(); i++) { - materialIDs.push_back(triangles[i].materialID >> 16); - } - primMatIDs = ospNewData(materialIDs.size(), OSP_INT, &materialIDs[0], 0); - ospSetData(ospGeometry,"prim.materialID",primMatIDs); - - OSPMaterial mat = NULL; - // try to generate ospray material from the sg material stored with this object - if (material) { - material->render(ctx); - mat = material->ospMaterial; - } - - // if object couldt generate a valid material, create a default one - if (!mat) { - std::cout << "#osp:sg: no material on object, creating default one" << std::endl; - mat = ospNewMaterial(ctx.integrator?ctx.integrator->getOSPHandle():NULL,"default"); - assert(mat); - vec3f kd(.7f); - vec3f ks(.3f); - ospSet3fv(mat,"kd",&kd.x); - ospSet3fv(mat,"ks",&ks.x); - ospSet1f(mat,"Ns",99.f); - ospCommit(mat); - } - assert(mat); - ospSetMaterial(ospGeometry,mat); - -#if 0 - // THIS CODE DOESN"T WORK RIGHT NOW!!!! - std::vector ospMaterials; - for (size_t i = 0; i < materialList.size(); i++) { - //If the material hasn't already been 'rendered' ensure that it is. - materialList[i]->render(ctx); - //Push the 'rendered' material onto the list - ospMaterials.push_back(materialList[i]->ospMaterial); - //std::cout << "#qtViewer 'rendered' material " << materialList[i]->name << std::endl; - } - - OSPData materialData = ospNewData(materialList.size(), OSP_OBJECT, &ospMaterials[0], 0); - ospSetData(ospGeometry, "materialList", materialData); -#endif - - ospCommit(ospGeometry); - ospAddGeometry(ctx.world->ospModel,ospGeometry); - //std::cout << "#qtViewer 'rendered' mesh\n"; + ospAddGeometry(ctx.currentOSPModel,ospGeometry); } OSP_REGISTER_SG_NODE(TriangleMesh); diff --git a/apps/common/sg/geometry/TriangleMesh.h b/apps/common/sg/geometry/TriangleMesh.h index 69e186bd7c..c2912f8cf4 100644 --- a/apps/common/sg/geometry/TriangleMesh.h +++ b/apps/common/sg/geometry/TriangleMesh.h @@ -19,27 +19,26 @@ #include "sg/common/Node.h" #include "sg/geometry/Geometry.h" #include "sg/common/Data.h" +#include "sg/common/World.h" namespace ospray { namespace sg { /*! A Simple Triangle Mesh that stores vertex, normal, texcoord, and vertex color in separate arrays */ - struct TriangleMesh : public sg::Geometry { + struct TriangleMesh : public sg::Geometry + { + TriangleMesh(); - //! constructor - TriangleMesh() : Geometry("trianglemesh"), ospGeometry(NULL) {}; - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const { return "ospray::sg::Geometry"; } + std::string toString() const override; - //! return bounding box of all primitives - virtual box3f getBounds(); + box3f computeBounds() const override; - /*! 'render' the nodes */ - virtual void render(RenderContext &ctx); + void postCommit(RenderContext &ctx) override; + void postRender(RenderContext& ctx) override; - //! \brief Initialize this node's value from given XML node + //! \brief Initialize this node's value from given XML node /*! \detailed This allows a plug-and-play concept where a XML file can specify all kind of nodes wihout needing to know @@ -47,27 +46,32 @@ namespace ospray { create a proper C++ instance of the given node type (the OSP_REGISTER_SG_NODE() macro will allow it to do so), and can tell the node to parse itself from the given XML content and - XML children - + XML children + \param node The XML node specifying this node's fields \param binBasePtr A pointer to an accompanying binary file (if existant) that contains additional binary data that the xml node fields may point into */ - virtual void setFromXML(const xml::Node *const node, const unsigned char *binBasePtr); + void setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) override; + + // Data members // + + OSPGeometry ospGeometry {nullptr}; + OSPGeometry ospGeometryInstance {nullptr}; + OSPModel ospModel {nullptr}; - OSPGeometry ospGeometry; - // to allow memory-mapping triangle arrays (or in general, // sharing data with an application) we use data arrays, not std::vector's //! vertex (position) array std::shared_ptr vertex; - + //! vertex normal array. empty means 'not present' std::shared_ptr normal; - + //! vertex color array. empty means 'not present' std::shared_ptr color; @@ -80,51 +84,29 @@ namespace ospray { /*! A special triangle mesh that allows per-triangle materials */ - struct PTMTriangleMesh : public sg::Geometry { - + //TODO: add commit code to commit material list! + struct PTMTriangleMesh : public sg::TriangleMesh + { /*! triangle with per-triangle material ID */ - struct Triangle { + struct Triangle + { uint32_t vtxID[3], materialID; }; //! constructor - PTMTriangleMesh() : Geometry("trianglemesh"), ospGeometry(NULL) {}; - - // return bounding box of all primitives - virtual box3f getBounds(); + PTMTriangleMesh(); - /*! 'render' the nodes */ - virtual void render(RenderContext &ctx); + // Data members // - OSPGeometry ospGeometry; + /*! \brief "material list" for this trianglemesh - /*! \brief "material list" for this trianglemesh - If non-empty, the 'Triangle::materialID' indexes into this list; if empty, all trianlges should use the Geometry::material no matter what Triangle::materialID is set */ - std::vector > materialList; - std::vector materialIDs; - - // to allow memory-mapping triangle arrays (or in general, - // sharing data with an application) we use data arrays, not std::vector's - - //! vertex (position) array - std::shared_ptr vertex; - - //! vertex normal array. empty means 'not present' - std::shared_ptr normal; - - //! vertex color array. empty means 'not present' - std::shared_ptr color; + std::vector> materialList; + std::vector materialIDs; - //! vertex texture coordinate array. empty means 'not present' - std::shared_ptr texcoord; - - //! triangle indices - std::shared_ptr index; - //! material IDs OSPData primMatIDs; }; diff --git a/apps/common/sg/importer/ImportOBJ.cpp b/apps/common/sg/importer/ImportOBJ.cpp index 123e8abe73..bff38875d3 100644 --- a/apps/common/sg/importer/ImportOBJ.cpp +++ b/apps/common/sg/importer/ImportOBJ.cpp @@ -43,10 +43,10 @@ namespace ospray { std::shared_ptr loadTexture(const std::string &path, const std::string &fileName, - const bool prefereLinear = false) + const bool preferLinear = false) { FileName texFileName = path+"/"+fileName; - std::shared_ptr tex = Texture2D::load(texFileName, prefereLinear); + std::shared_ptr tex = Texture2D::load(texFileName, preferLinear); if (!tex) std::cout << "could not load texture " << texFileName << " !" << endl; return tex; @@ -59,14 +59,14 @@ namespace ospray { Vertex(int v) : v(v), vt(v), vn(v) {}; Vertex(int v, int vt, int vn) : v(v), vt(vt), vn(vn) {}; }; - + static inline bool operator < ( const Vertex& a, const Vertex& b ) { if (a.v != b.v) return a.v < b.v; if (a.vn != b.vn) return a.vn < b.vn; if (a.vt != b.vt) return a.vt < b.vt; return false; } - + /*! Fill space at the end of the token with 0s. */ static inline const char* trimEnd(const char* token) { size_t len = strlen(token); @@ -76,24 +76,24 @@ namespace ospray { *pe-- = 0; return token; } - + /*! Determine if character is a separator. */ static inline bool isSep(const char c) { return (c == ' ') || (c == '\t'); } - + /*! Parse separator. */ static inline const char* parseSep(const char*& token) { size_t sep = strspn(token, " \t"); if (!sep) throw std::runtime_error("separator expected"); return token+=sep; } - + /*! Parse optional separator. */ static inline const char* parseSepOpt(const char*& token) { return token+=strspn(token, " \t"); } - + /*! Read float from a string. */ static inline float getFloat(const char*& token) { token += strspn(token, " \t"); @@ -101,14 +101,14 @@ namespace ospray { token += strcspn(token, " \t\r"); return n; } - + /*! Read vec2f from a string. */ static inline vec2f getVec2f(const char*& token) { float x = getFloat(token); float y = getFloat(token); return vec2f(x,y); } - + /*! Read vec3f from a string. */ static inline vec3f getVec3f(const char*& token) { float x = getFloat(token); @@ -117,25 +117,25 @@ namespace ospray { return vec3f(x,y,z); } - + class OBJLoader { public: - - std::shared_ptr world; + + std::shared_ptr world; std::map > material; - + /*! Constructor. */ - OBJLoader(std::shared_ptr world, const FileName& fileName); - + OBJLoader(std::shared_ptr world, const FileName& fileName); + /*! Destruction */ ~OBJLoader(); - + /*! Public methods. */ void loadMTL(const FileName& fileName); - + template - inline T parse(const char *&token); + inline T parse(const char *&token, std::string& type); /*! try to parse given token stream as a float-type with given keyword; and if successful, assign to material. returns true @@ -144,7 +144,7 @@ namespace ospray { bool tryToMatch(const char *&token, const char *keyWord, const std::shared_ptr &mat); - + /*! try to parse given token stream as a float-type with given keyword; and if successful, load the texture and assign to material. returns true if matched, false if not */ @@ -156,12 +156,14 @@ namespace ospray { private: FileName path; + FileName fullPath; /*! Geometry buffer. */ std::vector v; std::vector vn; std::vector vt; std::vector > curGroup; + std::string curGroupName; /*! Material handling. */ std::shared_ptr curMaterial; @@ -179,17 +181,18 @@ namespace ospray { const Vertex& i); }; - - template<> inline vec3f OBJLoader::parse(const char *&token) - { return getVec3f(token); } - template<> inline float OBJLoader::parse(const char *&token) - { return getFloat(token); } - template<> inline std::string OBJLoader::parse(const char *&token) - { return std::string(token); } - OBJLoader::OBJLoader(std::shared_ptr world, const FileName &fileName) + template<> inline vec3f OBJLoader::parse(const char *&token, std::string& type) + { type="vec3f"; return getVec3f(token); } + template<> inline float OBJLoader::parse(const char *&token, std::string& type) + { type="float"; return getFloat(token); } + template<> inline std::string OBJLoader::parse(const char *&token, std::string& type) + { type="string"; return std::string(token); } + + OBJLoader::OBJLoader(std::shared_ptr world, const FileName &fileName) : world(world), - path(fileName.path()) + path(fileName.path()), + fullPath(fileName) { /* open file */ std::ifstream cin; @@ -203,6 +206,7 @@ namespace ospray { // Handle defaultMaterial = g_device->rtNewMaterial("matte"); // g_device->rtSetFloat3(defaultMaterial, "reflectance", 0.5f, 0.5f, 0.5f); // g_device->rtCommit(defaultMaterial); + defaultMaterial = std::static_pointer_cast(createNode("material","Material")); curMaterial = defaultMaterial; char line[1000000]; @@ -223,6 +227,9 @@ namespace ospray { const char* token = trimEnd(line + strspn(line, " \t")); if (token[0] == 0) continue; + if (token[0] == 'g') + curGroupName = std::string(parseSep(token += 1)); + /*! parse position */ if (token[0] == 'v' && isSep(token[1])) { v.push_back(getVec3f(token += 2)); continue; } @@ -257,7 +264,9 @@ namespace ospray { if (material.find(name) == material.end()) curMaterial = defaultMaterial; else + { curMaterial = material[name]; + } continue; } @@ -266,13 +275,13 @@ namespace ospray { loadMTL(path + std::string(parseSep(token += 6))); continue; } - + // ignore unknown stuff } flushFaceGroup(); cin.close(); } - + OBJLoader::~OBJLoader() { } @@ -288,13 +297,15 @@ namespace ospray { { if (strncasecmp(token, keyWord, strlen(keyWord))) return false; - + parseSep(token+=strlen(keyWord)); - mat->setParam(keyWord, parse(token)); + std::string type; + auto val = parse(token,type); + mat->createChild(keyWord, type, val); return true; } - + /*! try to parse given token stream as a float-type with given keyword; and if successful, load the texture and assign to material. returns true if matched, false if not */ @@ -307,8 +318,11 @@ namespace ospray { return false; parseSep(token+=strlen(keyWord)); - mat->setParam(keyWord, - loadTexture(path,parse(token),preferLinear)); + std::string type; + auto node = loadTexture(path,parse(token,type),preferLinear); + // mat->createChildWithValue(keyWord, type, val); + std::string name(keyWord); + mat->setChild(keyWord, node); return true; } @@ -326,7 +340,7 @@ namespace ospray { memset(line, 0, sizeof(line)); // Handle cur = null; - std::shared_ptr cur; + std::shared_ptr cur = std::static_pointer_cast(createNode("material","Material")); while (cin.peek() != -1) { /* load next multiline */ @@ -347,7 +361,8 @@ namespace ospray { parseSep(token+=6); // if (cur) g_device->rtCommit(cur); std::string name(token); - material[name] = cur = std::make_shared(); //g_device->rtNewMaterial("obj"); + material[name] = cur = std::static_pointer_cast(createNode(name,"Material")); + //g_device->rtNewMaterial("obj"); // model.material.push_back(cur); cur->name = name; cur->type = "OBJ"; @@ -360,13 +375,13 @@ namespace ospray { continue; }//throw std::runtime_error("invalid material file: newmtl expected first"); - if (!strncmp(token, "illum_4",7)) { + if (!strncmp(token, "illum_4",7)) { /*! iw: hack for VMD-exported OBJ files, working around a bug in VMD's OBJ exporter (VMD writes "illum_4" (with an underscore) rather than "illum 4" (with a whitespace) */ - parseSep(token += 7); - continue; + parseSep(token += 7); + continue; } if (tryToMatch(token,"illum",cur)) continue; @@ -376,7 +391,8 @@ namespace ospray { if (tryToMatch(token,"Ka",cur)) continue; if (tryToMatch(token,"Kd",cur)) continue; if (tryToMatch(token,"Ks",cur)) continue; - if (tryToMatch(token,"Tf",cur)) continue; + //todo: Tf in path tracer often produces transparent walls + // if (tryToMatch(token,"Tf",cur)) continue; if (tryToMatchTexture(token,"map_d",cur,true)) continue; if (tryToMatchTexture(token,"map_Ns",cur,true)) continue; @@ -391,16 +407,16 @@ namespace ospray { if (!strncmp(token, "type", 4)) { parseSep(token += 4); - cur->type = std::string(token); + cur->child("type").setValue(std::string(token)); continue; } - // add anything else as float param + //add anything else as float param const char * ident = token; token += strcspn(token, " \t"); *(char*)token = 0; parseSepOpt(token += 1); - cur->setParam(ident, getFloat(token)); + cur->createChildWithValue(ident, "float", getFloat(token)); } // if (cur) g_device->rtCommit(cur); cin.close(); @@ -444,7 +460,7 @@ namespace ospray { return(v); } - uint32_t OBJLoader::getVertex(std::map& vertexMap, + uint32_t OBJLoader::getVertex(std::map& vertexMap, const std::shared_ptr &mesh, const Vertex& i) { @@ -477,20 +493,32 @@ namespace ospray { if (curGroup.empty()) return; std::map vertexMap; - std::shared_ptr mesh = std::make_shared(); + static int counter=0; + std::stringstream ss; + ss << fullPath.name() << "_" << counter++ << "_" << curGroupName; + std::string name = ss.str(); + //scenegraph + auto mesh = + std::static_pointer_cast(createNode(name, "TriangleMesh")); + auto model = createNode(name+"_model", "Model"); + auto instance = createNode(name+"_instance", "Instance"); + model->add(mesh); + instance->setChild("model", model); + model->setParent(instance); + world->add(instance); mesh->vertex = std::make_shared(); mesh->normal = std::make_shared(); mesh->texcoord = std::make_shared(); mesh->index = std::make_shared(); - mesh->material = curMaterial; - world->add(mesh); + //std::cout << "setting mesh curMaterial: " << curMaterial->name << std::endl; + mesh->setChild("material",curMaterial); // merge three indices into one for (size_t j=0; j < curGroup.size(); j++) { /* iterate over all faces */ const std::vector& face = curGroup[j]; Vertex i0 = face[0], i1 = Vertex(-1), i2 = face[1]; - + /* triangulate the face with a triangle fan */ for (size_t k=2; k < face.size(); k++) { i1 = i2; i2 = face[k]; @@ -501,19 +529,17 @@ namespace ospray { continue; vec3i tri(v0,v1,v2); - // mesh->index.cast()->push_back(tri); //Vec3i(v0, v1, v2)); std::dynamic_pointer_cast(mesh->index)->push_back(tri); //Vec3i(v0, v1, v2)); } } curGroup.clear(); } - void importOBJ(const std::shared_ptr &world, const FileName &fileName) + void importOBJ(const std::shared_ptr &world, const FileName &fileName) { std::cout << "ospray::sg::importOBJ: importing from " << fileName << endl; OBJLoader loader(world,fileName); } - } } // ::ospray diff --git a/apps/common/sg/importer/ImportOSG.cpp b/apps/common/sg/importer/ImportOSG.cpp new file mode 100644 index 0000000000..b4b5d4bdf1 --- /dev/null +++ b/apps/common/sg/importer/ImportOSG.cpp @@ -0,0 +1,253 @@ +// // ======================================================================== // +// // Copyright 2009-2016 Intel Corporation // +// // // +// // Licensed under the Apache License, Version 2.0 (the "License"); // +// // you may not use this file except in compliance with the License. // +// // You may obtain a copy of the License at // +// // // +// // http://www.apache.org/licenses/LICENSE-2.0 // +// // // +// // Unless required by applicable law or agreed to in writing, software // +// // distributed under the License is distributed on an "AS IS" BASIS, // +// // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// // See the License for the specific language governing permissions and // +// // limitations under the License. // +// // ======================================================================== // + +// #undef NDEBUG + +// // header +// #include "SceneGraph.h" +// // stl +// #include +// // xml +// #include "common/xml/XML.h" + +// namespace ospray { +// namespace sg { +// using std::string; +// using std::cout; +// using std::endl; + +// // ================================================================== +// // sg node registry code +// // ================================================================== +// typedef sg::Node *(*creatorFct)(); + +// std::map osgNodeRegistry; + +// /*! create a node of given type if registered (and tell it to +// parse itself from that xml node), or throw an exception if +// unkown node type */ +// // sg::Node *createSGNodeFrom(const xml::Node &node, const unsigned char *binBasePtr) +// // { +// // std::map::iterator it = osgNodeRegistry.find(node.name); +// // creatorFct creator = NULL; +// // if (it == osgNodeRegistry.end()) { +// // std::string creatorName = "ospray_create_sg_node__"+std::string(node.name); +// // creator = (creatorFct)getSymbol(creatorName); +// // if (!creator) +// // throw std::runtime_error("unknown ospray scene graph node '"+node.name+"'"); +// // else +// // std::cout << "#osp:sg: creating at least one instance of node type '" << node.name << "'" << std::endl; +// // osgNodeRegistry[node.name] = creator; +// // } else creator = it->second; +// // assert(creator); +// // sg::Node *newNode = creator(); +// // assert(newNode); +// // newNode->setType(node.name); +// // newNode->setName(node.getProp("name")); +// // try { +// // newNode->setFromXML(node,binBasePtr); +// // return newNode; +// // } catch (std::runtime_error e) { +// // delete newNode; +// // throw e; +// // } +// // } + +// // ================================================================== +// // XLM parser +// // ================================================================== + +// sg::Node *parseSGNode(const xml::Node &node); + +// bool parseSGParam(sg::Node *target, const xml::Node &node) +// { +// const std::string name = node.getProp("name"); +// if (name == "") return false; +// if (node.name == "data") { +// assert(node.child.size() == 1); +// sg::Node *value = parseSGNode(*node.child[0]); +// assert(value != NULL); +// std::shared_ptr dataNode(value); +// assert(dataNode); +// target->setParam(name,dataNode); +// // target->addParam(new ParamT >(name,dataNode)); +// return true; +// } +// if (node.name == "object") { +// assert(node.child.size() == 1); +// std::shared_ptr value = std::shared_ptr(parseSGNode(*node.child[0])); +// assert(value); +// target->setParam(name,value); +// // target->addParam(new ParamT >(name,value)); +// return true; +// } +// if (node.name == "int") { +// // target->addParam(new ParamT(name,atoi(node.content.c_str()))); +// target->setParam(name,std::stoi(node.content)); +// return true; +// } +// if (node.name == "float") { +// // target->addParam(new ParamT(name,atof(node.content.c_str()))); +// target->setParam(name,std::stof(node.content)); +// return true; +// } +// return false; +// } + +// sg::Info *parseSGInfoNode(const xml::Node &node) +// { +// assert(node.name == "Info"); +// Info *info = new Info; +// for (auto c : node.child) { +// if (c->name == "acks") +// info->acks = c->content; +// else if (c->name == "description") +// info->description = c->content; +// else if (c->name == "permissions") +// info->permissions = c->content; +// else +// throw std::runtime_error("unknown node type '"+c->name +// +"' in ospray::sg::Info node"); +// } +// return info; +// } + +// sg::Integrator *parseSGIntegratorNode(const xml::Node &node) +// { +// assert(node.name == "Integrator"); +// Integrator *integrator = new Integrator(node.getProp("type","")); +// for (auto c : node.child) { +// if (parseSGParam(integrator,*c)) +// continue; +// throw std::runtime_error("unknown node type '"+c->name +// +"' in ospray::sg::Integrator node"); +// } +// return integrator; +// } + +// void parseSGWorldNode(sg::World *world, +// const xml::Node &node, +// const unsigned char *binBasePtr) +// { +// for (auto c : node.child) { +// //TODO: Carson: better way to do this? +// std::cout << "loading node: " << c->name << std::endl; +// if (c->name.find("Chombo") != std::string::npos) +// { +// std::cout << "loading amr\n"; +// ospLoadModule("amr"); +// ospLoadModule("sg_amr"); +// } +// std::shared_ptr newNode = +// createNode(c->getProp("name"),c->name).get(); +// newNode->setFromXML(*c,binBasePtr); +// world->nodes.push_back(newNode); +// world->add(newNode); +// std::cout << "adding node to world: " << newNode->name() << " " +// << newNode->type() << "\n"; +// } +// } + +// sg::DataBuffer *parseSGDataNode(const xml::Node &node) +// { +// #if 1 +// NOTIMPLEMENTED; +// #else +// assert(node.name == "Data"); +// Data *data = new Data; +// assert(node.child.empty()); +// size_t num = atol(node.getProp("num").c_str()); +// size_t ofs = atol(node.getProp("ofs").c_str()); +// return data; +// #endif +// } + +// sg::Node *parseSGNode(const xml::Node &node) +// { +// if (node.name == "Data") +// return parseSGDataNode(node); +// if (node.name == "Info") +// return parseSGInfoNode(node); +// if (node.name == "Integrator") +// return parseSGIntegratorNode(node); +// std::cout << "warning: unknown sg::Node type '" << node.name << "'" << std::endl; +// return NULL; +// } + +// std::shared_ptr loadOSG(const std::string &fileName) +// { +// std::shared_ptr doc; +// // Ref doc = NULL; +// cout << "#osp:sg: starting to read OSPRay XML file '" << fileName << "'" << endl; +// doc = xml::readXML(fileName); +// cout << "#osp:sg: XML file read, starting to parse content..." << endl; + +// const std::string binFileName = fileName+"bin"; +// const unsigned char * const binBasePtr = mapFile(binFileName); + +// if (!doc) +// throw std::runtime_error("could not parse "+fileName); + +// if (doc->child.size() != 1) +// throw std::runtime_error("not an ospray xml file (empty XML document; no 'ospray' child node)'"); +// if ((doc->child[0]->name != "ospray" && doc->child[0]->name != "OSPRay") ) +// throw std::runtime_error("not an ospray xml file (document root node is '"+doc->child[0]->name+"', should be 'ospray'"); + +// std::shared_ptr root = doc->child[0]; +// std::shared_ptr world(new World);//parseOSPRaySection(root->child[0]); +// if (root->child.size() == 1 && root->child[0]->name == "World") { +// parseSGWorldNode(world.get(),*root->child[0],binBasePtr); +// } else { +// parseSGWorldNode(world.get(),*root,binBasePtr); +// } + +// cout << "#osp:sg: done parsing OSP file" << endl; +// return world; +// } + +// void loadOSG(const std::string &fileName, std::shared_ptr world) +// { +// std::shared_ptr doc; +// // Ref doc = NULL; +// cout << "#osp:sg: starting to read OSPRay XML file '" << fileName << "'" << endl; +// doc = xml::readXML(fileName); +// cout << "#osp:sg: XML file read, starting to parse content..." << endl; + +// const std::string binFileName = fileName+"bin"; +// const unsigned char * const binBasePtr = mapFile(binFileName); + +// if (!doc) +// throw std::runtime_error("could not parse "+fileName); + +// if (doc->child.size() != 1) +// throw std::runtime_error("not an ospray xml file (empty XML document; no 'ospray' child node)'"); +// if ((doc->child[0]->name != "ospray" && doc->child[0]->name != "OSPRay") ) +// throw std::runtime_error("not an ospray xml file (document root node is '"+doc->child[0]->name+"', should be 'ospray'"); + +// std::shared_ptr root = doc->child[0]; +// // std::shared_ptr world(new World);//parseOSPRaySection(root->child[0]); +// if (root->child.size() == 1 && root->child[0]->name == "World") { +// parseSGWorldNode(world.get(),*root->child[0],binBasePtr); +// } else { +// parseSGWorldNode(world.get(),*root,binBasePtr); +// } + +// cout << "#osp:sg: done parsing OSP file" << endl; +// // return world; +// } + +// } // ::ospray::sg +// } // ::ospray diff --git a/apps/common/sg/importer/ImportOSP.cpp b/apps/common/sg/importer/ImportOSP.cpp index e43c2f58a2..08e0589bf0 100644 --- a/apps/common/sg/importer/ImportOSP.cpp +++ b/apps/common/sg/importer/ImportOSP.cpp @@ -30,40 +30,29 @@ namespace ospray { using std::cout; using std::endl; - // ================================================================== - // sg node registry code - // ================================================================== - typedef std::shared_ptr (*creatorFct)(); - - std::map sgNodeRegistry; - /*! create a node of given type if registered (and tell it to parse itself from that xml node), or throw an exception if unkown node type */ - std::shared_ptr createNodeFrom(const xml::Node &node, const unsigned char *binBasePtr) + std::shared_ptr createNodeFrom(const xml::Node &node, + const unsigned char *binBasePtr) { - std::map::iterator it = sgNodeRegistry.find(node.name); - creatorFct creator = NULL; - if (it == sgNodeRegistry.end()) { - const std::string creatorName = "ospray_create_sg_node__"+std::string(node.name); - creator = (creatorFct)getSymbol(creatorName); - if (!creator) - throw std::runtime_error("unknown ospray scene graph node '"+node.name+"'"); - else - std::cout << "#osp:sg: creating at least one instance of node type '" << node.name << "'" << std::endl; - sgNodeRegistry[node.name] = creator; + if (node.name.find("Chombo") != std::string::npos) { + if (!ospLoadModule("amr")) + std::runtime_error("could not load amr module\n"); + if (!ospLoadModule("sg_amr")) + std::runtime_error("could not load amr module\n"); } - else - creator = it->second; - - assert(creator); - std::shared_ptr newNode = creator(); - if (!newNode) + + std::string name = "untitled"; + if (node.hasProp("name")) + name = node.getProp("name"); + + std::shared_ptr newNode = createNode(name, node.name); + if (!newNode.get()) throw std::runtime_error("could not create scene graph node"); newNode->setFromXML(node,binBasePtr); - if (node.hasProp("name")) - registerNamedNode(node.getProp("name"),newNode); + return newNode; } @@ -76,35 +65,32 @@ namespace ospray { bool parseParam(std::shared_ptr target, const xml::Node &node) { const std::string name = node.getProp("name"); - if (name == "") return false; - if (node.name == "data") { + + if (name.empty()) { + return false; + } else if (node.name == "data") { assert(node.child.size() == 1); std::shared_ptr value = parseNode(*node.child[0]); - assert(value != NULL); - std::shared_ptr dataNode = std::dynamic_pointer_cast(value); + assert(value != nullptr); + std::shared_ptr dataNode = + std::dynamic_pointer_cast(value); assert(dataNode); - target->setParam(name,dataNode); - // target->addParam(new ParamT >(name,dataNode)); + target->createChildWithValue(name,"data",dataNode); return true; - } - if (node.name == "object") { + } else if (node.name == "object") { assert(node.child.size() == 1); std::shared_ptr value = parseNode(*node.child[0]); assert(value); - target->setParam(name,value); - // target->addParam(new ParamT >(name,value)); + target->createChildWithValue(name,"object",value); return true; - } - if (node.name == "int") { - // target->addParam(new ParamT(name,atoi(node.content.c_str()))); - target->setParam(name,std::stoi(node.content)); + } else if (node.name == "int") { + target->createChildWithValue(name,"int",std::stoi(node.content)); return true; - } - if (node.name == "float") { - // target->addParam(new ParamT(name,atof(node.content.c_str()))); - target->setParam(name,std::stof(node.content)); + } else if (node.name == "float") { + target->createChildWithValue(name,"float",std::stof(node.content)); return true; } + return false; } @@ -126,20 +112,7 @@ namespace ospray { return info; } - std::shared_ptr parseIntegratorNode(const xml::Node &node) - { - assert(node.name == "Integrator"); - std::shared_ptr integrator = std::make_shared(node.getProp("type","")); - for (auto c : node.child) { - if (parseParam(std::dynamic_pointer_cast(integrator),*c)) - continue; - throw std::runtime_error("unknown node type '"+c->name - +"' in ospray::sg::Integrator node"); - } - return integrator; - } - - void parseWorldNode(std::shared_ptr world, + void parseWorldNode(std::shared_ptr world, const xml::Node &node, const unsigned char *binBasePtr) { @@ -169,17 +142,16 @@ namespace ospray { return parseDataNode(node); if (node.name == "Info") return parseInfoNode(node); - if (node.name == "Integrator") - return parseIntegratorNode(node); - std::cout << "warning: unknown sg::Node type '" << node.name << "'" << std::endl; + std::cout << "warning: unknown sg::Node type '" << node.name << "'" + << std::endl; return std::shared_ptr(); } - std::shared_ptr importOSPVolumeViewerFile(std::shared_ptr doc) + void importOSPVolumeViewerFile(std::shared_ptr doc, std::shared_ptr world) { - std::shared_ptr world = std::make_shared(); std::shared_ptr volume - = std::make_shared(); + = std::dynamic_pointer_cast( + createNode("volume", "StructuredVolumeFromFile")); vec3i dimensions(-1); std::string fileName = ""; @@ -192,27 +164,29 @@ namespace ospray { else if (child.name == "filename") fileName = child.content; else if (child.name == "samplingRate") { - std::cout << "#osp.sg: cowardly refusing to parse 'samplingRate'" << std::endl; + std::cout << "#osp.sg: cowardly refusing to parse 'samplingRate'" + << std::endl; std::cout << "#osp.sg: (note this should be OK)" << std::endl; } else - throw std::runtime_error("unknown old-style osp file component volume::" + child.name); + throw std::runtime_error("unknown old-style osp file component volume::" + + child.name); }); std::cout << "#osp.sg: parsed old-style osp file as: " << std::endl; std::cout << " fileName = " << fileName << std::endl; std::cout << " voxelType = " << voxelType << std::endl; std::cout << " dimensions = " << dimensions << std::endl; std::cout << " path = " << doc->fileName.path() << std::endl; - volume->setTransferFunction(std::make_shared()); + // TODO: This works on the old style of scenegraph! + //volume->setTransferFunction(std::make_shared()); volume->fileNameOfCorrespondingXmlDoc = doc->fileName; volume->setFileName(fileName); volume->setDimensions(dimensions); volume->setScalarType(voxelType); world->add(volume); - return world; } - std::shared_ptr loadOSP(const std::string &fileName) + void loadOSP(std::shared_ptr world, const std::string &fileName) { std::shared_ptr doc; // std::shared_ptr doc = NULL; @@ -224,17 +198,16 @@ namespace ospray { if (doc->child.empty()) throw std::runtime_error("ospray xml input file does not contain any nodes!?"); -#if 1 /* TEMPORARY FIX while we transition old volumeViewer .osp files (that are not in scene graph format) to actual scene graph */ if (doc->child[0]->name == "volume") { std::cout << "#osp.sg: Seems the file we are parsing is actually not a " << endl; - std::cout << "#osp.sg: ospray scene graph file, but rather a (older) " << endl; - std::cout << "#osp.sg: ospVolumeViewer input file. Herocically trying to " << endl; + std::cout << "#osp.sg: ospray scene graph file, but rather an (older) " << endl; + std::cout << "#osp.sg: ospVolumeViewer input file. Heroically trying to " << endl; std::cout << "#osp.sg: convert this to scene graph while loading. " << endl; - return importOSPVolumeViewerFile(doc); + importOSPVolumeViewerFile(doc, world); + return; } -#endif const std::string binFileName = fileName+"bin"; const unsigned char * const binBasePtr = mapFile(binFileName); @@ -243,12 +216,18 @@ namespace ospray { throw std::runtime_error("could not parse "+fileName); if (doc->child.size() != 1) - throw std::runtime_error("not an ospray xml file (empty XML document; no 'ospray' child node)'"); + { + throw std::runtime_error( + "not an ospray xml file (empty XML document; no 'ospray' child node)'"); + } if ((doc->child[0]->name != "ospray" && doc->child[0]->name != "OSPRay") ) - throw std::runtime_error("not an ospray xml file (document root node is '"+doc->child[0]->name+"', should be 'ospray'"); + { + throw std::runtime_error("not an ospray xml file (document root node is '" + +doc->child[0]->name+"', should be 'ospray'"); + } std::shared_ptr root = doc->child[0]; - std::shared_ptr world = std::make_shared();//parseOSPRaySection(root->child[0]); + if (root->child.size() == 1 && root->child[0]->name == "World") { parseWorldNode(world,*root->child[0],binBasePtr); } else { @@ -256,7 +235,14 @@ namespace ospray { } cout << "#osp:sg: done parsing OSP file" << endl; - return world; + } + + std::shared_ptr loadOSP(const std::string &fileName) + { + auto world = createNode("world", "World"); + loadOSP(std::static_pointer_cast(world), fileName); + + return std::static_pointer_cast(world); } } // ::ospray::sg diff --git a/apps/common/sg/importer/ImportPLY.cpp b/apps/common/sg/importer/ImportPLY.cpp index f3b977973f..82ae02c384 100644 --- a/apps/common/sg/importer/ImportPLY.cpp +++ b/apps/common/sg/importer/ImportPLY.cpp @@ -109,7 +109,7 @@ namespace ospray { }; - std::shared_ptr readFile(const std::string &fileName) + void readFile(const std::string &fileName, std::shared_ptr mesh) { int nprops; PlyProperty **plist; @@ -267,8 +267,6 @@ namespace ospray { fprintf(stderr, "Faces must have vertex indices\n"); exit(-1); } - // if (!has_face_blue) - // material = getMaterial(200,200,200); triangles = num_elems; // numTrisWritten += num_elems; @@ -290,9 +288,6 @@ namespace ospray { vtx.x = tmp.verts[0]; //builder.addVertex(pos[tmp.verts[0]]); vtx.y = tmp.verts[1]; //builder.addVertex(pos[tmp.verts[1]]); vtx.z = tmp.verts[2]; //builder.addVertex(pos[tmp.verts[2]]); - // int matID = builder.addMaterial(material,true); - // builder.addTriangle(vtx,matID); - // vtx = vtx - vec3i(1); idx->v.push_back(vtx); } else { PRINT((int)tmp.nverts); @@ -308,8 +303,6 @@ namespace ospray { } } - // if (pos) delete[] pos; - // if (nor) delete[] nor; int num_comments; char **comments; @@ -324,8 +317,6 @@ namespace ospray { ply_close (ply); - // numFilesDone ++; - // cout << "total done so far: " << numFilesDone << " files, " << pretty(numTrisWritten) << " tris" << endl; for (int i=0;inelems;i++) { if (ply->elems[i]) continue; PlyElement *e = ply->elems[i]; @@ -345,19 +336,19 @@ namespace ospray { free(ply->elems); - std::shared_ptr mesh = std::make_shared(); mesh->index = idx; mesh->vertex = pos; if (!nor->v.empty()) mesh->normal = nor; - return mesh; } } // ::ospray::sg::ply - void importPLY(std::shared_ptr &world, const FileName &fileName) + void importPLY(std::shared_ptr &world, const FileName &fileName) { - std::shared_ptr mesh = ply::readFile(fileName.str()); + std::shared_ptr mesh = + std::static_pointer_cast(sg::createNode(fileName.name(), "TriangleMesh")); + ply::readFile(fileName.str(), mesh); world->add(std::dynamic_pointer_cast(mesh)); } diff --git a/apps/common/sg/importer/ImportRIVL.cpp b/apps/common/sg/importer/ImportRIVL.cpp index 16ac3adac4..9f1909e38a 100644 --- a/apps/common/sg/importer/ImportRIVL.cpp +++ b/apps/common/sg/importer/ImportRIVL.cpp @@ -16,13 +16,12 @@ #undef NDEBUG -#define WARN_ON_INCLUDING_OSPCOMMON 1 - #include "SceneGraph.h" #include "sg/common/Texture2D.h" #include "sg/geometry/TriangleMesh.h" // stl #include +#include namespace ospray { namespace sg { @@ -36,7 +35,13 @@ namespace ospray { void parseTextureNode(const xml::Node &node) { + std::stringstream ss; + ss << "rivlTexture_" << nodeList.size(); + const std::string name = ss.str(); + const std::string type = "Texture2D"; std::shared_ptr txt = std::make_shared(); + txt->setName(name); + txt->setType(type); txt->ospTexture = NULL; nodeList.push_back(txt); @@ -53,11 +58,11 @@ namespace ospray { else if (name == "depth") depth = atol(value.c_str()); }); - assert(ofs != size_t(-1) && "Offset not properly parsed for Texture2D nodes"); - assert(width != size_t(-1) && "Width not properly parsed for Texture2D nodes"); - assert(height != size_t(-1) && "Height not properly parsed for Texture2D nodes"); - assert(channels != size_t(-1) && "Channel count not properly parsed for Texture2D nodes"); - assert(depth != size_t(-1) && "Depth not properly parsed for Texture2D nodes"); + assert(ofs != -1 && "Offset not properly parsed for Texture2D nodes"); + assert(width != -1 && "Width not properly parsed for Texture2D nodes"); + assert(height != -1 && "Height not properly parsed for Texture2D nodes"); + assert(channels != -1 && "Channel count not properly parsed for Texture2D nodes"); + assert(depth != -1 && "Depth not properly parsed for Texture2D nodes"); txt->texelType = OSP_TEXTURE_R8; if (channels == 4 && depth == 1) @@ -70,6 +75,8 @@ namespace ospray { txt->texelType = OSP_TEXTURE_RGB32F; txt->size = vec2i(width, height); + txt->channels = channels; + txt->depth = depth; if (channels == 4) { // RIVL bin stores alpha channel inverted, fix here size_t sz = width * height; @@ -78,7 +85,6 @@ namespace ospray { memcpy(texel, (char*)binBasePtr+ofs, sz*sizeof(vec4uc)); for (size_t p = 0; p < sz; p++) texel[p].w = 255 - texel[p].w; - // txt->texel = texel; txt->texelData = std::make_shared((unsigned char*)texel, width*height*sizeof(vec4uc),true); } else { // float @@ -86,14 +92,12 @@ namespace ospray { memcpy(texel, (char*)binBasePtr+ofs, sz*sizeof(vec4f)); for (size_t p = 0; p < sz; p++) texel[p].w = 1.0f - texel[p].w; - // txt->texel = texel; txt->texelData = std::make_shared((unsigned char*)texel, width*height*sizeof(vec4f),true); } } else txt->texelData = std::make_shared((unsigned char*)(binBasePtr)+ofs, width*height*3,false); - //(char*)(binBasePtr)+ofs; } @@ -130,20 +134,28 @@ namespace ospray { #define NEXT_TOK strtok(NULL, " \t\n\r") char *s = strtok((char*)value, " \t\n\r"); //TODO: UGLY! Find a better way. - if (!paramType.compare("float")) { - mat->setParam(paramName,(float)atof(s)); + if (paramName.find("map_") != std::string::npos) + { + //TODO: lookup id into textures + int texID = atoi(s); + std::shared_ptr tex = std::dynamic_pointer_cast(mat->textures[texID]); + s = strtok(NULL, " \t\n\r"); + mat->setChild(paramName, tex); + } + else if (!paramType.compare("float")) { + mat->createChildWithValue(paramName,"float",(float)atof(s)); } else if (!paramType.compare("float2")) { float x = atof(s); s = NEXT_TOK; float y = atof(s); - mat->setParam(paramName,vec2f(x,y)); + mat->createChildWithValue(paramName,"vec2f",vec2f(x,y)); } else if (!paramType.compare("float3")) { float x = atof(s); s = NEXT_TOK; float y = atof(s); s = NEXT_TOK; float z = atof(s); - mat->setParam(paramName,vec3f(x,y,z)); + mat->createChildWithValue(paramName,"vec3f",vec3f(x,y,z)); } else if (!paramType.compare("float4")) { float x = atof(s); s = NEXT_TOK; @@ -152,21 +164,21 @@ namespace ospray { float z = atof(s); s = NEXT_TOK; float w = atof(s); - mat->setParam(paramName, vec4f(x,y,z,w)); + mat->createChildWithValue(paramName, "vec4f",vec4f(x,y,z,w)); } else if (!paramType.compare("int")) { - mat->setParam(paramName, (int32_t)atol(s)); + mat->createChildWithValue(paramName, "int",(int32_t)atol(s)); } else if (!paramType.compare("int2")) { int32_t x = atol(s); s = NEXT_TOK; int32_t y = atol(s); - mat->setParam(paramName, vec2i(x,y)); + mat->createChildWithValue(paramName, "vec2i",vec2i(x,y)); } else if (!paramType.compare("int3")) { int32_t x = atol(s); s = NEXT_TOK; int32_t y = atol(s); s = NEXT_TOK; int32_t z = atol(s); - mat->setParam(paramName, vec3i(x,y,z)); + mat->createChildWithValue(paramName, "vec3i",vec3i(x,y,z)); } else if (!paramType.compare("int4")) { int32_t x = atol(s); s = NEXT_TOK; @@ -175,7 +187,7 @@ namespace ospray { int32_t z = atol(s); s = NEXT_TOK; int32_t w = atol(s); - mat->setParam(paramName, vec4i(x,y,z,w)); + mat->createChildWithValue(paramName, "vec4i",vec4i(x,y,z,w)); } else { //error! throw std::runtime_error("unknown parameter type '" + paramType + "' when parsing RIVL materials."); @@ -186,11 +198,10 @@ namespace ospray { void parseMaterialNode(const xml::Node &node) { std::shared_ptr mat = std::make_shared(); - mat->ospMaterial = NULL; nodeList.push_back(mat); - mat->name = node.getProp("name",""); - mat->type = node.getProp("type",""); + mat->setName(node.getProp("name")); + mat->child("type").setValue(node.getProp("type")); xml::for_each_child_of(node,[&](const xml::Node &child){ if (!child.name.compare("textures")) @@ -204,13 +215,17 @@ namespace ospray { { std::shared_ptr child; affine3f xfm; + int id=0; + size_t childID=0; // find child ID xml::for_each_prop(node,[&](const std::string &name, const std::string &value){ if (name == "child") { - size_t childID = atoi(value.c_str());//(char*)value); + childID = atoi(value.c_str()); child = nodeList[childID]; assert(child); + } else if (name == "id") { + id = atoi(value.c_str()); } }); @@ -229,12 +244,23 @@ namespace ospray { &xfm.p.x, &xfm.p.y, &xfm.p.z); - //xmlFree(value); if (numRead != 12) { throw std::runtime_error("invalid number of elements in RIVL transform node"); } - std::shared_ptr xfNode = std::make_shared(xfm,child); + std::stringstream ss; + ss << "transform_" << id; + auto xfNode = createNode(ss.str(), "Transform"); + if (child->type() == "Model") + { + auto instance = createNode(ss.str(), "Instance"); + instance->setChild("model",child); + child->setParent(instance); + child = instance; + } + xfNode->setChild(child->name(), child); + child->setParent(xfNode); + std::static_pointer_cast(xfNode)->baseTransform = xfm; nodeList.push_back(std::dynamic_pointer_cast(xfNode)); } @@ -243,7 +269,16 @@ namespace ospray { void parseMeshNode(const xml::Node &node) { std::shared_ptr mesh = std::make_shared(); - nodeList.push_back(mesh); + std::stringstream ss; + ss << node.getProp("name"); + if (ss.str() == "") + ss << "mesh"; + ss << "_" << node.getProp("id"); + mesh->setName(ss.str()); + mesh->setType("PTMTriangleMesh"); + auto model = createNode ("model_"+ss.str(),"Model"); + model->add(mesh); + nodeList.push_back(model); xml::for_each_child_of(node,[&](const xml::Node &child){ assert(binBasePtr); @@ -254,8 +289,6 @@ namespace ospray { if (!binBasePtr) throw std::runtime_error("xml file mapping to binary file, but binary file not present"); mesh->vertex = make_aligned((char*)binBasePtr+ofs, num); - // mesh->vertex = std::make_shared(make_aligned((char*)binBasePtr+ofs, num), - // num,true); } else if (child.name == "normal") { size_t ofs = std::stoll(child.getProp("ofs")); size_t num = std::stoll(child.getProp("num")); @@ -282,6 +315,8 @@ namespace ospray { = std::dynamic_pointer_cast(nodeList[matID]); assert(mat); mesh->materialList.push_back(mat); + mesh->setChild("material", mat); + mat->setParent(mesh); } free(value); } else { @@ -293,6 +328,10 @@ namespace ospray { void parseGroupNode(const xml::Node &node) { std::shared_ptr group = std::make_shared(); + std::stringstream ss; + ss << "group_" << nodeList.size(); + group->setName(ss.str()); + group->setType("Node"); nodeList.push_back(group); if (node.content == "") // empty group... @@ -303,12 +342,23 @@ namespace ospray { size_t childID = atoi(s); std::shared_ptr child = nodeList[childID]; group->children.push_back(child); + std::stringstream ss; + ss << "child_" << childID; + if (child->type() == "Model") + { + auto instance = createNode(ss.str(), "Instance"); + instance->setChild("model",child); + child->setParent(instance); + child = instance; + } + group->setChild(ss.str(), child); + child->setParent(group); } free(value); } } - void parseBGFscene(std::shared_ptr world, const xml::Node &root) + void parseBGFscene(std::shared_ptr world, const xml::Node &root) { if (root.name != "BGFscene") throw std::runtime_error("XML file is not a RIVL model !?"); @@ -317,7 +367,6 @@ namespace ospray { std::shared_ptr lastNode; xml::for_each_child_of(root,[&](const xml::Node &node){ - PRINT(node.name); if (node.name == "text") { // ------------------------------------------------------- } else if (node.name == "Texture2D") { @@ -331,6 +380,7 @@ namespace ospray { } else if (node.name == "Transform") { // ------------------------------------------------------- parseTransformNode(node); + lastNode = nodeList.back(); // ------------------------------------------------------- } else if (node.name == "Mesh") { // ------------------------------------------------------- @@ -343,13 +393,13 @@ namespace ospray { lastNode = nodeList.back(); } else { nodeList.push_back({}); - //throw std::runtime_error("unknown node type '"+node.name+"' in RIVL model"); } }); world->add(lastNode); } - std::shared_ptr importRIVL(const std::string &fileName) + void importRIVL(std::shared_ptr world, + const std::string &fileName) { string xmlFileName = fileName; string binFileName = fileName+".bin"; @@ -365,10 +415,8 @@ namespace ospray { if (doc->child.size() != 1 || doc->child[0]->name != "BGFscene") throw std::runtime_error("could not parse RIVL file: Not in RIVL format!?"); const xml::Node &root_element = *doc->child[0]; - std::shared_ptr world = std::make_shared(); parseBGFscene(world,root_element); - PRINT(world->getBounds()); - return world; + PRINT(world->bounds()); } } // ::ospray::sg diff --git a/apps/common/sg/importer/Importer.cpp b/apps/common/sg/importer/Importer.cpp index e0c1a64679..61a3e151b0 100644 --- a/apps/common/sg/importer/Importer.cpp +++ b/apps/common/sg/importer/Importer.cpp @@ -16,38 +16,239 @@ // ospray::sg #include "Importer.h" +#include "common/sg/SceneGraph.h" +#include "common/sg/geometry/TriangleMesh.h" +#include "common/miniSG/miniSG.h" /*! \file sg/module/Importer.cpp Defines the interface for writing - file importers for the ospray::sg */ + file importers for the ospray::sg */ namespace ospray { namespace sg { - std::map *importerForExtension = nullptr; + + struct AutoFree + { + AutoFree(void *s) : s(s) {}; + ~AutoFree() { free(s); } + void *s; + }; + + + /*! do the actual parsing, and return a formatURL */ + FormatURL::FormatURL(const std::string &input) + { + char *buffer = strdup(input.c_str()); + AutoFree _buffer(buffer); + + char *urlSep = strstr(buffer,"://"); + if (!urlSep) + throw std::runtime_error("not actually a file format url"); + + *urlSep = 0; + this->formatType = buffer; + + char *fileName = urlSep+3; + char *arg = strtok(fileName,":"); + this->fileName = fileName; + + arg = strtok(NULL,":"); + std::vector args; + while (arg) { + args.push_back(arg); + arg = strtok(NULL,":"); + } + + // now, parse all name:value pairs + for (auto arg_i : args) { + char *s = strdup(arg_i.c_str()); + AutoFree _s(s); + char *name = strtok(s,"="); + char *val = strtok(NULL,"="); + std::pair newArg(name,val?val:""); + this->args.push_back(newArg); + } + } + + /*! returns whether the given argument was specified in the format url */ + bool FormatURL::hasArg(const std::string &name) const + { + for (auto &a : args) + if (a.first == name) return true; + return false; + } + + /*! return value of parameter with given name; returns "" if + parameter wasn't supplied */ + std::string FormatURL::operator[](const std::string &name) const + { + for (auto &a : args) + if (a.first == name) return a.second; + return std::string(""); + } + + /*! return value of parameter with given name; returns "" if + parameter wasn't supplied */ + std::string FormatURL::operator[](const char *name) const + { + return (*this)[std::string(name)]; + } + + + + + // for now, let's hardcode the importers - should be moved to a + // registry at some point ... + void importFileType_points(std::shared_ptr &world, + const FileName &url); + + + + // Helper functions /////////////////////////////////////////////////////// + + static inline void importMiniSg(miniSG::Model &msgModel, + const ospcommon::FileName &fn) + { + if (fn.ext() == "stl") + miniSG::importSTL(msgModel,fn); + else if (fn.ext() == "msg") + miniSG::importMSG(msgModel,fn); + else if (fn.ext() == "tri") + miniSG::importTRI_xyz(msgModel,fn); + else if (fn.ext() == "xyzs") + miniSG::importTRI_xyzs(msgModel,fn); + else if (fn.ext() == "xml") + miniSG::importRIVL(msgModel,fn); + else if (fn.ext() == "hbp") + miniSG::importHBP(msgModel,fn); + else if (fn.ext() == "x3d") + miniSG::importX3D(msgModel,fn); + } + + // Importer definitions /////////////////////////////////////////////////// + + using FileExtToImporterMap = std::map; + + FileExtToImporterMap importerForExtension; /*! declare an importer function for a given file extension */ void declareImporterForFileExtension(const std::string &fileExtension, ImporterFunction importer) { - if (!importerForExtension) - importerForExtension = new std::map; - (*importerForExtension)[fileExtension] = importer; + importerForExtension[fileExtension] = importer; } /*! import a given file. throws a sg::RuntimeError if this could not be done */ void importFile(std::shared_ptr &world, const FileName &fileName) { - if (!importerForExtension) - importerForExtension = new std::map; - ImporterFunction importer = (*importerForExtension)[fileName.ext()]; + ImporterFunction importer = importerForExtension[fileName.ext()]; if (importer) { ImportState state(world); importer(fileName,state); - } else - throw sg::RuntimeError("unknown file format (fileName was '"+fileName.str()+"')"); + } else { + throw sg::RuntimeError("unknown file format (fileName was '" + + fileName.str() + "')"); + } } + + Importer::Importer() + { + createChild("fileName", "string"); + } + + void Importer::setChildrenModified(TimeStamp t) + { + Node::setChildrenModified(t); + ospcommon::FileName fileName(child("fileName").valueAs()); + + if (fileName.str() == loadedFileName) + return; + + std::cout << "attempting importing file: " << fileName.str() << std::endl; + + if (loadedFileName != "" || fileName.str() == "") + return; //TODO: support dynamic re-loading, need to clear children first + + loadedFileName = ""; + + std::shared_ptr wsg(std::dynamic_pointer_cast(shared_from_this())); + +#if 1 + std::shared_ptr fu; + try { + fu = std::make_shared(fileName.c_str()); + } catch (std::runtime_error e) { + /* this failed so this was not a file type url ... */ + fu = nullptr; + } + + if (fu) { + // so this _was_ a file type url + + /* todo: move this code to a registry that automatically + looks up right function based on loaded symbols... */ + if (fu->formatType == "points" || fu->formatType == "spheres") { + importFileType_points(wsg,fileName); + loadedFileName = fileName; + return; + } else + std::cout << "Found a URL-style file type specified, but didn't recognize file type '" << fu->formatType<< "' ... reverting to loading by file extension" << std::endl; + } +#endif + if (fileName.ext() == "obj") { + sg::importOBJ(std::static_pointer_cast(shared_from_this()), fileName); + } else if (fileName.ext() == "ply") { + sg::importPLY(wsg, fileName); + } else if (fileName.ext() == "osg" || fileName.ext() == "osp") { + sg::loadOSP(wsg, fileName); + } else if (fileName.ext() == "xml") { + sg::importRIVL(wsg, fileName); + } else if (fileName.ext() == "x3d" || fileName.ext() == "hbp" || + fileName.ext() == "msg" || fileName.ext() == "stl" || + fileName.ext() == "tri" || fileName.ext() == "xml") { + + miniSG::Model msgModel; + importMiniSg(msgModel, fileName); + + for (auto mesh : msgModel.mesh) { + auto sgMesh = std::dynamic_pointer_cast(createNode(mesh->name, "TriangleMesh")); + + auto vertex = std::make_shared(); + for(size_t i = 0; i < mesh->position.size(); i++) + vertex->push_back(mesh->position[i]); + sgMesh->vertex = vertex; + + auto normal = std::make_shared(); + for(size_t i = 0; i < mesh->normal.size(); i++) + normal->push_back(mesh->normal[i]); + sgMesh->normal = normal; + + auto texcoord = std::make_shared(); + for(size_t i =0; i < mesh->texcoord.size(); i++) + texcoord->push_back(mesh->texcoord[i]); + sgMesh->texcoord = texcoord; + + auto index = std::make_shared(); + for(size_t i =0; i < mesh->triangle.size(); i++) { + index->push_back(vec3i(mesh->triangle[i].v0, + mesh->triangle[i].v1, + mesh->triangle[i].v2)); + } + sgMesh->index = index; + + add(sgMesh); + } + } else { + std::cout << "unsupported file format\n"; + return; + } - } -} + loadedFileName = fileName.str(); + } + + OSP_REGISTER_SG_NODE(Importer); + + }// ::ospray::sg +}// ::ospray diff --git a/apps/common/sg/importer/Importer.h b/apps/common/sg/importer/Importer.h index 6f24998c8a..b2dbf616bc 100644 --- a/apps/common/sg/importer/Importer.h +++ b/apps/common/sg/importer/Importer.h @@ -20,32 +20,68 @@ #include "../common/World.h" #include "ospcommon/FileName.h" -/*! \file sg/module/Importer.h Defines the interface for writing - file importers for the ospray::sg */ - namespace ospray { namespace sg { - struct ImportState { - std::shared_ptr world; - // std::vector searchPaths; + /*! helper class that parses a "points:///tmp/myfile.xyz:format=xyz:whatEver:radius=.3" + file format url specifier into, in this example, + + formatType="points" + fileName="/tmp/myFile.xyz" + args={ {"format","xyz"}, {"whatEver",""}, {"radius",".3"} } + */ + struct FormatURL + { + /*! do the actual parsing, and return a formatURL */ + FormatURL(const std::string &input); + + /*! returns whether the given argument was specified in the format url */ + bool hasArg(const std::string &name) const; + + /*! return value of parameter with given name; returns "" if + parameter wasn't supplied */ + std::string operator[](const std::string &name) const; + + /*! return value of parameter with given name; returns "" if + parameter wasn't supplied */ + std::string operator[](const char *name) const; + + std::string formatType; + std::string fileName; + std::vector> args; + }; + + struct ImportState + { ImportState(std::shared_ptr world) : world(world) {} + + std::shared_ptr world; + }; + + struct Importer : public sg::Renderable + { + Importer(); + + virtual void setChildrenModified(TimeStamp t) override; + + std::string loadedFileName; }; /*! prototype for any scene graph importer function */ - typedef void (*ImporterFunction)(const FileName &fileName, - sg::ImportState &importerState); + using ImporterFunction = void (*)(const FileName &fileName, + sg::ImportState &importerState); /*! declare an importer function for a given file extension */ void declareImporterForFileExtension(const std::string &fileExtension, ImporterFunction importer); + /*! import a given file. throws a sg::RuntimeError if this could not be done */ void importFile(std::shared_ptr &world, const FileName &fileName); - } -} + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/sg/importer/importPoints.cpp b/apps/common/sg/importer/importPoints.cpp new file mode 100644 index 0000000000..d60f9b5808 --- /dev/null +++ b/apps/common/sg/importer/importPoints.cpp @@ -0,0 +1,185 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "Importer.h" +#include "common/sg/SceneGraph.h" +#include + +/*! \file sg/module/Importer.cpp Defines the interface for writing + file importers for the ospray::sg */ + +namespace ospray { + namespace sg { + + struct ColorMap + { + ColorMap(float lo, float hi) + : lo(lo), hi(hi) + { + assert(lo <= hi); + + // TODO: need a better color map here ... + // color.push_back(vec3f(0.f,0.f,0.f)); + // color.push_back(vec3f(0.f,0.f,1.f)); + // color.push_back(vec3f(0.f,1.f,0.f)); + // color.push_back(vec3f(1.f,0.f,0.f)); + // color.push_back(vec3f(1.f,1.f,0.f)); + // color.push_back(vec3f(0.f,1.f,1.f)); + // color.push_back(vec3f(1.f,0.f,1.f)); + // color.push_back(vec3f(1.f,1.f,1.f)); + + // from old qtivewre: "cool to warm" + color.push_back(ospcommon::vec3f(0.231373 , 0.298039 , 0.752941 )); + color.push_back(ospcommon::vec3f(0.865003 , 0.865003 , 0.865003 )); + color.push_back(ospcommon::vec3f(0.705882 , 0.0156863 , 0.14902 )); + } + + vec4f colorFor(float f) + { + if (f <= lo) return vec4f(color.front(),1.f); + if (f >= hi) return vec4f(color.back(),1.f); + + float r = ((f-lo) * (color.size()-1)) / (hi-lo); + int idx = int(r); + if (idx < 0) idx = 0; + if (idx >= color.size()) idx = color.size()-2; + + vec3f c = color[idx] + (r-idx)*(color[idx+1]-color[idx]); + return vec4f(c,1.f); + } + + float lo, hi; + std::vector color; + }; + + bool readOne(FILE *file, float *f, int N, bool ascii) + { + if (!ascii) + return fread(f,sizeof(float),N,file) == N; + + // ascii: + for (int i=0;i &world, + const FileName &url) + { + std::cout << "--------------------------------------------" << std::endl; + std::cout << "#osp.sg: importer for 'points': " << url.str() << std::endl; + + FormatURL fu(url.str()); + FILE *file = fopen(fu.fileName.c_str(),"rb"); + if (!file) + throw std::runtime_error("could not open file "+fu.fileName); + + // read the data vector + std::shared_ptr> sphereData + = std::make_shared>(); + + float radius = .1f; + if (fu.hasArg("radius")) + radius = std::stof(fu["radius"]); + if (radius == 0.f) + throw std::runtime_error("#sg.importPoints: could not parse radius ..."); + + std::string format = "xyz"; + if (fu.hasArg("format")) + format = fu["format"]; + + bool ascii = fu.hasArg("ascii"); + + /* for now, hard-coded sphere componetns to be in float format, + so the number of chars in the format string is the num components */ + int numFloatsPerSphere = format.size(); + size_t xPos = format.find("x"); + size_t yPos = format.find("y"); + size_t zPos = format.find("z"); + size_t rPos = format.find("r"); + size_t sPos = format.find("s"); + + if (xPos == std::string::npos) + throw std::runtime_error("invalid points format: no x component"); + if (yPos == std::string::npos) + throw std::runtime_error("invalid points format: no y component"); + if (zPos == std::string::npos) + throw std::runtime_error("invalid points format: no z component"); + + float * const f = (float *)alloca(sizeof(float)*numFloatsPerSphere); + box3f bounds; + + std::vector mappedScalarVector; + float mappedScalarMin = +std::numeric_limits::infinity(); + float mappedScalarMax = -std::numeric_limits::infinity(); + + while (readOne(file,f,numFloatsPerSphere,ascii)) { + // read one more sphere .... + Spheres::Sphere s; + s.position.x = f[xPos]; + s.position.y = f[yPos]; + s.position.z = f[zPos]; + s.radius + = (rPos == std::string::npos) + ? radius + : f[rPos]; + sphereData->v.push_back(s); + bounds.extend(s.position-s.radius); + bounds.extend(s.position+s.radius); + + if (sPos != std::string::npos) { + mappedScalarVector.push_back(f[sPos]); + mappedScalarMin = std::min(mappedScalarMin,f[sPos]); + mappedScalarMax = std::max(mappedScalarMax,f[sPos]); + } + } + fclose(file); + + // create the node + auto &sphereObject = world->createChild("spheres","Spheres"); + + // iw - note that 'add' sounds wrong here, but that's the way + // the current scene graph works - 'adding' that node (which + // happens to have the right name) will essentially replace the + // old value of that node, and thereby assign the 'data' field + sphereData->setName("sphereData"); + sphereObject.add(sphereData); //["data"]->setValue(data); + + if (!mappedScalarVector.empty()) { + std::cout << "#osp.sg: creating color map for points data ..." + << std::endl; + ColorMap cm(mappedScalarMin,mappedScalarMax); + std::shared_ptr> colorData + = std::make_shared>(); + for (size_t i = 0; i < mappedScalarVector.size(); i++) + colorData->v.push_back(cm.colorFor(mappedScalarVector[i])); + colorData->setName("colorData"); + sphereObject.add(colorData); + } + + std::cout << "#osp.sg: imported " << prettyNumber(sphereData->v.size()) + << " points, bounds = " << bounds << std::endl;; + } + + }// ::ospray::sg +}// ::ospray + + diff --git a/apps/common/sg/module/Module.cpp b/apps/common/sg/module/Module.cpp index 320a2e59b7..d711928265 100644 --- a/apps/common/sg/module/Module.cpp +++ b/apps/common/sg/module/Module.cpp @@ -37,13 +37,14 @@ namespace ospray { alreadyLoaded.insert(moduleName); - const std::string libName = "ospray_sg_"+moduleName; + const std::string libName = "ospray_module_sg_"+moduleName; const std::string symName = "ospray_sg_"+moduleName+"_init"; ospcommon::loadLibrary(libName); void *sym = ospcommon::getSymbol(symName); if (!sym) - throw sg::RuntimeError("could not load module '"+moduleName+"' (symbol '"+symName+"' not found)"); + throw sg::RuntimeError("could not load module '" + moduleName + + "' (symbol '" + symName + "' not found)"); void (*initFct)() = (void (*)())sym; initFct(); diff --git a/apps/common/sg/transferFunction/TransferFunction.cpp b/apps/common/sg/transferFunction/TransferFunction.cpp index b3ff48ac68..360244da10 100644 --- a/apps/common/sg/transferFunction/TransferFunction.cpp +++ b/apps/common/sg/transferFunction/TransferFunction.cpp @@ -20,111 +20,114 @@ namespace ospray { namespace sg { - using std::cout; - using std::endl; //! constructor - TransferFunction::TransferFunction() - : ospTransferFunction(nullptr), - ospColorData(nullptr), - ospAlphaData(nullptr), - valueRange(0.f,1.f), - numSamples(128) + TransferFunction::TransferFunction() { - setDefaultValues(); + setDefaultValues(); + createChild("valueRange", "vec2f", vec2f(0.f,1.f)); + createChild("numSamples", "int", 256); } // //! \brief Sets a new 'texture map' to be used for the color mapping void TransferFunction::setColorMap(const std::vector &colorArray) { - if (ospColorData) { ospRelease(ospColorData); ospColorData = nullptr; } + if (ospColorData) { + ospRelease(ospColorData); + ospColorData = nullptr; + } + this->colorArray.clear(); for (uint32_t i = 0; i < colorArray.size(); ++i) this->colorArray.push_back({i, colorArray[i]}); + + markAsModified(); } //! \brief Sets a new 'texture map' to be used for the alpha mapping void TransferFunction::setAlphaMap(const std::vector &alphaArray) { - if (ospAlphaData) { ospRelease(ospAlphaData); ospAlphaData = nullptr; } + if (ospAlphaData) { + ospRelease(ospAlphaData); + ospAlphaData = nullptr; + } this->alphaArray.clear(); for (const auto &alpha : alphaArray) this->alphaArray.push_back({alpha.x, alpha.y}); + + markAsModified(); } - float TransferFunction::getInterpolatedAlphaValue(float x) + const std::vector> &TransferFunction::alphas() const + { + return alphaArray; + } + + float TransferFunction::interpolatedAlpha(float x) { if (x <= alphaArray.front().first) return alphaArray.front().second; + for (uint32_t i = 1; i < alphaArray.size(); i++) { if (x <= alphaArray[i].first) { - return - alphaArray[i-1].second + - (alphaArray[i].second - alphaArray[i-1].second) - * (x-alphaArray[i-1].first) - / (alphaArray[i].first - alphaArray[i-1].first) - ; + const float t = (x - alphaArray[i - 1].first) + / (alphaArray[i].first - alphaArray[i-1].first); + return (1.0 - t) * alphaArray[i-1].second + t * alphaArray[i].second; } } return alphaArray.back().second; } - void TransferFunction::setValueRange(const vec2f &range) - { - valueRange = range; - lastModified = TimeStamp::now(); + OSPTransferFunction TransferFunction::handle() const + { + return ospTransferFunction; } - //! \brief commit the current field values to ospray - void TransferFunction::commit() + void TransferFunction::preCommit(RenderContext &ctx) { - ospSetVec2f(ospTransferFunction,"valueRange",osp::vec2f{valueRange.x,valueRange.y}); - if (ospColorData == nullptr) { + if (!ospTransferFunction) { + ospTransferFunction = ospNewTransferFunction("piecewise_linear"); + setValue((OSPObject)ospTransferFunction); + } + + vec2f valueRange = child("valueRange").valueAs(); + ospSetVec2f(ospTransferFunction,"valueRange",{valueRange.x,valueRange.y}); + + if (ospColorData == nullptr && colorArray.size()) { // for now, no resampling - just use the colors ... vec3f *colors = (vec3f*)alloca(sizeof(vec3f)*colorArray.size()); for (uint32_t i = 0; i < colorArray.size(); i++) colors[i] = colorArray[i].second; - ospColorData = ospNewData(colorArray.size(),OSP_FLOAT3,colors); + ospColorData = ospNewData(colorArray.size(),OSP_FLOAT3,colors); ospCommit(ospColorData); ospSetData(ospTransferFunction,"colors",ospColorData); - lastModified = TimeStamp::now(); } - if (ospAlphaData == nullptr) { + + if (ospAlphaData == nullptr && alphaArray.size()) { float *alpha = (float*)alloca(sizeof(float)*numSamples); float x0 = alphaArray.front().first; float dx = (alphaArray.back().first - x0) / (numSamples-1); for (int i=0;i lastCommitted) { - lastCommitted = rdtsc(); - ospCommit(ospTransferFunction); } } - void TransferFunction::render(RenderContext &ctx) + void TransferFunction::postCommit(RenderContext &ctx) { - if (!ospTransferFunction) { - ospTransferFunction = ospNewTransferFunction("piecewise_linear"); - } - commit(); + ospCommit(ospTransferFunction); } - void TransferFunction::setFromXML(const xml::Node &node, - const unsigned char *binBasePtr) + + void TransferFunction::setFromXML(const xml::Node& node, + const unsigned char *binBasePtr) { setDefaultValues(); - // const std::string name = node->getProp("name",""); - // if (name != "") - // registerNamedNode(name,this); - xml::for_each_child_of(node,[&](const xml::Node &child) { // ------------------------------------------------------- // colors @@ -133,13 +136,15 @@ namespace ospray { colorArray.clear(); char *cont = strdup(child.content.c_str()); assert(cont); - + const char *c = strtok(cont,",\n"); while (c) { - colorArray.push_back(std::pair(colorArray.size(),toVec3f(c))); + colorArray.push_back( + std::pair(colorArray.size(),toVec3f(c)) + ); c = strtok(nullptr,",\n"); } - + free(cont); } @@ -162,8 +167,8 @@ namespace ospray { } }); } - - //! \brief Initialize this node's value from given corresponding XML node + + //! \brief Initialize this node's value from given corresponding XML node void TransferFunction::setDefaultValues() { static float col[7][3] = {{0 , 0 , 0.562493 }, @@ -175,14 +180,15 @@ namespace ospray { {0.500008 , 0 , 0 }}; colorArray.clear(); - for (int i=0;i<7;i++) - colorArray.push_back(std::pair(i,vec3f(col[i][0],col[i][1],col[i][2]))); - + for (int i = 0; i < 7; i++) { + colorArray.push_back( + std::pair(i, vec3f(col[i][0], col[i][1], col[i][2])) + ); + } + alphaArray.clear(); alphaArray.push_back(std::pair(0.f,0.f)); alphaArray.push_back(std::pair(1.f,1.f)); - // for (int i=0;i(i,1.f)); //i/float(colorArray.size()-1)); } std::string TransferFunction::toString() const @@ -190,6 +196,7 @@ namespace ospray { return "ospray::sg::TransferFunction"; } - OSP_REGISTER_SG_NODE(TransferFunction) + OSP_REGISTER_SG_NODE(TransferFunction); + } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/transferFunction/TransferFunction.h b/apps/common/sg/transferFunction/TransferFunction.h index d61a28f82e..cec2b786ee 100644 --- a/apps/common/sg/transferFunction/TransferFunction.h +++ b/apps/common/sg/transferFunction/TransferFunction.h @@ -25,56 +25,53 @@ namespace ospray { uniformly spaced color and alpha values between which the value will be linearly interpolated (similar to a 1D texture for each) */ - struct TransferFunction : public sg::Node + struct OSPSG_INTERFACE TransferFunction : public sg::Node { - //! constructor TransferFunction(); //! \brief initialize color and alpha arrays to 'some' useful values void setDefaultValues(); /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; + std::string toString() const override; - //! \brief creates ospray-side object(s) for this node - virtual void render(RenderContext &ctx) override; + void preCommit(RenderContext &ctx) override; + void postCommit(RenderContext &ctx) override; //! \brief Initialize this node's value from given corresponding XML node - virtual void setFromXML(const xml::Node &node, - const unsigned char *binBasePtr) override; - virtual void commit(); - - void setValueRange(const vec2f &range); + void setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) override; - // /*! set a new color map array (using array of uniformly samples colors) */ + /*! set a new color map array (using array of uniformly samples colors) */ void setColorMap(const std::vector &colorArray); - /*! set a new alpha map array - x coordinate is point pos, y is point alpha value */ + /*! set a new alpha map array - x coordinate is point pos, + y is point alpha value */ void setAlphaMap(const std::vector &alphaArray); - const std::vector > &getAlphaArray() const - { return alphaArray; } + const std::vector> &alphas() const; - float getInterpolatedAlphaValue(float x); + float interpolatedAlpha(float x); /*! return the ospray handle for this xfer fct, so we can assign it to ospray obejcts that need a reference to the ospray version of this xf */ - OSPTransferFunction getOSPHandle() const { return ospTransferFunction; } + OSP_DEPRECATED OSPTransferFunction handle() const; + protected: - OSPTransferFunction ospTransferFunction; - OSPData ospColorData; - OSPData ospAlphaData; - vec2f valueRange; + + OSPTransferFunction ospTransferFunction {nullptr}; + OSPData ospColorData {nullptr}; + OSPData ospAlphaData {nullptr}; // number of samples we'll use in the colordata and alphadata arrays - int numSamples; + int numSamples {256}; // array of (x,color(x)) color samples; the first and last x // determine the range of x'es, all values will be resampled // uniformly into this range. samples must be sorted by x // coordinate, and must span a non-empty range of x coordinates - std::vector > colorArray; + std::vector> colorArray; // array of (x,alpha(x)) opacity samples; otherwise same as colorArray - std::vector > alphaArray; + std::vector> alphaArray; }; } // ::ospray::sg diff --git a/apps/common/sg/volume/Volume.cpp b/apps/common/sg/volume/Volume.cpp index e50bff7f2d..d04d689a0d 100644 --- a/apps/common/sg/volume/Volume.cpp +++ b/apps/common/sg/volume/Volume.cpp @@ -14,13 +14,89 @@ // limitations under the License. // // ======================================================================== // +#include +#include +#include +#include #include "Volume.h" #include "sg/common/World.h" -#include "sg/common/Integrator.h" namespace ospray { namespace sg { + static vec3i checkForAndEnableDistributedVolumes() + { + auto dpFromEnv = getEnvVar("OSPRAY_DATA_PARALLEL"); + + if (dpFromEnv.first) { + // Create the OSPRay object. + vec3i blockDims; + int rc = sscanf(dpFromEnv.second.c_str(), "%dx%dx%d", + &blockDims.x, &blockDims.y, &blockDims.z); + if (rc != 3) { + throw std::runtime_error("could not parse OSPRAY_DATA_PARALLEL " + "env-var. Must be of format xx<>Z " + "(e.g., '4x4x4'"); + } + Volume::useDataDistributedVolume = true; + return blockDims; + } else { + return {0, 0, 0,}; + } + } + + /*! helper function to help build voxel ranges during parsing */ + template + inline void extendVoxelRange(ospcommon::vec2f &voxelRange, + const T *voxel, size_t num) + { + for (size_t i = 0; i < num; ++i) { + voxelRange.x = std::min(voxelRange.x, static_cast(voxel[i])); + voxelRange.y = std::max(voxelRange.y, static_cast(voxel[i])); + } + } + + //! Convenient wrapper that will do the template dispatch for you based on + // the voxelType passed + inline void extendVoxelRange(ospcommon::vec2f &voxelRange, + const OSPDataType voxelType, + const unsigned char *voxels, + const size_t numVoxels) + { + switch (voxelType) { + case OSP_UCHAR: + extendVoxelRange(voxelRange, voxels, numVoxels); + break; + case OSP_SHORT: + extendVoxelRange(voxelRange, + reinterpret_cast(voxels), + numVoxels); + break; + case OSP_USHORT: + extendVoxelRange(voxelRange, + reinterpret_cast(voxels), + numVoxels); + break; + case OSP_FLOAT: + extendVoxelRange(voxelRange, + reinterpret_cast(voxels), + numVoxels); + break; + case OSP_DOUBLE: + extendVoxelRange(voxelRange, + reinterpret_cast(voxels), + numVoxels); + break; + default: + throw std::runtime_error("sg::extendVoxelRange: unsupported voxel type!"); + } + } + + bool unsupportedVoxelType(const std::string &type) { + return type != "uchar" && type != "ushort" && type != "short" + && type != "float" && type != "double"; + } + // ======================================================= // base volume class // ======================================================= @@ -28,35 +104,76 @@ namespace ospray { bool Volume::useDataDistributedVolume = false; /*! \brief returns a std::string with the c++ name of this class */ + Volume::Volume() + { + createChild("transferFunction", "TransferFunction"); + createChild("gradientShadingEnabled", "bool", true); + createChild("preIntegration", "bool", true); + createChild("singleShade", "bool", true); + createChild("voxelRange", "vec2f", + vec2f(std::numeric_limits::infinity(), + -std::numeric_limits::infinity())); + createChild("adaptiveSampling", "bool", true); + createChild("adaptiveScalar", "float", 15.f); + createChild("adaptiveBacktrack", "float", 0.03f); + createChild("samplingRate", "float", 0.125f); + createChild("adaptiveMaxSamplingRate", "float", 2.f); + createChild("volumeClippingBoxLower", "vec3f", vec3f(0.f)); + createChild("volumeClippingBoxUpper", "vec3f", vec3f(0.f)); + createChild("specular", "vec3f", vec3f(0.3f)); + createChild("gridOrigin", "vec3f", vec3f(0.0f)); + createChild("gridSpacing", "vec3f", vec3f(1.f)); + createChild("isosurfaceEnabled", "bool", false); + createChild("isosurface", "float", + -std::numeric_limits::infinity(), + NodeFlags::valid_min_max | + NodeFlags::gui_slider).setMinMax(0.f,255.f); + } + std::string Volume::toString() const - { return "ospray::sg::Volume"; } + { + return "ospray::sg::Volume"; + } void Volume::serialize(sg::Serialization::State &state) { Node::serialize(state); - if (transferFunction) - transferFunction->serialize(state); } - + + void Volume::preRender(RenderContext &ctx) + { + if (volume) { + ospAddVolume(ctx.world->ospModel,volume); + if (child("isosurfaceEnabled").valueAs() == true + && isosurfacesGeometry) + ospAddGeometry(ctx.world->ospModel, isosurfacesGeometry); + } + } + // ======================================================= // structured volume class // ======================================================= //! constructor StructuredVolume::StructuredVolume() - : dimensions(-1), voxelType(""), - mappedPointer(nullptr) - {} + : dimensions(-1), voxelType(""), mappedPointer(nullptr) + { + } /*! \brief returns a std::string with the c++ name of this class */ std::string StructuredVolume::toString() const - { return "ospray::sg::StructuredVolume"; } - + { + return "ospray::sg::StructuredVolume"; + } + //! return bounding box of all primitives - box3f StructuredVolume::getBounds() - { return box3f(vec3f(0.f),vec3f(getDimensions())); } + box3f StructuredVolume::bounds() const + { + return {vec3f(0.f), + vec3f(getDimensions())*child("gridSpacing").valueAs()}; + } - //! \brief Initialize this node's value from given XML node + //! \brief Initialize this node's value from given XML node void StructuredVolume::setFromXML(const xml::Node &node, const unsigned char *binBasePtr) @@ -69,53 +186,22 @@ namespace ospray { mappedPointer = binBasePtr + std::stoll(node.getProp("ofs","0")); dimensions = toVec3i(node.getProp("dimensions").c_str()); - if (voxelType != "float" && voxelType != "uint8") - throw std::runtime_error("unknown StructuredVolume.voxelType (currently only supporting 'float' and 'uint8')"); - - if (!transferFunction) - setTransferFunction(std::make_shared()); - - std::cout << "#osp:sg: created StructuredVolume from XML file, dimensions = " - << getDimensions() << std::endl; - } - - /*! \brief 'render' the object to ospray */ - void StructuredVolume::render(RenderContext &ctx) - { - if (volume) return; - - if (dimensions.x <= 0 || dimensions.y <= 0 || dimensions.z <= 0) - throw std::runtime_error("StructuredVolume::render(): invalid volume dimensions"); - - volume = ospNewVolume(useDataDistributedVolume - ? "data_distributed_volume" - : "block_bricked_volume"); - if (!volume) - THROW_SG_ERROR("could not allocate volume"); - - ospSetString(volume,"voxelType",voxelType.c_str()); - ospSetVec3i(volume,"dimensions",(const osp::vec3i&)dimensions); - size_t nPerSlice = (size_t)dimensions.x*(size_t)dimensions.y; - assert(mappedPointer != nullptr); - - for (int z=0;zrender(ctx); + std::cout << "#osp:sg: created StructuredVolume from XML file, " + << "dimensions = " << getDimensions() << std::endl; + } - ospSetObject(volume,"transferFunction",transferFunction->getOSPHandle()); - ospCommit(volume); - ospAddVolume(ctx.world->ospModel,volume); + void StructuredVolume::postCommit(RenderContext &ctx) + { } OSP_REGISTER_SG_NODE(StructuredVolume); - // ======================================================= // structured volume that is stored in a separate file (ie, a file // other than the ospbin file) @@ -128,13 +214,18 @@ namespace ospray { /*! \brief returns a std::string with the c++ name of this class */ std::string StructuredVolumeFromFile::toString() const - { return "ospray::sg::StructuredVolumeFromFile"; } - + { + return "ospray::sg::StructuredVolumeFromFile"; + } + //! return bounding box of all primitives - box3f StructuredVolumeFromFile::getBounds() - { return box3f(vec3f(0.f),vec3f(getDimensions())); } + box3f StructuredVolumeFromFile::bounds() const + { + return {vec3f(0.f), + vec3f(getDimensions())*child("gridSpacing").valueAs()}; + } - //! \brief Initialize this node's value from given XML node + //! \brief Initialize this node's value from given XML node void StructuredVolumeFromFile::setFromXML(const xml::Node &node, const unsigned char *binBasePtr) { @@ -142,121 +233,138 @@ namespace ospray { if (voxelType == "uint8") voxelType = "uchar"; dimensions = toVec3i(node.getProp("dimensions").c_str()); fileName = node.getProp("fileName"); - if (fileName == "") throw std::runtime_error("sg::StructuredVolumeFromFile: no 'fileName' specified"); - fileNameOfCorrespondingXmlDoc = node.doc->fileName; - - if (voxelType != "float" && voxelType != "uchar") - throw std::runtime_error("unknown StructuredVolume.voxelType (currently only supporting 'float')"); - - if (!transferFunction) { - const std::string xfName = node.getProp("transferFunction"); - std::shared_ptr xf = std::dynamic_pointer_cast(findNamedNode(xfName)); - if (xf) - setTransferFunction(xf); + if (fileName.empty()) { + throw std::runtime_error("sg::StructuredVolumeFromFile: " + "no 'fileName' specified"); + } + if (unsupportedVoxelType(voxelType)) { + THROW_SG_ERROR("unknown StructuredVolume.voxelType '" + voxelType + "'"); } - if (!transferFunction) - setTransferFunction(std::make_shared()); - - std::cout << "#osp:sg: created StructuredVolume from XML file, dimensions = " - << getDimensions() << std::endl; + + fileNameOfCorrespondingXmlDoc = node.doc->fileName; + + std::cout << "#osp:sg: created StructuredVolume from XML file, " + << "dimensions = " << getDimensions() << std::endl; } - - /*! \brief 'render' the object to ospray */ - void StructuredVolumeFromFile::render(RenderContext &ctx) + + void StructuredVolumeFromFile::preCommit(RenderContext &ctx) { - if (volume) return; - - if (dimensions.x <= 0 || dimensions.y <= 0 || dimensions.z <= 0) - throw std::runtime_error("StructuredVolume::render(): invalid volume dimensions"); + if (volume) { + ospCommit(volume); + if (child("isosurfaceEnabled").valueAs() == true + && isosurfacesGeometry) { + OSPData isovaluesData = ospNewData(1, OSP_FLOAT, + &child("isosurface").valueAs()); + ospSetData(isosurfacesGeometry, "isovalues", isovaluesData); + ospCommit(isosurfacesGeometry); + } + return; + } + + if (dimensions.x <= 0 || dimensions.y <= 0 || dimensions.z <= 0) { + throw std::runtime_error("StructuredVolume::render(): " + "invalid volume dimensions"); + } + + vec3i dataDistributedBlocks = checkForAndEnableDistributedVolumes(); bool useBlockBricked = 1; - if (useDataDistributedVolume) + if (useDataDistributedVolume) { volume = ospNewVolume("data_distributed_volume"); + ospSetVec3i(volume,"num_dp_blocks",(osp::vec3i&)dataDistributedBlocks); + } else - volume = ospNewVolume(useBlockBricked ? "block_bricked_volume" : "shared_structured_volume"); - if (!volume) - THROW_SG_ERROR("could not allocate volume"); - + volume = ospNewVolume(useBlockBricked ? "block_bricked_volume" : + "shared_structured_volume"); + + if (!volume) THROW_SG_ERROR("could not allocate volume"); + + isosurfacesGeometry = ospNewGeometry("isosurfaces"); + ospSetObject(isosurfacesGeometry, "volume", volume); + + setValue((OSPObject)volume); + ospSetString(volume,"voxelType",voxelType.c_str()); ospSetVec3i(volume,"dimensions",(const osp::vec3i&)dimensions); - + FileName realFileName = fileNameOfCorrespondingXmlDoc.path()+fileName; FILE *file = fopen(realFileName.c_str(),"rb"); - if (!file) + if (!file) throw std::runtime_error("StructuredVolumeFromFile::render(): could not open file '" +realFileName.str()+"' (expanded from xml file '" +fileNameOfCorrespondingXmlDoc.str() +"' and file name '"+fileName+"')"); + vec2f voxelRange(std::numeric_limits::infinity(), + -std::numeric_limits::infinity()); + const OSPDataType ospVoxelType = typeForString(voxelType); + const size_t voxelSize = sizeOf(ospVoxelType); if (useBlockBricked || useDataDistributedVolume) { - size_t nPerSlice = (size_t)dimensions.x * (size_t)dimensions.y; - if (voxelType == "float") { - float *slice = new float[nPerSlice]; - for (int z=0;z slice(nPerSlice * voxelSize, 0); + + for (int z = 0; z < dimensions.z; ++z) { + if (fread(slice.data(), voxelSize, nPerSlice, file) != nPerSlice) { + throw std::runtime_error("StructuredVolume::render(): read incomplete slice " + "data ... partial file or wrong format!?"); } - delete[] slice; + const vec3i region_lo(0, 0, z); + const vec3i region_sz(dimensions.x, dimensions.y, 1); + extendVoxelRange(voxelRange, ospVoxelType, slice.data(), nPerSlice); + ospSetRegion(volume, slice.data(), (const osp::vec3i&)region_lo, (const osp::vec3i&)region_sz); } } else { - size_t nVoxels = (size_t)dimensions.x * (size_t)dimensions.y * (size_t)dimensions.z; - float *voxels = new float[nVoxels]; - size_t nRead = fread(voxels,sizeof(float),nVoxels,file); - if (nRead != nVoxels) + const size_t nVoxels = (size_t)dimensions.x * (size_t)dimensions.y * (size_t)dimensions.z; + uint8_t *voxels = new uint8_t[nVoxels * voxelSize]; + if (fread(voxels, voxelSize, nVoxels, file) != nVoxels) { THROW_SG_ERROR("read incomplete data (truncated file or wrong format?!)"); - OSPData data = ospNewData(nVoxels,OSP_FLOAT,voxels,OSP_DATA_SHARED_BUFFER); + } + extendVoxelRange(voxelRange, ospVoxelType, voxels, nVoxels); + OSPData data = ospNewData(nVoxels, ospVoxelType, voxels, OSP_DATA_SHARED_BUFFER); ospSetData(volume,"voxelData",data); } fclose(file); - transferFunction->render(ctx); + child("voxelRange").setValue(voxelRange); + child("isosurface").setMinMax(voxelRange.x, voxelRange.y); + float iso = child("isosurface").valueAs(); + if (iso < voxelRange.x || iso > voxelRange.y) + child("isosurface").setValue((voxelRange.y-voxelRange.x)/2.f); + child("transferFunction")["valueRange"].setValue(voxelRange); + } - ospSetObject(volume,"transferFunction",transferFunction->getOSPHandle()); + void StructuredVolumeFromFile::postCommit(RenderContext &ctx) + { + ospSetObject(volume,"transferFunction", + child("transferFunction").valueAs()); ospCommit(volume); - ospAddVolume(ctx.world->ospModel,volume); } OSP_REGISTER_SG_NODE(StructuredVolumeFromFile); - - - - // ======================================================= // stacked slices volume class // ======================================================= - //! constructor StackedRawSlices::StackedRawSlices() : baseName(""), voxelType("uint8_t"), dimensions(-1) {} /*! \brief returns a std::string with the c++ name of this class */ std::string StackedRawSlices::toString() const - { return "ospray::sg::StackedRawSlices"; } - + { + return "ospray::sg::StackedRawSlices"; + } + //! return bounding box of all primitives - box3f StackedRawSlices::getBounds() - { return box3f(vec3f(0.f),vec3f(getDimensions())); } + box3f StackedRawSlices::bounds() const + { + return box3f(vec3f(0.f),vec3f(getDimensions())); + } - //! \brief Initialize this node's value from given XML node + //! \brief Initialize this node's value from given XML node void StackedRawSlices::setFromXML(const xml::Node &node, const unsigned char *binBasePtr) { @@ -265,61 +373,241 @@ namespace ospray { baseName = node.getProp("baseName"); firstSliceID = std::stoll(node.getProp("firstSliceID","0")); numSlices = std::stoll(node.getProp("numSlices")); - - if (voxelType != "uint8_t") - throw std::runtime_error("unknown StackedRawSlices.voxelType (currently only supporting 'uint8_t')"); - - if (!transferFunction) - setTransferFunction(std::make_shared()); + + if (voxelType != "uint8_t") { + throw std::runtime_error("unknown StackedRawSlices.voxelType " + "(currently only supporting 'uint8_t')"); + } } - - /*! \brief 'render' the object to ospray */ - void StackedRawSlices::render(RenderContext &ctx) + + OSP_REGISTER_SG_NODE(StackedRawSlices); + + // ======================================================= + // Richtmyer-Meshkov volume class and utils + // ======================================================= + + RichtmyerMeshkov::RichtmyerMeshkov() + : dimensions(2048, 2048, 1920) + {} + + std::string RichtmyerMeshkov::toString() const + { + return "ospray::sg::RichtmyerMeshkov"; + } + + //! return bounding box of all primitives + box3f RichtmyerMeshkov::bounds() const { - if (volume) return; + return box3f(vec3f(0.f), vec3f(getDimensions())); + } - dimensions.x = sliceResolution.x; - dimensions.y = sliceResolution.y; - dimensions.z = numSlices; + //! \brief Initialize this node's value from given XML node + void RichtmyerMeshkov::setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) + { + dirName = node.getProp("dirName"); + const std::string t = node.getProp("timeStep"); + if (t.empty()) { + THROW_SG_ERROR("sg::RichtmyerMeshkov: no 'timeStep' specified"); + } + timeStep = std::stoi(t); - if (dimensions.x <= 0 || dimensions.y <= 0 || dimensions.z <= 0) - throw std::runtime_error("StackedRawSlices::render(): invalid volume dimensions"); - - volume = ospNewVolume("block_bricked_volume"); - if (!volume) + if (dirName.empty()) { + THROW_SG_ERROR("sg::RichtmyerMeshkov: no 'dirName' specified"); + } + + fileNameOfCorrespondingXmlDoc = node.doc->fileName; + } + + void RichtmyerMeshkov::preCommit(RenderContext &ctx) + { + if (volume) { + ospCommit(volume); + if (child("isosurfaceEnabled").valueAs() == true + && isosurfacesGeometry) { + OSPData isovaluesData = ospNewData(1, OSP_FLOAT, + &child("isosurface").valueAs()); + ospSetData(isosurfacesGeometry, "isovalues", isovaluesData); + ospCommit(isosurfacesGeometry); + } + return; + } + + const vec3i dataDistributedBlocks = checkForAndEnableDistributedVolumes(); + + if (useDataDistributedVolume) { + volume = ospNewVolume("data_distributed_volume"); + ospSetVec3i(volume, "num_dp_blocks", (osp::vec3i&)dataDistributedBlocks); + } + else { + volume = ospNewVolume("block_bricked_volume"); + } + + if (!volume) { THROW_SG_ERROR("could not allocate volume"); + } - ospSetString(volume,"voxelType",voxelType.c_str()); - ospSetVec3i(volume,"dimensions",(const osp::vec3i&)dimensions); - size_t nPerSlice = dimensions.x*dimensions.y; - uint8_t *slice = new uint8_t[nPerSlice]; - for (int sliceID=0;sliceID threads; + createChild("blocksLoaded", "string", "0/" + std::to_string(LoaderState::NUM_BLOCKS)); + + for (size_t i = 0; i < std::thread::hardware_concurrency(); ++i) { + threads.push_back(std::thread([&](){ loaderThread(loaderState); })); + } + for (auto &t : threads) { + t.join(); } - delete[] slice; - transferFunction->render(ctx); + child("voxelRange").setValue(loaderState.voxelRange); + child("isosurface").setMinMax(loaderState.voxelRange.x, + loaderState.voxelRange.y); + float iso = child("isosurface").valueAs(); + if (iso < loaderState.voxelRange.x || iso > loaderState.voxelRange.y) { + child("isosurface").setValue((loaderState.voxelRange.y - loaderState.voxelRange.x) / 2.f); + } + child("transferFunction")["valueRange"].setValue(loaderState.voxelRange); + child("transferFunction").preCommit(ctx); + ospSetObject(volume,"transferFunction", + child("transferFunction").valueAs()); + ospCommit(volume); + } - ospSetObject(volume,"transferFunction",transferFunction->getOSPHandle()); + void RichtmyerMeshkov::postCommit(RenderContext &ctx) + { + // In StructuredVolumeFromFile it does this at the end + // of pre-commit as well, but shouldn't that not be needed? Since + // it will be done in postCommit which is called immediately after? + ospSetObject(volume,"transferFunction", + child("transferFunction").valueAs()); ospCommit(volume); - ospAddVolume(ctx.world->ospModel,volume); } - OSP_REGISTER_SG_NODE(StackedRawSlices); + void RichtmyerMeshkov::loaderThread(LoaderState &state) + { + Node &progressLog = child("blocksLoaded"); + std::vector block(LoaderState::BLOCK_SIZE, 0); + while (true) { + const size_t blockID = state.loadNextBlock(block); + if (blockID >= LoaderState::NUM_BLOCKS) { + break; + } + + const int I = blockID % 8; + const int J = (blockID / 8) % 8; + const int K = (blockID / 64); + + vec2f blockRange(block[0]); + extendVoxelRange(blockRange, &block[0], LoaderState::BLOCK_SIZE); + const vec3i region_lo(I * 256, J * 256, K * 128); + const vec3i region_sz(256, 256, 128); + { + std::lock_guard lock(state.mutex); + ospSetRegion(volume, block.data(), (const osp::vec3i&)region_lo, + (const osp::vec3i&)region_sz); + + state.voxelRange.x = std::min(state.voxelRange.x, blockRange.x); + state.voxelRange.y = std::max(state.voxelRange.y, blockRange.y); + progressLog.setValue(std::to_string(blockID + 1) + "/" + + std::to_string(LoaderState::NUM_BLOCKS)); + } + } + } + + const size_t RichtmyerMeshkov::LoaderState::BLOCK_SIZE = 256 * 256 * 128; + const size_t RichtmyerMeshkov::LoaderState::NUM_BLOCKS = 8 * 8 * 15; + + RichtmyerMeshkov::LoaderState::LoaderState(const FileName &fullDirName, + const int timeStep) + : nextBlockID(0), + useGZip(getenv("OSPRAY_RM_NO_GZIP") == nullptr), + fullDirName(fullDirName), + timeStep(timeStep), + voxelRange(std::numeric_limits::infinity(), + -std::numeric_limits::infinity()) + {} + + size_t RichtmyerMeshkov::LoaderState::loadNextBlock(std::vector &b) + { + const size_t blockID = nextBlockID.fetch_add(1); + if (blockID >= NUM_BLOCKS) { + return blockID; + } + + // The RM Bob filenames are at most 15 characters long, + // in the case of gzipped ones + char bobName[16] = {0}; + FILE *file = NULL; + if (useGZip) { +#ifndef _WIN32 + if (std::snprintf(bobName, 16, "d_%04d_%04li.gz", timeStep, blockID) > 15) { + THROW_SG_ERROR("sg::RichtmyerMeshkov: Invalid timestep or blockID!"); + } + const FileName fileName = fullDirName + FileName(bobName); + const std::string cmd = "gunzip -c " + std::string(fileName); + file = popen(cmd.c_str(), "r"); + if (!file) { + THROW_SG_ERROR("sg::RichtmyerMeshkov: could not open popen '" + + cmd + "'"); + } +#else + THROW_SG_ERROR("sg::RichtmyerMeshkov: gzipped RM bob's" + " aren't supported on Windows!"); +#endif + } else { +#ifdef _WIN32 + if (_snprintf_s(bobName, 16, 16, "d_%04d_%04li", timeStep, blockID) > 15) { +#else + if (std::snprintf(bobName, 16, "d_%04d_%04li", timeStep, blockID) > 15) { +#endif + THROW_SG_ERROR("sg::RichtmyerMeshkov: Invalid timestep or blockID!"); + } + const FileName fileName = fullDirName + FileName(bobName); + file = fopen(fileName.c_str(), "rb"); + if (!file) { + THROW_SG_ERROR("sg::RichtmyerMeshkov: could not open file '" + + std::string(fileName) + "'"); + } + } + + if (b.size() < LoaderState::BLOCK_SIZE) { + b.resize(LoaderState::BLOCK_SIZE); + } + + if (std::fread(b.data(), LoaderState::BLOCK_SIZE, 1, file) != 1) { + if (useGZip) { +#ifndef _WIN32 + pclose(file); +#endif + } else { + fclose(file); + } + THROW_SG_ERROR("sg::RichtmyerMeshkov: failed to read data from bob " + + std::string(bobName)); + } + + if (useGZip) { +#ifndef _WIN32 + pclose(file); +#endif + } else { + fclose(file); + } + + return blockID; + } + + OSP_REGISTER_SG_NODE(RichtmyerMeshkov); } // ::ospray::sg } // ::ospray diff --git a/apps/common/sg/volume/Volume.h b/apps/common/sg/volume/Volume.h index b56915408b..7187dec255 100644 --- a/apps/common/sg/volume/Volume.h +++ b/apps/common/sg/volume/Volume.h @@ -22,110 +22,156 @@ namespace ospray { namespace sg { - /*! a geometry node - the generic geometry node */ - struct Volume : public sg::Node { - Volume() : volume(nullptr) {}; + struct Volume : public sg::Renderable + { + Volume(); - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; + virtual std::string toString() const override; //! return bounding box of all primitives - virtual box3f getBounds() override = 0; + virtual box3f bounds() const override = 0; - //! serialize into given serialization state + //! serialize into given serialization state virtual void serialize(sg::Serialization::State &state) override; + virtual void preRender(RenderContext &ctx) override; + static bool useDataDistributedVolume; - SG_NODE_DECLARE_MEMBER(std::shared_ptr,transferFunction,TransferFunction); - //! ospray volume object handle - public: OSPVolume volume; + OSPVolume volume {nullptr}; + OSPGeometry isosurfacesGeometry{nullptr}; }; /*! a plain old structured volume */ - struct StructuredVolume : public Volume { - //! constructor + struct StructuredVolume : public Volume + { StructuredVolume(); - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; + std::string toString() const override; //! return bounding box of all primitives - virtual box3f getBounds() override; + box3f bounds() const override; - //! \brief Initialize this node's value from given XML node - virtual void setFromXML(const xml::Node &node, - const unsigned char *binBasePtr) override; + //! \brief Initialize this node's value from given XML node + void setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) override; - /*! \brief 'render' the object to ospray */ - virtual void render(RenderContext &ctx) override; + void postCommit(RenderContext &ctx) override; - SG_NODE_DECLARE_MEMBER(vec3i,dimensions,Dimensions) - SG_NODE_DECLARE_MEMBER(std::string,voxelType,ScalarType) + SG_NODE_DECLARE_MEMBER(vec3i, dimensions, Dimensions); + SG_NODE_DECLARE_MEMBER(std::string, voxelType, ScalarType); const unsigned char *mappedPointer; }; /*! a plain old structured volume */ - struct StructuredVolumeFromFile : public Volume { - //! constructor + struct StructuredVolumeFromFile : public Volume + { StructuredVolumeFromFile(); - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; + std::string toString() const override; //! return bounding box of all primitives - virtual box3f getBounds() override; + box3f bounds() const override; - //! \brief Initialize this node's value from given XML node - virtual void setFromXML(const xml::Node &node, - const unsigned char *binBasePtr) override; + //! \brief Initialize this node's value from given XML node + void setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) override; - /*! \brief 'render' the object to ospray */ - virtual void render(RenderContext &ctx) override; + void preCommit(RenderContext &ctx) override; + void postCommit(RenderContext &ctx) override; - SG_NODE_DECLARE_MEMBER(vec3i,dimensions,Dimensions); - SG_NODE_DECLARE_MEMBER(std::string,fileName,FileName); - SG_NODE_DECLARE_MEMBER(std::string,voxelType,ScalarType); + + SG_NODE_DECLARE_MEMBER(vec3i, dimensions, Dimensions); + SG_NODE_DECLARE_MEMBER(std::string, fileName, FileName); + SG_NODE_DECLARE_MEMBER(std::string, voxelType, ScalarType); public: - //! \brief file name of the xml doc when the node was loaded/parsed from xml + //! \brief file name of the xml doc when the node was loaded from xml /*! \detailed we need this to properly resolve relative file names */ FileName fileNameOfCorrespondingXmlDoc; - }; /*! a structured volume whose input comes from a set of stacked RAW files */ - struct StackedRawSlices : public Volume { - + struct StackedRawSlices : public Volume + { StackedRawSlices(); - /*! \brief returns a std::string with the c++ name of this class */ - virtual std::string toString() const override; + std::string toString() const override; //! return bounding box of all primitives - virtual box3f getBounds() override; + box3f bounds() const override; - //! \brief Initialize this node's value from given XML node - virtual void setFromXML(const xml::Node &node, + //! \brief Initialize this node's value from given XML node + void setFromXML(const xml::Node &node, const unsigned char *binBasePtr) override; - /*! \brief 'render' the object to ospray */ - virtual void render(RenderContext &ctx) override; - /*! resolution (X x Y) of each slice */ - SG_NODE_DECLARE_MEMBER(vec2i,sliceResolution,SliceResolution); - /*! base path name for the slices, in "printf format" (e.g., "/mydir/slice%04i.raw") */ - SG_NODE_DECLARE_MEMBER(std::string,baseName,BaseName); - SG_NODE_DECLARE_MEMBER(int32_t,firstSliceID,FirstSliceID); - SG_NODE_DECLARE_MEMBER(int32_t,numSlices,numSlices); - SG_NODE_DECLARE_MEMBER(std::string,voxelType,ScalarType); - - //! actual dimensions after the data is loaded in - to be computed from sliceResolutiona nd numSlices - SG_NODE_DECLARE_MEMBER(vec3i,dimensions,Dimensions); + SG_NODE_DECLARE_MEMBER(vec2i, sliceResolution, SliceResolution); + + /*! base path name for the slices, in "printf format" + * (e.g., "/mydir/slice%04i.raw") + */ + SG_NODE_DECLARE_MEMBER(std::string, baseName, BaseName); + SG_NODE_DECLARE_MEMBER(int32_t, firstSliceID, FirstSliceID); + SG_NODE_DECLARE_MEMBER(int32_t, numSlices, numSlices); + SG_NODE_DECLARE_MEMBER(std::string, voxelType, ScalarType); + + //! actual dimensions after the data is loaded in - to be computed from + // sliceResolutiona nd numSlices + SG_NODE_DECLARE_MEMBER(vec3i,dimensions,Dimensions); }; - - } // ::ospray::sg -} // ::ospray + /*! a structured volume loaded from the Richtmyer-Meshkov .bob files */ + struct RichtmyerMeshkov : public Volume + { + RichtmyerMeshkov(); + std::string toString() const override; + + //! return bounding box of all primitives + box3f bounds() const override; + + //! \brief Initialize this node's value from given XML node + void setFromXML(const xml::Node &node, + const unsigned char *binBasePtr) override; + + void preCommit(RenderContext &ctx) override; + void postCommit(RenderContext &ctx) override; + + SG_NODE_DECLARE_MEMBER(vec3i, dimensions, Dimensions); + SG_NODE_DECLARE_MEMBER(std::string, dirName, DirName); + SG_NODE_DECLARE_MEMBER(int, timeStep, TimeStep); + // TODO WILL: I don't think we need this + //SG_NODE_DECLARE_MEMBER(std::string, voxelType, ScalarType); + + //! \brief file name of the xml doc when the node was loaded from xml + /*! \detailed we need this to properly resolve relative directory names */ + FileName fileNameOfCorrespondingXmlDoc; + + private: + //! \brief state for the loader threads to use, for picking which block to load + struct LoaderState + { + std::mutex mutex; + std::atomic nextBlockID; + bool useGZip; + FileName fullDirName; + int timeStep; + vec2f voxelRange; + + const static size_t BLOCK_SIZE; + const static size_t NUM_BLOCKS; + + LoaderState(const FileName &fullDirName, const int timeStep); + //! \brief Load the next RM block and return the id of the loaded block. + // If all blocks are loaded, returns a block ID >= NUM_BLOCKS + size_t loadNextBlock(std::vector &b); + }; + + //! \brief worker thread function for loading blocks of the RM data + void loaderThread(LoaderState &state); + }; + + } // ::ospray::sg +} // ::ospray diff --git a/apps/common/widgets/OSPGlutViewer.cpp b/apps/common/widgets/OSPGlutViewer.cpp deleted file mode 100644 index cea0921894..0000000000 --- a/apps/common/widgets/OSPGlutViewer.cpp +++ /dev/null @@ -1,376 +0,0 @@ -// ======================================================================== // -// Copyright 2016 SURVICE Engineering Company // -// Copyright 2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "OSPGlutViewer.h" - -using std::cout; -using std::endl; - -using std::string; - -using std::lock_guard; -using std::mutex; - -using namespace ospcommon; - -// Static local helper functions ////////////////////////////////////////////// - -// helper function to write the rendered image as PPM file -static void writePPM(const string &fileName, const int sizeX, const int sizeY, - const uint32_t *pixel) -{ - FILE *file = fopen(fileName.c_str(), "wb"); - fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); - unsigned char *out = (unsigned char *)alloca(3*sizeX); - for (int y = 0; y < sizeY; y++) { - const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; - for (int x = 0; x < sizeX; x++) { - out[3*x + 0] = in[4*x + 0]; - out[3*x + 1] = in[4*x + 1]; - out[3*x + 2] = in[4*x + 2]; - } - fwrite(out, 3*sizeX, sizeof(char), file); - } - fprintf(file, "\n"); - fclose(file); -} - -// MSGViewer definitions ////////////////////////////////////////////////////// - -namespace ospray { - -OSPGlutViewer::OSPGlutViewer(const std::deque &worldBounds, std::deque model, - cpp::Renderer renderer, cpp::Camera camera) - : Glut3DWidget(Glut3DWidget::FRAMEBUFFER_NONE), - sceneModels(model), - frameBuffer(nullptr), - renderer(renderer), - camera(camera), - queuedRenderer(nullptr), - alwaysRedraw(true), - fullScreen(false), - worldBounds(worldBounds), - lockFirstAnimationFrame(false) -{ - if (!worldBounds.empty()) { - setWorldBounds(worldBounds[0]); - } - renderer.set("world", sceneModels[0]); - renderer.set("model", sceneModels[0]); - renderer.set("camera", camera); - renderer.commit(); - -#if 0 - cout << "#ospGlutViewer: set world bounds " << worldBounds - << ", motion speed " << motionSpeed << endl; -#endif - - resetAccum = false; - frameTimer = ospcommon::getSysTime(); - animationTimer = 0.; - animationFrameDelta = .03; - animationFrameId = 0; - animationPaused = false; - glutViewPort = viewPort; - scale = vec3f(1,1,1); -} - -void OSPGlutViewer::create(const char* title, - const vec2i& size, - bool fullScreen) -{ - windowTitle = std::string(title); - Glut3DWidget::create(title, size, fullScreen); -} - -void OSPGlutViewer::setRenderer(OSPRenderer renderer) -{ - lock_guard lock{rendererMutex}; - queuedRenderer = renderer; -} - -void OSPGlutViewer::resetAccumulation() -{ - resetAccum = true; -} - -void OSPGlutViewer::toggleFullscreen() -{ - fullScreen = !fullScreen; - - if(fullScreen) { - glutFullScreen(); - } else { - glutPositionWindow(0,10); - } -} - -void OSPGlutViewer::resetView() -{ - auto oldAspect = viewPort.aspect; - viewPort = glutViewPort; - viewPort.aspect = oldAspect; -} - -void OSPGlutViewer::printViewport() -{ - printf("-vp %f %f %f -vu %f %f %f -vi %f %f %f\n", - viewPort.from.x, viewPort.from.y, viewPort.from.z, - viewPort.up.x, viewPort.up.y, viewPort.up.z, - viewPort.at.x, viewPort.at.y, viewPort.at.z); - fflush(stdout); -} - -void OSPGlutViewer::saveScreenshot(const std::string &basename) -{ - const uint32_t *p = (uint32_t*)frameBuffer.map(OSP_FB_COLOR); - writePPM(basename + ".ppm", windowSize.x, windowSize.y, p); - cout << "#ospGlutViewer: saved current frame to '" << basename << ".ppm'" - << endl; -} - -void OSPGlutViewer::setWorldBounds(const box3f &worldBounds) { - Glut3DWidget::setWorldBounds(worldBounds); - renderer.set("aoDistance", (worldBounds.upper.x - worldBounds.lower.x)/4.f); - renderer.commit(); -} -void OSPGlutViewer::reshape(const vec2i &newSize) -{ - Glut3DWidget::reshape(newSize); - windowSize = newSize; - frameBuffer = cpp::FrameBuffer(osp::vec2i{newSize.x, newSize.y}, - OSP_FB_SRGBA, - OSP_FB_COLOR | OSP_FB_DEPTH | OSP_FB_ACCUM); - - frameBuffer.clear(OSP_FB_ACCUM); - - camera.set("aspect", viewPort.aspect); - camera.commit(); - viewPort.modified = true; - forceRedraw(); -} - -void OSPGlutViewer::keypress(char key, const vec2i &where) -{ - switch (key) { - case ' ': - animationPaused = !animationPaused; - break; - case '=': - animationFrameDelta = max(animationFrameDelta-0.01, 0.0001); - break; - case '-': - animationFrameDelta = min(animationFrameDelta+0.01, 1.0); - break; - case 'R': - alwaysRedraw = !alwaysRedraw; - forceRedraw(); - break; - case '!': - saveScreenshot("ospglutviewer"); - break; - case 'X': - if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) { - viewPort.up = - viewPort.up; - } else { - viewPort.up = vec3f(1,0,0); - } - viewPort.modified = true; - forceRedraw(); - break; - case 'Y': - if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) { - viewPort.up = - viewPort.up; - } else { - viewPort.up = vec3f(0,1,0); - } - viewPort.modified = true; - forceRedraw(); - break; - case 'Z': - if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) { - viewPort.up = - viewPort.up; - } else { - viewPort.up = vec3f(0,0,1); - } - viewPort.modified = true; - forceRedraw(); - break; - case 'c': - viewPort.modified = true;//Reset accumulation - break; - case 'f': - toggleFullscreen(); - break; - case 'r': - resetView(); - break; - case 'p': - printViewport(); - break; - default: - Glut3DWidget::keypress(key,where); - } -} - -void OSPGlutViewer::mouseButton(int32_t whichButton, - bool released, - const vec2i &pos) -{ - Glut3DWidget::mouseButton(whichButton, released, pos); - if((currButtonState == (1< 0) fps.doneRender(); - - // NOTE: consume a new renderer if one has been queued by another thread - switchRenderers(); - - updateAnimation(ospcommon::getSysTime()-frameTimer); - frameTimer = ospcommon::getSysTime(); - - if (resetAccum) { - frameBuffer.clear(OSP_FB_ACCUM); - resetAccum = false; - } - - ++frameID; - - if (viewPort.modified) { - Assert2(camera.handle(),"ospray camera is null"); - camera.set("pos", viewPort.from); - auto dir = viewPort.at - viewPort.from; - camera.set("dir", dir); - camera.set("up", viewPort.up); - camera.set("aspect", viewPort.aspect); - camera.set("fovy", viewPort.openingAngle); - camera.commit(); - - viewPort.modified = false; - frameBuffer.clear(OSP_FB_ACCUM); - } - fps.startRender(); - - renderer.renderFrame(frameBuffer, OSP_FB_COLOR | OSP_FB_ACCUM); - - // set the glut3d widget's frame buffer to the OSPRay frame buffer, - // then display - ucharFB = (uint32_t *)frameBuffer.map(OSP_FB_COLOR); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - Glut3DWidget::display(); - - frameBuffer.unmap(ucharFB); - - // that pointer is no longer valid, so set it to null - ucharFB = nullptr; - - std::string title = windowTitle; - - if (alwaysRedraw) { - title += " (" + std::to_string((long double)fps.getFPS()) + " fps)"; - setTitle(title); - forceRedraw(); - } else { - setTitle(title); - } -} - -void OSPGlutViewer::switchRenderers() -{ - lock_guard lock{rendererMutex}; - - if (queuedRenderer.handle()) { - renderer = queuedRenderer; - queuedRenderer = nullptr; - frameBuffer.clear(OSP_FB_ACCUM); - } -} - -void OSPGlutViewer::updateAnimation(double deltaSeconds) -{ - if (sceneModels.size() < 2) - return; - if (animationPaused) - return; - animationTimer += deltaSeconds; - int framesSize = sceneModels.size(); - const int frameStart = (lockFirstAnimationFrame ? 1 : 0); - if (lockFirstAnimationFrame) - framesSize--; - - if (animationTimer > animationFrameDelta) - { - animationFrameId++; - //set animation time to remainder off of delta - animationTimer = animationTimer - int(animationTimer/deltaSeconds)*deltaSeconds; - - size_t dataFrameId = animationFrameId%framesSize+frameStart; - if (lockFirstAnimationFrame) - { - ospcommon::affine3f xfm = ospcommon::one; - xfm = xfm*ospcommon::affine3f::translate(translate)*ospcommon::affine3f::scale(scale); - OSPGeometry dynInst = - ospNewInstance((OSPModel)sceneModels[dataFrameId].object(), - (osp::affine3f&)xfm); - ospray::cpp::Model worldModel = ospNewModel(); - ospcommon::affine3f staticXFM = ospcommon::one; - OSPGeometry staticInst = - ospNewInstance((OSPModel)sceneModels[0].object(), - (osp::affine3f&)staticXFM); - //Carson: TODO: creating new world model every frame unecessary - worldModel.addGeometry(staticInst); - worldModel.addGeometry(dynInst); - worldModel.commit(); - renderer.set("model", worldModel); - } - else - { - renderer.set("model", sceneModels[dataFrameId]); - } - renderer.commit(); - resetAccumulation(); - } -} - -}// namepace ospray diff --git a/apps/common/widgets/glut3D.cpp b/apps/common/widgets/glut3D.cpp deleted file mode 100644 index 2bab343fa3..0000000000 --- a/apps/common/widgets/glut3D.cpp +++ /dev/null @@ -1,912 +0,0 @@ -// ======================================================================== // -// Copyright 2016 SURVICE Engineering Company // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "glut3D.h" - -#ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include // for Sleep -# define _USE_MATH_DEFINES -# include // M_PI -#else -# include -# include // for usleep -#endif -#include -#include -#include -#include - -namespace ospray { - - namespace glut3D { - - bool dumpScreensDuringAnimation = false; - - FPSCounter::FPSCounter() - { - smooth_nom = 0.; - smooth_den = 0.; - frameStartTime = 0.; - } - - void FPSCounter::startRender() - { - frameStartTime = ospcommon::getSysTime(); - } - - void FPSCounter::doneRender() { - double seconds = ospcommon::getSysTime() - frameStartTime; - smooth_nom = smooth_nom * 0.8f + seconds; - smooth_den = smooth_den * 0.8f + 1.f; - } - - /*! write given frame buffer to file, in PPM P6 format. */ - void saveFrameBufferToFile(const char *fileName, - const uint32_t *pixel, - const uint32_t sizeX, const uint32_t sizeY) - { - FILE *file = fopen(fileName,"wb"); - if (!file) { - std::cerr << "#osp:glut3D: Warning - could not create screenshot file '" - << fileName << "'" << std::endl; - return; - } - fprintf(file,"P6\n%i %i\n255\n",sizeX,sizeY); - unsigned char *out = (unsigned char *)alloca(3*sizeX); - for (size_t y = 0; y < sizeY; y++) { - const unsigned char *in = - (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; - for (size_t x = 0; x < sizeX; x++) { - out[3*x+0] = in[4*x+0]; - out[3*x+1] = in[4*x+1]; - out[3*x+2] = in[4*x+2]; - } - fwrite(out, 3*sizeX, sizeof(char), file); - } - fprintf(file,"\n"); - fclose(file); - std::cout << "#osp:glut3D: saved framebuffer to file " - << fileName << std::endl; - } - -#define INVERT_RMB - /*! currently active window */ - Glut3DWidget *Glut3DWidget::activeWindow = nullptr; - vec2i Glut3DWidget::defaultInitSize(1024,768); - - bool animating = false; - - // InspectCenter Glut3DWidget::INSPECT_CENTER; - /*! viewport as specified on the command line */ - Glut3DWidget::ViewPort *viewPortFromCmdLine = nullptr; - vec3f upVectorFromCmdLine(0,1,0); - - // ------------------------------------------------------------------ - // glut event handlers - // ------------------------------------------------------------------ - - void glut3dReshape(int32_t x, int32_t y) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->reshape(vec2i(x,y)); - } - - void glut3dDisplay( void ) - { - if (animating && Glut3DWidget::activeWindow && Glut3DWidget::activeWindow->inspectCenterManipulator) { - InspectCenter *hack = (InspectCenter *) Glut3DWidget::activeWindow->inspectCenterManipulator; - hack->rotate(-10.f * Glut3DWidget::activeWindow->motionSpeed, 0); - } - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->display(); - } - - void glut3dKeyboard(unsigned char key, int32_t x, int32_t y) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->keypress(key,vec2i(x,y)); - } - void glut3dSpecial(int32_t key, int32_t x, int32_t y) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->specialkey(key,vec2i(x,y)); - } - - void glut3dIdle( void ) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->idle(); - } - void glut3dMotionFunc(int32_t x, int32_t y) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->motion(vec2i(x,y)); - } - - void glut3dMouseFunc(int32_t whichButton, int32_t released, int32_t x, int32_t y) - { - if (Glut3DWidget::activeWindow) - Glut3DWidget::activeWindow->mouseButton(whichButton,released,vec2i(x,y)); - } - - - // ------------------------------------------------------------------ - // implementation of glut3d::viewPorts - // ------------------------------------------------------------------ - Glut3DWidget::ViewPort::ViewPort() : - modified(true), - from(0,0,-1), - at(0,0,0), - up(upVectorFromCmdLine), - openingAngle(60.f), - aspect(1.f) - { - frame = AffineSpace3fa::translate(from) * AffineSpace3fa(ospcommon::one); - } - - void Glut3DWidget::ViewPort::snapUp() - { - if (fabsf(dot(up,frame.l.vz)) < 1e-3f) - return; - frame.l.vx = normalize(cross(frame.l.vy,up)); - frame.l.vz = normalize(cross(frame.l.vx,frame.l.vy)); - frame.l.vy = normalize(cross(frame.l.vz,frame.l.vx)); - } - - // ------------------------------------------------------------------ - // implementation of glut3d widget - // ------------------------------------------------------------------ - void Glut3DWidget::mouseButton(int32_t whichButton, bool released, const vec2i &pos) - { - - if (pos != currMousePos) - motion(pos); - lastButtonState = currButtonState; - - if (released) - currButtonState = currButtonState & ~(1<button(this, pos); - } - - void Glut3DWidget::motion(const vec2i &pos) - { - currMousePos = pos; - if (currButtonState != lastButtonState) { - // some button got pressed; reset 'old' pos to new pos. - lastMousePos = currMousePos; - lastButtonState = currButtonState; - } - - manipulator->motion(this); - lastMousePos = currMousePos; - if (viewPort.modified) - forceRedraw(); - } - - Glut3DWidget::Glut3DWidget(FrameBufferMode frameBufferMode, - ManipulatorMode initialManipulator, - int allowedManipulators) : - lastMousePos(-1,-1), - currMousePos(-1,-1), - lastButtonState(0), - currButtonState(0), - currModifiers(0), - windowID(-1), - windowSize(-1,-1), - motionSpeed(.003f), - rotateSpeed(.003f), - frameBufferMode(frameBufferMode), - ucharFB(nullptr) - { - worldBounds.lower = vec3f(-1); - worldBounds.upper = vec3f(+1); - - if (allowedManipulators & INSPECT_CENTER_MODE) { - inspectCenterManipulator = new InspectCenter(this); - } - if (allowedManipulators & MOVE_MODE) { - moveModeManipulator = new MoveMode(this); - } - switch(initialManipulator) { - case MOVE_MODE: - manipulator = moveModeManipulator; - break; - case INSPECT_CENTER_MODE: - manipulator = inspectCenterManipulator; - break; - } - Assert2(manipulator != nullptr,"invalid initial manipulator mode"); - - if (viewPortFromCmdLine) { - viewPort = *viewPortFromCmdLine; - - if (length(viewPort.up) < 1e-3f) - viewPort.up = vec3f(0,0,1.f); - - this->worldBounds = worldBounds; - computeFrame(); - } - } - - void Glut3DWidget::computeFrame() - { - viewPort.frame.l.vy = normalize(viewPort.at - viewPort.from); - viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,viewPort.up)); - viewPort.frame.l.vz = normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); - viewPort.frame.p = viewPort.from; - viewPort.snapUp(); - viewPort.modified = true; - } - - void Glut3DWidget::setZUp(const vec3f &up) - { - viewPort.up = up; - if (up != vec3f(0.f)) { - viewPort.snapUp(); - forceRedraw(); - } - } - - void Glut3DWidget::idle() - { -#ifdef _WIN32 - Sleep(1); -#else - usleep(1000); -#endif - } - - void Glut3DWidget::reshape(const vec2i &newSize) - { - windowSize = newSize; - viewPort.aspect = newSize.x/float(newSize.y); - glViewport(0, 0, windowSize.x, windowSize.y); - - forceRedraw(); - } - - void Glut3DWidget::activate() - { - activeWindow = this; - glutSetWindow(windowID); - } - - void Glut3DWidget::forceRedraw() - { - glutPostRedisplay(); - } - - void Glut3DWidget::display() - { - if (frameBufferMode == Glut3DWidget::FRAMEBUFFER_UCHAR && ucharFB) { - glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_UNSIGNED_BYTE, ucharFB); -#ifndef _WIN32 - if (animating && dumpScreensDuringAnimation) { - char tmpFileName[] = "/tmp/ospray_scene_dump_file.XXXXXXXXXX"; - static const char *dumpFileRoot; - if (!dumpFileRoot) - dumpFileRoot = getenv("OSPRAY_SCREEN_DUMP_ROOT"); - if (!dumpFileRoot) { - auto rc = mkstemp(tmpFileName); - (void)rc; - dumpFileRoot = tmpFileName; - } - - char fileName[100000]; - sprintf(fileName,"%s_%08ld.ppm",dumpFileRoot,times(nullptr)); - saveFrameBufferToFile(fileName,ucharFB,windowSize.x,windowSize.y); - } -#endif - } else if (frameBufferMode == Glut3DWidget::FRAMEBUFFER_FLOAT && floatFB) { - glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_FLOAT, floatFB); - } else { - glClearColor(0.f,0.f,0.f,1.f); - glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - } - glutSwapBuffers(); - } - - void Glut3DWidget::clearPixels() - { - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glutSwapBuffers(); - } - - void Glut3DWidget::drawPixels(const uint32_t *framebuffer) - { - throw std::runtime_error("should not be used right now"); - glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_UNSIGNED_BYTE, framebuffer); - glutSwapBuffers(); - } - - void Glut3DWidget::drawPixels(const vec3fa *framebuffer) - { - throw std::runtime_error("should not be used right now"); - glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_FLOAT, framebuffer); - glutSwapBuffers(); - } - - void Glut3DWidget::setViewPort(const vec3f from, - const vec3f at, - const vec3f up) - { - const vec3f dir = at - from; - viewPort.at = at; - viewPort.from = from; - viewPort.up = up; - - this->worldBounds = worldBounds; - viewPort.frame.l.vy = normalize(dir); - viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,up)); - viewPort.frame.l.vz = normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); - viewPort.frame.p = from; - viewPort.snapUp(); - viewPort.modified = true; - } - - void Glut3DWidget::setWorldBounds(const box3f &worldBounds) - { - vec3f center = ospcommon::center(worldBounds); - vec3f diag = worldBounds.size(); - diag = max(diag,vec3f(0.3f*length(diag))); - vec3f from = center - .75f*vec3f(-.6*diag.x,-1.2f*diag.y,.8f*diag.z); - vec3f dir = center - from; - vec3f up = viewPort.up; - - if (!viewPortFromCmdLine) { - viewPort.at = center; - viewPort.from = from; - viewPort.up = up; - - if (length(up) < 1e-3f) - up = vec3f(0,0,1.f); - - this->worldBounds = worldBounds; - viewPort.frame.l.vy = normalize(dir); - viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,up)); - viewPort.frame.l.vz = normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); - viewPort.frame.p = from; - viewPort.snapUp(); - viewPort.modified = true; - } - motionSpeed = length(diag) * .001f; - // std::cout << "glut3d: setting world bounds " << worldBounds << ", motion speed " << motionSpeed << std::endl; - } - - void Glut3DWidget::setTitle(const char *title) - { - Assert2(windowID >= 0,"must call Glut3DWidget::create() before calling setTitle()"); - glutSetWindow(windowID); - glutSetWindowTitle(title); - } - - void Glut3DWidget::create(const char *title, - const vec2i &size, - bool fullScreen) - { - glutInitWindowSize( size.x, size.y ); - windowID = glutCreateWindow(title); - activeWindow = this; - glutDisplayFunc( glut3dDisplay ); - glutReshapeFunc( glut3dReshape ); - glutKeyboardFunc(glut3dKeyboard ); - glutSpecialFunc( glut3dSpecial ); - glutMotionFunc( glut3dMotionFunc ); - glutMouseFunc( glut3dMouseFunc ); - glutIdleFunc( glut3dIdle ); - - if (fullScreen) - glutFullScreen(); - } - - void runGLUT() - { - glutMainLoop(); - } - - void initGLUT(int32_t *ac, const char **av) - { - glutInit(ac, (char **) av); - - - // glutInitDisplayMode (GLUT_RGBA | GLUT_SINGLE); - glutInitDisplayMode (GLUT_RGBA | GLUT_DOUBLE); - - for(int i = 1; i < *ac;i++) - { - std::string arg(av[i]); - if (arg == "-win") { - std::string arg2(av[i+1]); - size_t pos = arg2.find("x"); - if (pos != std::string::npos) { - arg2.replace(pos, 1, " "); - std::stringstream ss(arg2); - ss >> Glut3DWidget::defaultInitSize.x >> Glut3DWidget::defaultInitSize.y; - removeArgs(*ac,(char **&)av,i,2); --i; - } else { - Glut3DWidget::defaultInitSize.x = atoi(av[i+1]); - Glut3DWidget::defaultInitSize.y = atoi(av[i+1]); - removeArgs(*ac,(char **&)av,i,3); --i; - } - continue; - } - if (arg == "--1k" || arg == "-1k") { - Glut3DWidget::defaultInitSize.x = Glut3DWidget::defaultInitSize.y = 1024; - removeArgs(*ac,(char **&)av,i,1); --i; - continue; - } - if (arg == "--size") { - Glut3DWidget::defaultInitSize.x = atoi(av[i+1]); - Glut3DWidget::defaultInitSize.y = atoi(av[i+2]); - removeArgs(*ac,(char **&)av,i,3); --i; - continue; - } - if (arg == "-v" || arg == "--view") { - std::ifstream fin(av[i+1]); - if (!fin.is_open()) - { - throw std::runtime_error("Failed to open \"" + - std::string(av[i+1]) + - "\" for reading"); - } - - if (!viewPortFromCmdLine) - viewPortFromCmdLine = new Glut3DWidget::ViewPort; - - auto& fx = viewPortFromCmdLine->from.x; - auto& fy = viewPortFromCmdLine->from.y; - auto& fz = viewPortFromCmdLine->from.z; - - auto& ax = viewPortFromCmdLine->at.x; - auto& ay = viewPortFromCmdLine->at.y; - auto& az = viewPortFromCmdLine->at.z; - - auto& ux = viewPortFromCmdLine->up.x; - auto& uy = viewPortFromCmdLine->up.y; - auto& uz = viewPortFromCmdLine->up.z; - - auto& fov = viewPortFromCmdLine->openingAngle; - - auto token = std::string(""); - while (fin >> token) - { - if (token == "-vp") - fin >> fx >> fy >> fz; - else if (token == "-vu") - fin >> ux >> uy >> uz; - else if (token == "-vi") - fin >> ax >> ay >> az; - else if (token == "-fv") - fin >> fov; - else - { - throw std::runtime_error("Unrecognized token: \"" + token + - '\"'); - } - } - - assert(i+1 < *ac); - removeArgs(*ac,(char **&)av, i, 2); --i; - continue; - } - if (arg == "-vu") { - upVectorFromCmdLine.x = atof(av[i+1]); - upVectorFromCmdLine.y = atof(av[i+2]); - upVectorFromCmdLine.z = atof(av[i+3]); - if (viewPortFromCmdLine) - viewPortFromCmdLine->up = upVectorFromCmdLine; - assert(i+3 < *ac); - removeArgs(*ac,(char **&)av,i,4); --i; - continue; - } - if (arg == "-vp") { - if (!viewPortFromCmdLine) viewPortFromCmdLine = new Glut3DWidget::ViewPort; - viewPortFromCmdLine->from.x = atof(av[i+1]); - viewPortFromCmdLine->from.y = atof(av[i+2]); - viewPortFromCmdLine->from.z = atof(av[i+3]); - assert(i+3 < *ac); - removeArgs(*ac,(char **&)av,i,4); --i; - continue; - } - if (arg == "-vi") { - if (!viewPortFromCmdLine) viewPortFromCmdLine = new Glut3DWidget::ViewPort; - viewPortFromCmdLine->at.x = atof(av[i+1]); - viewPortFromCmdLine->at.y = atof(av[i+2]); - viewPortFromCmdLine->at.z = atof(av[i+3]); - assert(i+3 < *ac); - removeArgs(*ac,(char **&)av,i,4); --i; - continue; - } - } - } - - // ------------------------------------------------------------------ - // base manipulator - // ------------------------------------------------------------------ - void Manipulator::motion(Glut3DWidget *widget) - { - if ((widget->currButtonState == (1<currButtonState == (1<currModifiers & GLUT_ACTIVE_ALT)) - ) { - dragRight(widget,widget->currMousePos,widget->lastMousePos); - } else if ((widget->currButtonState == (1<currButtonState == (1<currModifiers & GLUT_ACTIVE_CTRL)) - ) { - dragMiddle(widget,widget->currMousePos,widget->lastMousePos); - } else if (widget->currButtonState == (1<currMousePos,widget->lastMousePos); - } - } - - void Manipulator::button(Glut3DWidget *widget, const vec2i &pos) - { - } - - // ------------------------------------------------------------------ - // INSPECT_CENTER manipulator - // ------------------------------------------------------------------ - InspectCenter::InspectCenter(Glut3DWidget *widget) - : Manipulator(widget) - , pivot(ospcommon::center(widget->worldBounds)) - {} - - void InspectCenter::keypress(Glut3DWidget *widget, - int32_t key) - { - switch(key) { - case 'a': { - rotate(+10.f*widget->rotateSpeed,0); - } return; - case 'd': { - rotate(-10.f*widget->rotateSpeed,0); - } return; - case 'w': { - rotate(0,+10.f*widget->rotateSpeed); - } return; - case 's': { - rotate(0,-10.f*widget->rotateSpeed); - } return; - } - - Manipulator::keypress(widget,key); - } - - void InspectCenter::button(Glut3DWidget *widget, const vec2i &pos) - { - } - - void InspectCenter::rotate(float du, float dv) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - const vec3f pivot = widget->viewPort.at;//center(widget->worldBounds); - AffineSpace3fa xfm - = AffineSpace3fa::translate(pivot) - * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) - * AffineSpace3fa::rotate(cam.frame.l.vz,-du) - * AffineSpace3fa::translate(-pivot); - cam.frame = xfm * cam.frame; - cam.from = xfmPoint(xfm,cam.from); - cam.at = xfmPoint(xfm,cam.at); - cam.snapUp(); - cam.modified = true; - } - - void InspectCenter::specialkey(Glut3DWidget *widget, - int32_t key) - { - switch(key) { - case GLUT_KEY_LEFT: { - rotate(+10.f*widget->rotateSpeed,0); - } return; - case GLUT_KEY_RIGHT: { - rotate(-10.f*widget->rotateSpeed,0); - } return; - case GLUT_KEY_UP: { - rotate(0,+10.f*widget->rotateSpeed); - } return; - case GLUT_KEY_DOWN: { - rotate(0,-10.f*widget->rotateSpeed); - } return; - } - Manipulator::specialkey(widget,key); - } - - /*! INSPECT_CENTER::RightButton: move lookfrom/viewPort positoin - forward/backward on right mouse button */ - void InspectCenter::dragRight(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float fwd = -#ifdef INVERT_RMB -#else - - -#endif - (to.y - from.y) * 4 * widget->motionSpeed; - // * length(widget->worldBounds.size()); - float oldDist = length(cam.at - cam.from); - float newDist = oldDist - fwd; - if (newDist < 1e-3f) - return; - cam.from = cam.at - newDist * cam.frame.l.vy; - cam.frame.p = cam.from; - cam.modified = true; - } - - /*! INSPECT_CENTER::MiddleButton: move lookat/center of interest - forward/backward on middle mouse button */ - void InspectCenter::dragMiddle(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float du = (to.x - from.x); - float dv = (to.y - from.y); - - AffineSpace3fa xfm = - AffineSpace3fa::translate(widget->motionSpeed * dv * cam.frame.l.vz ) - * AffineSpace3fa::translate(-1.0f * widget->motionSpeed - * du * cam.frame.l.vx); - - cam.frame = xfm * cam.frame; - cam.from = xfmPoint(xfm, cam.from); - cam.at = xfmPoint(xfm, cam.at); - cam.modified = true; - } - - void InspectCenter::dragLeft(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float du = (to.x - from.x) * widget->rotateSpeed; - float dv = (to.y - from.y) * widget->rotateSpeed; - - const vec3f pivot = cam.at; - AffineSpace3fa xfm - = AffineSpace3fa::translate(pivot) - * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) - * AffineSpace3fa::rotate(cam.frame.l.vz,-du) - * AffineSpace3fa::translate(-pivot); - cam.frame = xfm * cam.frame; - cam.from = xfmPoint(xfm,cam.from); - cam.at = xfmPoint(xfm,cam.at); - cam.snapUp(); - cam.modified = true; - } - - - - // ------------------------------------------------------------------ - // MOVE_MOVE manipulator - TODO. - // ------------------------------------------------------------------ - - /*! \brief key press events for move mode - - Right now, recognizes the following move modes: - - - w : move forward - - s : move backward - - a : pan left - - d : pan right - - */ - void MoveMode::keypress(Glut3DWidget *widget, - int32_t key) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - switch(key) { - case 'w': { - float fwd = 8 * widget->motionSpeed; - cam.from = cam.from + fwd * cam.frame.l.vy; - cam.at = cam.at + fwd * cam.frame.l.vy; - cam.frame.p = cam.from; - cam.modified = true; - } return; - case 's': { - float fwd = 8 * widget->motionSpeed; - cam.from = cam.from - fwd * cam.frame.l.vy; - cam.at = cam.at - fwd * cam.frame.l.vy; - cam.frame.p = cam.from; - cam.modified = true; - } return; - case 'd': { - float fwd = 8 * widget->motionSpeed; - cam.from = cam.from + fwd * cam.frame.l.vx; - cam.at = cam.at + fwd * cam.frame.l.vx; - cam.frame.p = cam.from; - cam.modified = true; - } return; - case 'a': { - float fwd = 8 * widget->motionSpeed; - cam.from = cam.from - fwd * cam.frame.l.vx; - cam.at = cam.at - fwd * cam.frame.l.vx; - cam.frame.p = cam.from; - cam.modified = true; - } return; - } - Manipulator::keypress(widget,key); - } - - void MoveMode::dragRight(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float fwd = -#ifdef INVERT_RMB -#else - - -#endif - (to.y - from.y) * 4 * widget->motionSpeed; - cam.from = cam.from + fwd * cam.frame.l.vy; - cam.at = cam.at + fwd * cam.frame.l.vy; - cam.frame.p = cam.from; - cam.modified = true; - } - - /*! todo */ - void MoveMode::dragMiddle(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float du = (to.x - from.x); - float dv = (to.y - from.y); - - AffineSpace3fa xfm = AffineSpace3fa::translate( widget->motionSpeed * dv * cam.frame.l.vz ) - * AffineSpace3fa::translate( -1.0f * widget->motionSpeed * du * cam.frame.l.vx ); - - cam.frame = xfm * cam.frame; - cam.from = xfmPoint(xfm, cam.from); - cam.at = xfmPoint(xfm, cam.at); - cam.modified = true; - } - - void MoveMode::dragLeft(Glut3DWidget *widget, - const vec2i &to, const vec2i &from) - { - Glut3DWidget::ViewPort &cam = widget->viewPort; - float du = (to.x - from.x) * widget->rotateSpeed; - float dv = (to.y - from.y) * widget->rotateSpeed; - - const vec3f pivot = cam.from; //center(widget->worldBounds); - AffineSpace3fa xfm - = AffineSpace3fa::translate(pivot) - * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) - * AffineSpace3fa::rotate(cam.frame.l.vz,-du) - * AffineSpace3fa::translate(-pivot); - cam.frame = xfm * cam.frame; - cam.from = xfmPoint(xfm,cam.from); - cam.at = xfmPoint(xfm,cam.at); - cam.snapUp(); - cam.modified = true; - } - - void Glut3DWidget::specialkey(int32_t key, const vec2i &where) - { - if (manipulator) manipulator->specialkey(this,key); - } - void Glut3DWidget::keypress(char key, const vec2i &where) - { - if (key == '!') { - if (animating) { - dumpScreensDuringAnimation = !dumpScreensDuringAnimation; - } else { - char tmpFileName[] = "/tmp/ospray_screen_dump_file.XXXXXXXX"; - static const char *dumpFileRoot; - if (!dumpFileRoot) - dumpFileRoot = getenv("OSPRAY_SCREEN_DUMP_ROOT"); -#ifndef _WIN32 - if (!dumpFileRoot) { - auto rc = mkstemp(tmpFileName); - (void)rc; - dumpFileRoot = tmpFileName; - } -#endif - char fileName[100000]; - static int frameDumpSequenceID = 0; - sprintf(fileName,"%s_%05d.ppm",dumpFileRoot,frameDumpSequenceID++); - if (ucharFB) - saveFrameBufferToFile(fileName,ucharFB,windowSize.x,windowSize.y); - return; - } - } - - if (key == 'C') { - PRINT(viewPort); - return; - } - if (key == 'I' && inspectCenterManipulator) { - // 'i'nspect mode - manipulator = inspectCenterManipulator; - return; - } - if ((key == 'M' || key == 'F') && moveModeManipulator) { - manipulator = moveModeManipulator; - return; - } - if (key == 'F' && moveModeManipulator) { - // 'f'ly mode - manipulator = moveModeManipulator; - return; - } - if (key == 'A' && inspectCenterManipulator) { - animating = !animating; - return; - } - if (key == '+') { - motionSpeed *= 1.5f; - std::cout << "glut3d: new motion speed " << motionSpeed << std:: endl; - return; - } - if (key == '-') { - motionSpeed /= 1.5f; - std::cout << "glut3d: new motion speed " << motionSpeed << std:: endl; - return; - } - if (manipulator) manipulator->keypress(this,key); - } - - - void Manipulator::keypress(Glut3DWidget *widget, const int32_t key) - { - switch(key) { - case 27 /*ESC*/: - case 'q': - case 'Q': - std::exit(0); - } - } - - void Manipulator::specialkey(Glut3DWidget *widget, const int32_t key) - { - } - - - std::ostream &operator<<(std::ostream &o, const Glut3DWidget::ViewPort &cam) - { - o << "// " - << " -vp " << cam.from.x << " " << cam.from.y << " " << cam.from.z - << " -vi " << cam.at.x << " " << cam.at.y << " " << cam.at.z - << " -vu " << cam.up.x << " " << cam.up.y << " " << cam.up.z - << std::endl; - o << "" << std::endl; - o << " " << cam.from.x << " " << cam.from.y << " " << cam.from.z << "" << std::endl; - o << " " << cam.at.x << " " << cam.at.y << " " << cam.at.z << "" << std::endl; - o << " " << cam.up.x << " " << cam.up.y << " " << cam.up.z << "" << std::endl; - o << " " << cam.aspect << "" << std::endl; - o << " " << cam.frame.l.vx << "" << std::endl; - o << " " << cam.frame.l.vy << "" << std::endl; - o << " " << cam.frame.l.vz << "" << std::endl; - o << " " << cam.frame.p << "" << std::endl; - o << ""; - return o; - } - } -} - diff --git a/apps/common/widgets/glut3D.h b/apps/common/widgets/glut3D.h deleted file mode 100644 index f9f5ab12ff..0000000000 --- a/apps/common/widgets/glut3D.h +++ /dev/null @@ -1,315 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "ospcommon/common.h" -#include "ospcommon/box.h" -#include "ospcommon/AffineSpace.h" - -#ifdef __APPLE__ -#include -#else -#include -#endif - -#ifdef __linux__ -#include -#endif - -#include "common/widgets/Glut3dExport.h" - -namespace ospray { - //! dedicated namespace for 3D glut viewer widget - namespace glut3D { - - using namespace ospcommon; - - /*! initialize everything GLUT-related */ - OSPRAY_GLUT3D_INTERFACE void initGLUT(int32_t *ac, const char **av); - /*! switch over to GLUT for control flow. This function will not return */ - OSPRAY_GLUT3D_INTERFACE void runGLUT(); - - using ospcommon::AffineSpace3fa; - - /*! helper class that allows for easily computing (smoothed) frame rate */ - struct FPSCounter { - OSPRAY_GLUT3D_INTERFACE FPSCounter(); - OSPRAY_GLUT3D_INTERFACE void startRender(); - OSPRAY_GLUT3D_INTERFACE void doneRender(); - OSPRAY_GLUT3D_INTERFACE double getFPS() const - { return smooth_den / smooth_nom; } - - private: - double smooth_nom; - double smooth_den; - double frameStartTime; - }; - - struct Glut3DWidget; - - struct Manipulator { - // this is the fct that gets called when the mouse moved in the - // associated window - OSPRAY_GLUT3D_INTERFACE virtual void motion(Glut3DWidget *widget); - // this is the fct that gets called when any mouse button got - // pressed or released in the associated window - OSPRAY_GLUT3D_INTERFACE virtual void button(Glut3DWidget *widget, - const vec2i &pos); - /*! key press handler - override this fct to catch keyboard. */ - OSPRAY_GLUT3D_INTERFACE virtual void keypress(Glut3DWidget *widget, - const int32_t key); - OSPRAY_GLUT3D_INTERFACE virtual void specialkey(Glut3DWidget *widget, - const int32_t key); - OSPRAY_GLUT3D_INTERFACE Manipulator(Glut3DWidget *widget) - : widget(widget) {} - protected: - - // helper functions called from the default 'motion' fct - OSPRAY_GLUT3D_INTERFACE virtual void dragLeft(Glut3DWidget *widget, - const vec2i &to, - const vec2i &from) - {} - OSPRAY_GLUT3D_INTERFACE virtual void dragRight(Glut3DWidget *widget, - const vec2i &to, - const vec2i &from) - {} - OSPRAY_GLUT3D_INTERFACE virtual void dragMiddle(Glut3DWidget *widget, - const vec2i &to, - const vec2i &from) - {} - Glut3DWidget *widget; - }; - - struct InspectCenter : public Manipulator - { - virtual void dragLeft(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void dragRight(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void dragMiddle(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void specialkey(Glut3DWidget *widget, const int32_t key); - virtual void keypress(Glut3DWidget *widget, int32_t key); - virtual void button(Glut3DWidget *widget, const vec2i &pos); - InspectCenter(Glut3DWidget *widget); - void rotate(float du, float dv); - - vec3f pivot; - }; - - struct MoveMode : public Manipulator - { - virtual void dragLeft(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void dragRight(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void dragMiddle(Glut3DWidget *widget, - const vec2i &to, const vec2i &from); - virtual void keypress(Glut3DWidget *widget, int32_t key); - virtual void button(Glut3DWidget *widget, const vec2i &pos) {} - MoveMode(Glut3DWidget *widget) : Manipulator(widget) {} - }; - - /*! a GLUT-based 3D viewer widget that includes simple sample code - for manipulating a 3D viewPort with the mouse. - - This widget should allow users to easily write simple 3D viewers - with simple built-in viewPort motion. In theory, all one hsa to do - after creating the window (and initializeing the viewPort is - implement the respective 'display' callback to do the actual - rendering, using the widget's infrastructure for the boilerplace - stuff. - - If specified (\see FrameBufferMode the widget will automatically - allocate a frame buffer (of either uchar4 or float4 type), in - which case the 'display' routine will only have to write to the - respective frame buffer (the widget will do the respective gl - calls to display that framebuffer); if not specified, it's up to - the derived class to implement whatever opengl calls are required - to draw the window's content. - - */ - struct Glut3DWidget - { - /*! size we'll create a window at */ - OSPRAY_GLUT3D_INTERFACE static vec2i defaultInitSize; - - typedef enum { - FRAMEBUFFER_UCHAR,FRAMEBUFFER_FLOAT,FRAMEBUFFER_DEPTH,FRAMEBUFFER_NONE - } FrameBufferMode; - typedef enum { - MOVE_MODE =(1<<0), - INSPECT_CENTER_MODE =(1<<1) - } ManipulatorMode; - /*! internal viewPort class */ - struct ViewPort { - bool modified; /* the viewPort will set this flag any time any of - its values get changed. */ - - vec3f from; - vec3f at; - vec3f up; - float openingAngle; //!< in degrees, along Y direction - float aspect; //!< aspect ratio X:Y - // float focalDistance; - - /*! camera frame in which the Y axis is the depth axis, and X - and Z axes are parallel to the screen X and Y axis. The frame - itself remains normalized. */ - AffineSpace3fa frame; - - /*! set 'up' vector. if this vector is '0,0,0' the viewer will - *not* apply the up-vector after camera manipulation */ - OSPRAY_GLUT3D_INTERFACE void snapUp(); - - OSPRAY_GLUT3D_INTERFACE ViewPort(); - }; - - // static InspectCenter INSPECT_CENTER; - Manipulator *inspectCenterManipulator; - Manipulator *moveModeManipulator; - - /*! current manipulator */ - Manipulator *manipulator; - - OSPRAY_GLUT3D_INTERFACE - Glut3DWidget(FrameBufferMode frameBufferMode, - ManipulatorMode initialManipulator=INSPECT_CENTER_MODE, - int allowedManipulators=INSPECT_CENTER_MODE|MOVE_MODE); - - /*! set a default camera position that views given bounds from the - top left front */ - OSPRAY_GLUT3D_INTERFACE virtual void setWorldBounds(const box3f &worldBounds); - /*! tell GLUT that this window is 'dirty' and needs redrawing */ - OSPRAY_GLUT3D_INTERFACE virtual void forceRedraw(); - /*! set window title */ - OSPRAY_GLUT3D_INTERFACE void setTitle(const char *title); - /*! set window title */ - OSPRAY_GLUT3D_INTERFACE void setTitle(const std::string &title) - { setTitle(title.c_str()); } - /*! set viewport to given values */ - OSPRAY_GLUT3D_INTERFACE void setViewPort(const vec3f from, - const vec3f at, - const vec3f up); - - // ------------------------------------------------------------------ - // event handling - override this to change this widgets behavior - // to input events - // ------------------------------------------------------------------ - OSPRAY_GLUT3D_INTERFACE virtual void mouseButton(int32_t which, - bool released, - const vec2i &pos); - OSPRAY_GLUT3D_INTERFACE virtual void motion(const vec2i &pos); - // /*! mouse moved to this location, with given left/right/middle buttons pressed */ - // virtual void mouseMotion(const vec2i &from, - // const vec2i &to, - // bool left, - // bool right, - // bool middle); - OSPRAY_GLUT3D_INTERFACE virtual void reshape(const vec2i &newSize); - OSPRAY_GLUT3D_INTERFACE virtual void idle(); - /*! display this window. By default this will just clear this - window's framebuffer; it's up to the user to override this fct - to do something more useful */ - OSPRAY_GLUT3D_INTERFACE virtual void display(); - - // ------------------------------------------------------------------ - // helper functions - // ------------------------------------------------------------------ - /*! activate _this_ window, in the sense that all future glut - events get routed to this window instance */ - OSPRAY_GLUT3D_INTERFACE virtual void activate(); - /*! create this window. Note that this just *creates* the window, - but glut will not do anything else with this window before - 'run' got called */ - OSPRAY_GLUT3D_INTERFACE void create(const char *title, - const vec2i &size=defaultInitSize, - bool fullScreen = false); - - /*! clear the frame buffer color and depth bits */ - OSPRAY_GLUT3D_INTERFACE void clearPixels(); - - /*! draw uint32_t pixels into the GLUT window (assumes window and buffer - * dimensions are equal) */ - OSPRAY_GLUT3D_INTERFACE void drawPixels(const uint32_t *framebuffer); - - /*! draw float4 pixels into the GLUT window (assumes window and buffer - * dimensions are equal) */ - OSPRAY_GLUT3D_INTERFACE void drawPixels(const vec3fa *framebuffer); - - // ------------------------------------------------------------------ - // camera helper code - // ------------------------------------------------------------------ - OSPRAY_GLUT3D_INTERFACE void snapUp(); - /*! set 'up' vector. if this vector is '0,0,0' the viewer will - *not* apply the up-vector after camera manipulation */ - OSPRAY_GLUT3D_INTERFACE virtual void setZUp(const vec3f &up); - OSPRAY_GLUT3D_INTERFACE void noZUp() { setZUp(vec3f(0.f)); } - - // ------------------------------------------------------------------ - // internal state variables - // ------------------------------------------------------------------ - public: - vec2i lastMousePos; /*! last mouse screen position of mouse before - current motion */ - vec2i currMousePos; /*! current screen position of mouse */ - int64_t lastButtonState, currButtonState, currModifiers; - ViewPort viewPort; - box3f worldBounds; /*!< world bounds, to automatically set viewPort - lookat, mouse speed, etc */ - int32_t windowID; - vec2i windowSize; - /*! camera speed modifier - affects how many units the camera - _moves_ with each unit on the screen */ - float motionSpeed; - /*! camera rotation speed modifier - affects how many units the - camera _rotates_ with each unit on the screen */ - float rotateSpeed; - FrameBufferMode frameBufferMode; - - /*! recompute current viewPort's frame from cameras 'from', - 'at', 'up' values. */ - OSPRAY_GLUT3D_INTERFACE void computeFrame(); - - OSPRAY_GLUT3D_INTERFACE static Glut3DWidget *activeWindow; - /*! pointer to the frame buffer data. it is the repsonsiblity of - the applicatoin derived from this class to properly allocate - and deallocate the frame buffer pointer */ - union { - /*! uchar[4] RGBA-framebuffer, if applicable */ - uint32_t *ucharFB; - /*! float[4] RGBA-framebuffer, if applicable */ - vec3fa *floatFB; - }; - friend void glut3dReshape(int32_t x, int32_t y); - friend void glut3dDisplay(void); - friend void glut3dKeyboard(char key, int32_t x, int32_t y); - friend void glut3dIdle(void); - friend void glut3dMotionFunc(int32_t x, int32_t y); - friend void glut3dMouseFunc(int32_t whichButton, int32_t released, - int32_t x, int32_t y); - - OSPRAY_GLUT3D_INTERFACE virtual void keypress(char key, - const vec2i &where); - OSPRAY_GLUT3D_INTERFACE virtual void specialkey(int32_t key, - const vec2i &where); - }; - - std::ostream &operator<<(std::ostream &o, const Glut3DWidget::ViewPort &cam); - } -} - diff --git a/apps/common/xml/XML.cpp b/apps/common/xml/XML.cpp index d3b03a53f8..ddf1000381 100644 --- a/apps/common/xml/XML.cpp +++ b/apps/common/xml/XML.cpp @@ -260,7 +260,8 @@ namespace ospray { consume(s,"?>"); return true; } - if (!isWhite(*s)) return false; ++s; + if (!isWhite(*s)) return false; + ++s; skipWhites(s); @@ -360,7 +361,6 @@ namespace ospray { (void)rc; std::shared_ptr doc = std::make_shared(); doc->fileName = fn; - bool valid = false; parseXML(doc,mem); delete[] mem; fclose(file); diff --git a/apps/qtViewer/CMakeLists.txt b/apps/exampleViewer/CMakeLists.txt similarity index 51% rename from apps/qtViewer/CMakeLists.txt rename to apps/exampleViewer/CMakeLists.txt index 3917f2b476..b0d0af3cb7 100644 --- a/apps/qtViewer/CMakeLists.txt +++ b/apps/exampleViewer/CMakeLists.txt @@ -14,48 +14,51 @@ ## limitations under the License. ## ## ======================================================================== ## -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/) -INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) - -SET(LIBS ${LIBS} ${GLUT_LIBRARIES} ${OPENGL_LIBRARIES}) - -FIND_PACKAGE(Qt4 REQUIRED QtGui QtOpenGL) -SET(QT_USE_QTOPENGL TRUE) -FIND_PACKAGE(OpenGL REQUIRED) -INCLUDE(${QT_USE_FILE}) -SET(LIBS ${LIBS} Qt4::QtGui Qt4::QtOpenGL ${OPENGL_LIBRARIES}) - -SET(SRCS ${SRCS} - # qt helper widgets - widgets/affineSpaceManipulator/HelperGeometry.cpp - widgets/affineSpaceManipulator/QAffineSpaceManipulator.cpp - widgets/transferFunction/QTransferFunctionEditor.cpp - widgets/lightManipulator/QLightManipulator.cpp - - # modelviewer proper - ModelViewer.cpp - FPSCounter.cpp - - # the (th)ing to launch them all, and in darkness bind them... - main.cpp - ) - -SET(MOC_HEADERS - ModelViewer.h - widgets/transferFunction/QTransferFunctionEditor.h - widgets/affineSpaceManipulator/QAffineSpaceManipulator.h - widgets/lightManipulator/QLightManipulator.h - ) - -QT4_WRAP_CPP(MOC_OUTFILES ${MOC_HEADERS}) - -OSPRAY_CREATE_APPLICATION(ospQtViewer - ${SRCS} - ${MOC_OUTFILES} -LINK - ${LIBS} +find_package(OpenGL REQUIRED) + +ADD_DEFINITIONS(-DDW=1) + +############################################################## +# Build support libs +############################################################## + +add_subdirectory(common) +add_subdirectory(widgets) + +############################################################## +# Build app +############################################################## + +include_directories( + ${CMAKE_SOURCE_DIR} + common/gl3w + common/imgui + ${OPENGL_INCLUDE_DIR} +) + +ospray_create_application(ospExampleViewerSg + ospExampleViewerSg.cpp + LINK + ${OPENGL_LIBRARIES} + glfw + gl3w + imgui + ospray + ospray_common + ospray_commandline + ospray_imgui3d_sg ospray_sg - ${TBB_LIBRARY_MALLOC} - ${TBB_LIBRARY} +) + +ospray_create_application(ospExampleViewer + ospExampleViewer.cpp + LINK + ${OPENGL_LIBRARIES} + glfw + gl3w + imgui + ospray + ospray_common + ospray_commandline + ospray_imgui3d ) diff --git a/apps/exampleViewer/cmake/clang.cmake b/apps/exampleViewer/cmake/clang.cmake new file mode 100644 index 0000000000..56ea1b85de --- /dev/null +++ b/apps/exampleViewer/cmake/clang.cmake @@ -0,0 +1,22 @@ +## ======================================================================== ## +## Copyright 2009-2015 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +set(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") + +if (APPLE) + set(CMAKE_CXX_FLAGS + "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7 -stdlib=libc++") +endif (APPLE) diff --git a/apps/exampleViewer/cmake/gcc.cmake b/apps/exampleViewer/cmake/gcc.cmake new file mode 100644 index 0000000000..a3c71a0974 --- /dev/null +++ b/apps/exampleViewer/cmake/gcc.cmake @@ -0,0 +1,21 @@ +## ======================================================================== ## +## Copyright 2009-2015 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +set(CMAKE_CXX_FLAGS "-fPIC -std=c++11 ${CMAKE_CXX_FLAGS}") + +if (APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") +endif (APPLE) diff --git a/apps/exampleViewer/cmake/icc.cmake b/apps/exampleViewer/cmake/icc.cmake new file mode 100644 index 0000000000..b94991095d --- /dev/null +++ b/apps/exampleViewer/cmake/icc.cmake @@ -0,0 +1,22 @@ +## ======================================================================== ## +## Copyright 2009-2015 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +set(CMAKE_CXX_FLAGS "-fPIC -std=c++11 -static-intel ${CMAKE_CXX_FLAGS}") + +if (APPLE) + set (CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS_INIT} -dynamiclib) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") +endif (APPLE) diff --git a/apps/exampleViewer/cmake/msvc.cmake b/apps/exampleViewer/cmake/msvc.cmake new file mode 100644 index 0000000000..0959e436f5 --- /dev/null +++ b/apps/exampleViewer/cmake/msvc.cmake @@ -0,0 +1,30 @@ +## ======================================================================== ## +## Copyright 2009-2015 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +set(COMMON_CXX_FLAGS "/EHsc /MP /GR ") + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${COMMON_CXX_FLAGS}") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${COMMON_CXX_FLAGS} /Ox /fp:fast /Oi /Gy ") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} ${COMMON_CXX_FLAGS} /Ox /fp:fast /Oi /Gy ") + +# optionally use static runtime library +option(USE_STATIC_RUNTIME "Use the static version of the C/C++ runtime library." ON) +if (USE_STATIC_RUNTIME) + string(REPLACE "/MDd" "/MTd" CMAKE_CXX_FLAGS_DEBUG ${CMAKE_CXX_FLAGS_DEBUG}) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELWITHDEBINFO ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}) + string(REPLACE "/MD" "/MT" CMAKE_CXX_FLAGS_RELEASE ${CMAKE_CXX_FLAGS_RELEASE}) +endif() + diff --git a/apps/exampleViewer/common/CMakeLists.txt b/apps/exampleViewer/common/CMakeLists.txt new file mode 100644 index 0000000000..f7c4f1c7b6 --- /dev/null +++ b/apps/exampleViewer/common/CMakeLists.txt @@ -0,0 +1,72 @@ +## ======================================================================== ## +## Copyright 2009-2017 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +if (NOT WIN32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") +endif() + +############################################################## +# Find or build GLFW +############################################################## + +find_package(GLFW) + +if (NOT GLFW_FOUND) + message(STATUS "Building GLFW from included source") + + set(GLFW_BUILD_DOCS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "" FORCE) + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + set(GLFW_INSTALL OFF CACHE BOOL "" FORCE) + + if (USE_STATIC_RUNTIME) + set(USE_MSVC_RUNTIME_LIBRARY_DLL OFF) + else() + set(USE_MSVC_RUNTIME_LIBRARY_DLL ON) + endif() + set(USE_MSVC_RUNTIME_LIBRARY_DLL ${USE_MSVC_RUNTIME_LIBRARY_DLL} CACHE BOOL "" FORCE) + MARK_AS_ADVANCED(USE_MSVC_RUNTIME_LIBRARY_DLL) + + add_subdirectory(glfw) + + set(GLFW_INCLUDE_DIRS ${CMAKE_CURRENT_LIST_DIR}/glfw/include PARENT_SCOPE) + set(GLFW_LIBRARIES glfw PARENT_SCOPE) +else() + message(STATUS "Using GLFW found in the environment") +endif() + +############################################################## +# Build gl3w +############################################################## + +IF (USE_STATIC_RUNTIME) + STRING(REPLACE "/MDd" "/MTd" CMAKE_C_FLAGS_DEBUG ${CMAKE_C_FLAGS_DEBUG}) + STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELWITHDEBINFO ${CMAKE_C_FLAGS_RELWITHDEBINFO}) + STRING(REPLACE "/MD" "/MT" CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) +ENDIF() +add_subdirectory(gl3w) + +############################################################## +# Build imgui +############################################################## + +add_subdirectory(imgui) + +############################################################## +# Build ospImGui utilities +############################################################## + +add_subdirectory(util) diff --git a/apps/exampleViewer/common/gl3w/CMakeLists.txt b/apps/exampleViewer/common/gl3w/CMakeLists.txt new file mode 100644 index 0000000000..5904868c2b --- /dev/null +++ b/apps/exampleViewer/common/gl3w/CMakeLists.txt @@ -0,0 +1,28 @@ +## ======================================================================== ## +## Copyright 2009-2015 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + + +############################################################## +# Build gl3w +############################################################## + +set(LIB_NAME gl3w) + +include_directories(${CMAKE_CURRENT_LIST_DIR}) + +add_library(${LIB_NAME} STATIC gl3w.c) + +target_link_libraries(${LIB_NAME} ${OPENGL_LIBRARIES}) diff --git a/apps/exampleViewer/common/gl3w/GL/gl3w.h b/apps/exampleViewer/common/gl3w/GL/gl3w.h new file mode 100644 index 0000000000..ee563f8d40 --- /dev/null +++ b/apps/exampleViewer/common/gl3w/GL/gl3w.h @@ -0,0 +1,1234 @@ +#ifndef __gl3w_h_ +#define __gl3w_h_ + +#include + +#ifndef __gl_h_ +#define __gl_h_ +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* gl3w api */ +int gl3wInit(void); +int gl3wIsSupported(int major, int minor); +void *gl3wGetProcAddress(const char *proc); + +/* OpenGL functions */ +extern PFNGLCULLFACEPROC gl3wCullFace; +extern PFNGLFRONTFACEPROC gl3wFrontFace; +extern PFNGLHINTPROC gl3wHint; +extern PFNGLLINEWIDTHPROC gl3wLineWidth; +extern PFNGLPOINTSIZEPROC gl3wPointSize; +extern PFNGLPOLYGONMODEPROC gl3wPolygonMode; +extern PFNGLSCISSORPROC gl3wScissor; +extern PFNGLTEXPARAMETERFPROC gl3wTexParameterf; +extern PFNGLTEXPARAMETERFVPROC gl3wTexParameterfv; +extern PFNGLTEXPARAMETERIPROC gl3wTexParameteri; +extern PFNGLTEXPARAMETERIVPROC gl3wTexParameteriv; +extern PFNGLTEXIMAGE1DPROC gl3wTexImage1D; +extern PFNGLTEXIMAGE2DPROC gl3wTexImage2D; +extern PFNGLDRAWBUFFERPROC gl3wDrawBuffer; +extern PFNGLCLEARPROC gl3wClear; +extern PFNGLCLEARCOLORPROC gl3wClearColor; +extern PFNGLCLEARSTENCILPROC gl3wClearStencil; +extern PFNGLCLEARDEPTHPROC gl3wClearDepth; +extern PFNGLSTENCILMASKPROC gl3wStencilMask; +extern PFNGLCOLORMASKPROC gl3wColorMask; +extern PFNGLDEPTHMASKPROC gl3wDepthMask; +extern PFNGLDISABLEPROC gl3wDisable; +extern PFNGLENABLEPROC gl3wEnable; +extern PFNGLFINISHPROC gl3wFinish; +extern PFNGLFLUSHPROC gl3wFlush; +extern PFNGLBLENDFUNCPROC gl3wBlendFunc; +extern PFNGLLOGICOPPROC gl3wLogicOp; +extern PFNGLSTENCILFUNCPROC gl3wStencilFunc; +extern PFNGLSTENCILOPPROC gl3wStencilOp; +extern PFNGLDEPTHFUNCPROC gl3wDepthFunc; +extern PFNGLPIXELSTOREFPROC gl3wPixelStoref; +extern PFNGLPIXELSTOREIPROC gl3wPixelStorei; +extern PFNGLREADBUFFERPROC gl3wReadBuffer; +extern PFNGLREADPIXELSPROC gl3wReadPixels; +extern PFNGLGETBOOLEANVPROC gl3wGetBooleanv; +extern PFNGLGETDOUBLEVPROC gl3wGetDoublev; +extern PFNGLGETERRORPROC gl3wGetError; +extern PFNGLGETFLOATVPROC gl3wGetFloatv; +extern PFNGLGETINTEGERVPROC gl3wGetIntegerv; +extern PFNGLGETSTRINGPROC gl3wGetString; +extern PFNGLGETTEXIMAGEPROC gl3wGetTexImage; +extern PFNGLGETTEXPARAMETERFVPROC gl3wGetTexParameterfv; +extern PFNGLGETTEXPARAMETERIVPROC gl3wGetTexParameteriv; +extern PFNGLGETTEXLEVELPARAMETERFVPROC gl3wGetTexLevelParameterfv; +extern PFNGLGETTEXLEVELPARAMETERIVPROC gl3wGetTexLevelParameteriv; +extern PFNGLISENABLEDPROC gl3wIsEnabled; +extern PFNGLDEPTHRANGEPROC gl3wDepthRange; +extern PFNGLVIEWPORTPROC gl3wViewport; +extern PFNGLDRAWARRAYSPROC gl3wDrawArrays; +extern PFNGLDRAWELEMENTSPROC gl3wDrawElements; +extern PFNGLGETPOINTERVPROC gl3wGetPointerv; +extern PFNGLPOLYGONOFFSETPROC gl3wPolygonOffset; +extern PFNGLCOPYTEXIMAGE1DPROC gl3wCopyTexImage1D; +extern PFNGLCOPYTEXIMAGE2DPROC gl3wCopyTexImage2D; +extern PFNGLCOPYTEXSUBIMAGE1DPROC gl3wCopyTexSubImage1D; +extern PFNGLCOPYTEXSUBIMAGE2DPROC gl3wCopyTexSubImage2D; +extern PFNGLTEXSUBIMAGE1DPROC gl3wTexSubImage1D; +extern PFNGLTEXSUBIMAGE2DPROC gl3wTexSubImage2D; +extern PFNGLBINDTEXTUREPROC gl3wBindTexture; +extern PFNGLDELETETEXTURESPROC gl3wDeleteTextures; +extern PFNGLGENTEXTURESPROC gl3wGenTextures; +extern PFNGLISTEXTUREPROC gl3wIsTexture; +extern PFNGLBLENDCOLORPROC gl3wBlendColor; +extern PFNGLBLENDEQUATIONPROC gl3wBlendEquation; +extern PFNGLDRAWRANGEELEMENTSPROC gl3wDrawRangeElements; +extern PFNGLTEXIMAGE3DPROC gl3wTexImage3D; +extern PFNGLTEXSUBIMAGE3DPROC gl3wTexSubImage3D; +extern PFNGLCOPYTEXSUBIMAGE3DPROC gl3wCopyTexSubImage3D; +extern PFNGLACTIVETEXTUREPROC gl3wActiveTexture; +extern PFNGLSAMPLECOVERAGEPROC gl3wSampleCoverage; +extern PFNGLCOMPRESSEDTEXIMAGE3DPROC gl3wCompressedTexImage3D; +extern PFNGLCOMPRESSEDTEXIMAGE2DPROC gl3wCompressedTexImage2D; +extern PFNGLCOMPRESSEDTEXIMAGE1DPROC gl3wCompressedTexImage1D; +extern PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC gl3wCompressedTexSubImage3D; +extern PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC gl3wCompressedTexSubImage2D; +extern PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC gl3wCompressedTexSubImage1D; +extern PFNGLGETCOMPRESSEDTEXIMAGEPROC gl3wGetCompressedTexImage; +extern PFNGLBLENDFUNCSEPARATEPROC gl3wBlendFuncSeparate; +extern PFNGLMULTIDRAWARRAYSPROC gl3wMultiDrawArrays; +extern PFNGLMULTIDRAWELEMENTSPROC gl3wMultiDrawElements; +extern PFNGLPOINTPARAMETERFPROC gl3wPointParameterf; +extern PFNGLPOINTPARAMETERFVPROC gl3wPointParameterfv; +extern PFNGLPOINTPARAMETERIPROC gl3wPointParameteri; +extern PFNGLPOINTPARAMETERIVPROC gl3wPointParameteriv; +extern PFNGLGENQUERIESPROC gl3wGenQueries; +extern PFNGLDELETEQUERIESPROC gl3wDeleteQueries; +extern PFNGLISQUERYPROC gl3wIsQuery; +extern PFNGLBEGINQUERYPROC gl3wBeginQuery; +extern PFNGLENDQUERYPROC gl3wEndQuery; +extern PFNGLGETQUERYIVPROC gl3wGetQueryiv; +extern PFNGLGETQUERYOBJECTIVPROC gl3wGetQueryObjectiv; +extern PFNGLGETQUERYOBJECTUIVPROC gl3wGetQueryObjectuiv; +extern PFNGLBINDBUFFERPROC gl3wBindBuffer; +extern PFNGLDELETEBUFFERSPROC gl3wDeleteBuffers; +extern PFNGLGENBUFFERSPROC gl3wGenBuffers; +extern PFNGLISBUFFERPROC gl3wIsBuffer; +extern PFNGLBUFFERDATAPROC gl3wBufferData; +extern PFNGLBUFFERSUBDATAPROC gl3wBufferSubData; +extern PFNGLGETBUFFERSUBDATAPROC gl3wGetBufferSubData; +extern PFNGLMAPBUFFERPROC gl3wMapBuffer; +extern PFNGLUNMAPBUFFERPROC gl3wUnmapBuffer; +extern PFNGLGETBUFFERPARAMETERIVPROC gl3wGetBufferParameteriv; +extern PFNGLGETBUFFERPOINTERVPROC gl3wGetBufferPointerv; +extern PFNGLBLENDEQUATIONSEPARATEPROC gl3wBlendEquationSeparate; +extern PFNGLDRAWBUFFERSPROC gl3wDrawBuffers; +extern PFNGLSTENCILOPSEPARATEPROC gl3wStencilOpSeparate; +extern PFNGLSTENCILFUNCSEPARATEPROC gl3wStencilFuncSeparate; +extern PFNGLSTENCILMASKSEPARATEPROC gl3wStencilMaskSeparate; +extern PFNGLATTACHSHADERPROC gl3wAttachShader; +extern PFNGLBINDATTRIBLOCATIONPROC gl3wBindAttribLocation; +extern PFNGLCOMPILESHADERPROC gl3wCompileShader; +extern PFNGLCREATEPROGRAMPROC gl3wCreateProgram; +extern PFNGLCREATESHADERPROC gl3wCreateShader; +extern PFNGLDELETEPROGRAMPROC gl3wDeleteProgram; +extern PFNGLDELETESHADERPROC gl3wDeleteShader; +extern PFNGLDETACHSHADERPROC gl3wDetachShader; +extern PFNGLDISABLEVERTEXATTRIBARRAYPROC gl3wDisableVertexAttribArray; +extern PFNGLENABLEVERTEXATTRIBARRAYPROC gl3wEnableVertexAttribArray; +extern PFNGLGETACTIVEATTRIBPROC gl3wGetActiveAttrib; +extern PFNGLGETACTIVEUNIFORMPROC gl3wGetActiveUniform; +extern PFNGLGETATTACHEDSHADERSPROC gl3wGetAttachedShaders; +extern PFNGLGETATTRIBLOCATIONPROC gl3wGetAttribLocation; +extern PFNGLGETPROGRAMIVPROC gl3wGetProgramiv; +extern PFNGLGETPROGRAMINFOLOGPROC gl3wGetProgramInfoLog; +extern PFNGLGETSHADERIVPROC gl3wGetShaderiv; +extern PFNGLGETSHADERINFOLOGPROC gl3wGetShaderInfoLog; +extern PFNGLGETSHADERSOURCEPROC gl3wGetShaderSource; +extern PFNGLGETUNIFORMLOCATIONPROC gl3wGetUniformLocation; +extern PFNGLGETUNIFORMFVPROC gl3wGetUniformfv; +extern PFNGLGETUNIFORMIVPROC gl3wGetUniformiv; +extern PFNGLGETVERTEXATTRIBDVPROC gl3wGetVertexAttribdv; +extern PFNGLGETVERTEXATTRIBFVPROC gl3wGetVertexAttribfv; +extern PFNGLGETVERTEXATTRIBIVPROC gl3wGetVertexAttribiv; +extern PFNGLGETVERTEXATTRIBPOINTERVPROC gl3wGetVertexAttribPointerv; +extern PFNGLISPROGRAMPROC gl3wIsProgram; +extern PFNGLISSHADERPROC gl3wIsShader; +extern PFNGLLINKPROGRAMPROC gl3wLinkProgram; +extern PFNGLSHADERSOURCEPROC gl3wShaderSource; +extern PFNGLUSEPROGRAMPROC gl3wUseProgram; +extern PFNGLUNIFORM1FPROC gl3wUniform1f; +extern PFNGLUNIFORM2FPROC gl3wUniform2f; +extern PFNGLUNIFORM3FPROC gl3wUniform3f; +extern PFNGLUNIFORM4FPROC gl3wUniform4f; +extern PFNGLUNIFORM1IPROC gl3wUniform1i; +extern PFNGLUNIFORM2IPROC gl3wUniform2i; +extern PFNGLUNIFORM3IPROC gl3wUniform3i; +extern PFNGLUNIFORM4IPROC gl3wUniform4i; +extern PFNGLUNIFORM1FVPROC gl3wUniform1fv; +extern PFNGLUNIFORM2FVPROC gl3wUniform2fv; +extern PFNGLUNIFORM3FVPROC gl3wUniform3fv; +extern PFNGLUNIFORM4FVPROC gl3wUniform4fv; +extern PFNGLUNIFORM1IVPROC gl3wUniform1iv; +extern PFNGLUNIFORM2IVPROC gl3wUniform2iv; +extern PFNGLUNIFORM3IVPROC gl3wUniform3iv; +extern PFNGLUNIFORM4IVPROC gl3wUniform4iv; +extern PFNGLUNIFORMMATRIX2FVPROC gl3wUniformMatrix2fv; +extern PFNGLUNIFORMMATRIX3FVPROC gl3wUniformMatrix3fv; +extern PFNGLUNIFORMMATRIX4FVPROC gl3wUniformMatrix4fv; +extern PFNGLVALIDATEPROGRAMPROC gl3wValidateProgram; +extern PFNGLVERTEXATTRIB1DPROC gl3wVertexAttrib1d; +extern PFNGLVERTEXATTRIB1DVPROC gl3wVertexAttrib1dv; +extern PFNGLVERTEXATTRIB1FPROC gl3wVertexAttrib1f; +extern PFNGLVERTEXATTRIB1FVPROC gl3wVertexAttrib1fv; +extern PFNGLVERTEXATTRIB1SPROC gl3wVertexAttrib1s; +extern PFNGLVERTEXATTRIB1SVPROC gl3wVertexAttrib1sv; +extern PFNGLVERTEXATTRIB2DPROC gl3wVertexAttrib2d; +extern PFNGLVERTEXATTRIB2DVPROC gl3wVertexAttrib2dv; +extern PFNGLVERTEXATTRIB2FPROC gl3wVertexAttrib2f; +extern PFNGLVERTEXATTRIB2FVPROC gl3wVertexAttrib2fv; +extern PFNGLVERTEXATTRIB2SPROC gl3wVertexAttrib2s; +extern PFNGLVERTEXATTRIB2SVPROC gl3wVertexAttrib2sv; +extern PFNGLVERTEXATTRIB3DPROC gl3wVertexAttrib3d; +extern PFNGLVERTEXATTRIB3DVPROC gl3wVertexAttrib3dv; +extern PFNGLVERTEXATTRIB3FPROC gl3wVertexAttrib3f; +extern PFNGLVERTEXATTRIB3FVPROC gl3wVertexAttrib3fv; +extern PFNGLVERTEXATTRIB3SPROC gl3wVertexAttrib3s; +extern PFNGLVERTEXATTRIB3SVPROC gl3wVertexAttrib3sv; +extern PFNGLVERTEXATTRIB4NBVPROC gl3wVertexAttrib4Nbv; +extern PFNGLVERTEXATTRIB4NIVPROC gl3wVertexAttrib4Niv; +extern PFNGLVERTEXATTRIB4NSVPROC gl3wVertexAttrib4Nsv; +extern PFNGLVERTEXATTRIB4NUBPROC gl3wVertexAttrib4Nub; +extern PFNGLVERTEXATTRIB4NUBVPROC gl3wVertexAttrib4Nubv; +extern PFNGLVERTEXATTRIB4NUIVPROC gl3wVertexAttrib4Nuiv; +extern PFNGLVERTEXATTRIB4NUSVPROC gl3wVertexAttrib4Nusv; +extern PFNGLVERTEXATTRIB4BVPROC gl3wVertexAttrib4bv; +extern PFNGLVERTEXATTRIB4DPROC gl3wVertexAttrib4d; +extern PFNGLVERTEXATTRIB4DVPROC gl3wVertexAttrib4dv; +extern PFNGLVERTEXATTRIB4FPROC gl3wVertexAttrib4f; +extern PFNGLVERTEXATTRIB4FVPROC gl3wVertexAttrib4fv; +extern PFNGLVERTEXATTRIB4IVPROC gl3wVertexAttrib4iv; +extern PFNGLVERTEXATTRIB4SPROC gl3wVertexAttrib4s; +extern PFNGLVERTEXATTRIB4SVPROC gl3wVertexAttrib4sv; +extern PFNGLVERTEXATTRIB4UBVPROC gl3wVertexAttrib4ubv; +extern PFNGLVERTEXATTRIB4UIVPROC gl3wVertexAttrib4uiv; +extern PFNGLVERTEXATTRIB4USVPROC gl3wVertexAttrib4usv; +extern PFNGLVERTEXATTRIBPOINTERPROC gl3wVertexAttribPointer; +extern PFNGLUNIFORMMATRIX2X3FVPROC gl3wUniformMatrix2x3fv; +extern PFNGLUNIFORMMATRIX3X2FVPROC gl3wUniformMatrix3x2fv; +extern PFNGLUNIFORMMATRIX2X4FVPROC gl3wUniformMatrix2x4fv; +extern PFNGLUNIFORMMATRIX4X2FVPROC gl3wUniformMatrix4x2fv; +extern PFNGLUNIFORMMATRIX3X4FVPROC gl3wUniformMatrix3x4fv; +extern PFNGLUNIFORMMATRIX4X3FVPROC gl3wUniformMatrix4x3fv; +extern PFNGLCOLORMASKIPROC gl3wColorMaski; +extern PFNGLGETBOOLEANI_VPROC gl3wGetBooleani_v; +extern PFNGLGETINTEGERI_VPROC gl3wGetIntegeri_v; +extern PFNGLENABLEIPROC gl3wEnablei; +extern PFNGLDISABLEIPROC gl3wDisablei; +extern PFNGLISENABLEDIPROC gl3wIsEnabledi; +extern PFNGLBEGINTRANSFORMFEEDBACKPROC gl3wBeginTransformFeedback; +extern PFNGLENDTRANSFORMFEEDBACKPROC gl3wEndTransformFeedback; +extern PFNGLBINDBUFFERRANGEPROC gl3wBindBufferRange; +extern PFNGLBINDBUFFERBASEPROC gl3wBindBufferBase; +extern PFNGLTRANSFORMFEEDBACKVARYINGSPROC gl3wTransformFeedbackVaryings; +extern PFNGLGETTRANSFORMFEEDBACKVARYINGPROC gl3wGetTransformFeedbackVarying; +extern PFNGLCLAMPCOLORPROC gl3wClampColor; +extern PFNGLBEGINCONDITIONALRENDERPROC gl3wBeginConditionalRender; +extern PFNGLENDCONDITIONALRENDERPROC gl3wEndConditionalRender; +extern PFNGLVERTEXATTRIBIPOINTERPROC gl3wVertexAttribIPointer; +extern PFNGLGETVERTEXATTRIBIIVPROC gl3wGetVertexAttribIiv; +extern PFNGLGETVERTEXATTRIBIUIVPROC gl3wGetVertexAttribIuiv; +extern PFNGLVERTEXATTRIBI1IPROC gl3wVertexAttribI1i; +extern PFNGLVERTEXATTRIBI2IPROC gl3wVertexAttribI2i; +extern PFNGLVERTEXATTRIBI3IPROC gl3wVertexAttribI3i; +extern PFNGLVERTEXATTRIBI4IPROC gl3wVertexAttribI4i; +extern PFNGLVERTEXATTRIBI1UIPROC gl3wVertexAttribI1ui; +extern PFNGLVERTEXATTRIBI2UIPROC gl3wVertexAttribI2ui; +extern PFNGLVERTEXATTRIBI3UIPROC gl3wVertexAttribI3ui; +extern PFNGLVERTEXATTRIBI4UIPROC gl3wVertexAttribI4ui; +extern PFNGLVERTEXATTRIBI1IVPROC gl3wVertexAttribI1iv; +extern PFNGLVERTEXATTRIBI2IVPROC gl3wVertexAttribI2iv; +extern PFNGLVERTEXATTRIBI3IVPROC gl3wVertexAttribI3iv; +extern PFNGLVERTEXATTRIBI4IVPROC gl3wVertexAttribI4iv; +extern PFNGLVERTEXATTRIBI1UIVPROC gl3wVertexAttribI1uiv; +extern PFNGLVERTEXATTRIBI2UIVPROC gl3wVertexAttribI2uiv; +extern PFNGLVERTEXATTRIBI3UIVPROC gl3wVertexAttribI3uiv; +extern PFNGLVERTEXATTRIBI4UIVPROC gl3wVertexAttribI4uiv; +extern PFNGLVERTEXATTRIBI4BVPROC gl3wVertexAttribI4bv; +extern PFNGLVERTEXATTRIBI4SVPROC gl3wVertexAttribI4sv; +extern PFNGLVERTEXATTRIBI4UBVPROC gl3wVertexAttribI4ubv; +extern PFNGLVERTEXATTRIBI4USVPROC gl3wVertexAttribI4usv; +extern PFNGLGETUNIFORMUIVPROC gl3wGetUniformuiv; +extern PFNGLBINDFRAGDATALOCATIONPROC gl3wBindFragDataLocation; +extern PFNGLGETFRAGDATALOCATIONPROC gl3wGetFragDataLocation; +extern PFNGLUNIFORM1UIPROC gl3wUniform1ui; +extern PFNGLUNIFORM2UIPROC gl3wUniform2ui; +extern PFNGLUNIFORM3UIPROC gl3wUniform3ui; +extern PFNGLUNIFORM4UIPROC gl3wUniform4ui; +extern PFNGLUNIFORM1UIVPROC gl3wUniform1uiv; +extern PFNGLUNIFORM2UIVPROC gl3wUniform2uiv; +extern PFNGLUNIFORM3UIVPROC gl3wUniform3uiv; +extern PFNGLUNIFORM4UIVPROC gl3wUniform4uiv; +extern PFNGLTEXPARAMETERIIVPROC gl3wTexParameterIiv; +extern PFNGLTEXPARAMETERIUIVPROC gl3wTexParameterIuiv; +extern PFNGLGETTEXPARAMETERIIVPROC gl3wGetTexParameterIiv; +extern PFNGLGETTEXPARAMETERIUIVPROC gl3wGetTexParameterIuiv; +extern PFNGLCLEARBUFFERIVPROC gl3wClearBufferiv; +extern PFNGLCLEARBUFFERUIVPROC gl3wClearBufferuiv; +extern PFNGLCLEARBUFFERFVPROC gl3wClearBufferfv; +extern PFNGLCLEARBUFFERFIPROC gl3wClearBufferfi; +extern PFNGLGETSTRINGIPROC gl3wGetStringi; +extern PFNGLDRAWARRAYSINSTANCEDPROC gl3wDrawArraysInstanced; +extern PFNGLDRAWELEMENTSINSTANCEDPROC gl3wDrawElementsInstanced; +extern PFNGLTEXBUFFERPROC gl3wTexBuffer; +extern PFNGLPRIMITIVERESTARTINDEXPROC gl3wPrimitiveRestartIndex; +extern PFNGLGETINTEGER64I_VPROC gl3wGetInteger64i_v; +extern PFNGLGETBUFFERPARAMETERI64VPROC gl3wGetBufferParameteri64v; +extern PFNGLFRAMEBUFFERTEXTUREPROC gl3wFramebufferTexture; +extern PFNGLVERTEXATTRIBDIVISORPROC gl3wVertexAttribDivisor; +extern PFNGLMINSAMPLESHADINGPROC gl3wMinSampleShading; +extern PFNGLBLENDEQUATIONIPROC gl3wBlendEquationi; +extern PFNGLBLENDEQUATIONSEPARATEIPROC gl3wBlendEquationSeparatei; +extern PFNGLBLENDFUNCIPROC gl3wBlendFunci; +extern PFNGLBLENDFUNCSEPARATEIPROC gl3wBlendFuncSeparatei; +extern PFNGLISRENDERBUFFERPROC gl3wIsRenderbuffer; +extern PFNGLBINDRENDERBUFFERPROC gl3wBindRenderbuffer; +extern PFNGLDELETERENDERBUFFERSPROC gl3wDeleteRenderbuffers; +extern PFNGLGENRENDERBUFFERSPROC gl3wGenRenderbuffers; +extern PFNGLRENDERBUFFERSTORAGEPROC gl3wRenderbufferStorage; +extern PFNGLGETRENDERBUFFERPARAMETERIVPROC gl3wGetRenderbufferParameteriv; +extern PFNGLISFRAMEBUFFERPROC gl3wIsFramebuffer; +extern PFNGLBINDFRAMEBUFFERPROC gl3wBindFramebuffer; +extern PFNGLDELETEFRAMEBUFFERSPROC gl3wDeleteFramebuffers; +extern PFNGLGENFRAMEBUFFERSPROC gl3wGenFramebuffers; +extern PFNGLCHECKFRAMEBUFFERSTATUSPROC gl3wCheckFramebufferStatus; +extern PFNGLFRAMEBUFFERTEXTURE1DPROC gl3wFramebufferTexture1D; +extern PFNGLFRAMEBUFFERTEXTURE2DPROC gl3wFramebufferTexture2D; +extern PFNGLFRAMEBUFFERTEXTURE3DPROC gl3wFramebufferTexture3D; +extern PFNGLFRAMEBUFFERRENDERBUFFERPROC gl3wFramebufferRenderbuffer; +extern PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC gl3wGetFramebufferAttachmentParameteriv; +extern PFNGLGENERATEMIPMAPPROC gl3wGenerateMipmap; +extern PFNGLBLITFRAMEBUFFERPROC gl3wBlitFramebuffer; +extern PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC gl3wRenderbufferStorageMultisample; +extern PFNGLFRAMEBUFFERTEXTURELAYERPROC gl3wFramebufferTextureLayer; +extern PFNGLMAPBUFFERRANGEPROC gl3wMapBufferRange; +extern PFNGLFLUSHMAPPEDBUFFERRANGEPROC gl3wFlushMappedBufferRange; +extern PFNGLBINDVERTEXARRAYPROC gl3wBindVertexArray; +extern PFNGLDELETEVERTEXARRAYSPROC gl3wDeleteVertexArrays; +extern PFNGLGENVERTEXARRAYSPROC gl3wGenVertexArrays; +extern PFNGLISVERTEXARRAYPROC gl3wIsVertexArray; +extern PFNGLGETUNIFORMINDICESPROC gl3wGetUniformIndices; +extern PFNGLGETACTIVEUNIFORMSIVPROC gl3wGetActiveUniformsiv; +extern PFNGLGETACTIVEUNIFORMNAMEPROC gl3wGetActiveUniformName; +extern PFNGLGETUNIFORMBLOCKINDEXPROC gl3wGetUniformBlockIndex; +extern PFNGLGETACTIVEUNIFORMBLOCKIVPROC gl3wGetActiveUniformBlockiv; +extern PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC gl3wGetActiveUniformBlockName; +extern PFNGLUNIFORMBLOCKBINDINGPROC gl3wUniformBlockBinding; +extern PFNGLCOPYBUFFERSUBDATAPROC gl3wCopyBufferSubData; +extern PFNGLDRAWELEMENTSBASEVERTEXPROC gl3wDrawElementsBaseVertex; +extern PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC gl3wDrawRangeElementsBaseVertex; +extern PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC gl3wDrawElementsInstancedBaseVertex; +extern PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC gl3wMultiDrawElementsBaseVertex; +extern PFNGLPROVOKINGVERTEXPROC gl3wProvokingVertex; +extern PFNGLFENCESYNCPROC gl3wFenceSync; +extern PFNGLISSYNCPROC gl3wIsSync; +extern PFNGLDELETESYNCPROC gl3wDeleteSync; +extern PFNGLCLIENTWAITSYNCPROC gl3wClientWaitSync; +extern PFNGLWAITSYNCPROC gl3wWaitSync; +extern PFNGLGETINTEGER64VPROC gl3wGetInteger64v; +extern PFNGLGETSYNCIVPROC gl3wGetSynciv; +extern PFNGLTEXIMAGE2DMULTISAMPLEPROC gl3wTexImage2DMultisample; +extern PFNGLTEXIMAGE3DMULTISAMPLEPROC gl3wTexImage3DMultisample; +extern PFNGLGETMULTISAMPLEFVPROC gl3wGetMultisamplefv; +extern PFNGLSAMPLEMASKIPROC gl3wSampleMaski; +extern PFNGLBLENDEQUATIONIARBPROC gl3wBlendEquationiARB; +extern PFNGLBLENDEQUATIONSEPARATEIARBPROC gl3wBlendEquationSeparateiARB; +extern PFNGLBLENDFUNCIARBPROC gl3wBlendFunciARB; +extern PFNGLBLENDFUNCSEPARATEIARBPROC gl3wBlendFuncSeparateiARB; +extern PFNGLMINSAMPLESHADINGARBPROC gl3wMinSampleShadingARB; +extern PFNGLNAMEDSTRINGARBPROC gl3wNamedStringARB; +extern PFNGLDELETENAMEDSTRINGARBPROC gl3wDeleteNamedStringARB; +extern PFNGLCOMPILESHADERINCLUDEARBPROC gl3wCompileShaderIncludeARB; +extern PFNGLISNAMEDSTRINGARBPROC gl3wIsNamedStringARB; +extern PFNGLGETNAMEDSTRINGARBPROC gl3wGetNamedStringARB; +extern PFNGLGETNAMEDSTRINGIVARBPROC gl3wGetNamedStringivARB; +extern PFNGLBINDFRAGDATALOCATIONINDEXEDPROC gl3wBindFragDataLocationIndexed; +extern PFNGLGETFRAGDATAINDEXPROC gl3wGetFragDataIndex; +extern PFNGLGENSAMPLERSPROC gl3wGenSamplers; +extern PFNGLDELETESAMPLERSPROC gl3wDeleteSamplers; +extern PFNGLISSAMPLERPROC gl3wIsSampler; +extern PFNGLBINDSAMPLERPROC gl3wBindSampler; +extern PFNGLSAMPLERPARAMETERIPROC gl3wSamplerParameteri; +extern PFNGLSAMPLERPARAMETERIVPROC gl3wSamplerParameteriv; +extern PFNGLSAMPLERPARAMETERFPROC gl3wSamplerParameterf; +extern PFNGLSAMPLERPARAMETERFVPROC gl3wSamplerParameterfv; +extern PFNGLSAMPLERPARAMETERIIVPROC gl3wSamplerParameterIiv; +extern PFNGLSAMPLERPARAMETERIUIVPROC gl3wSamplerParameterIuiv; +extern PFNGLGETSAMPLERPARAMETERIVPROC gl3wGetSamplerParameteriv; +extern PFNGLGETSAMPLERPARAMETERIIVPROC gl3wGetSamplerParameterIiv; +extern PFNGLGETSAMPLERPARAMETERFVPROC gl3wGetSamplerParameterfv; +extern PFNGLGETSAMPLERPARAMETERIUIVPROC gl3wGetSamplerParameterIuiv; +extern PFNGLQUERYCOUNTERPROC gl3wQueryCounter; +extern PFNGLGETQUERYOBJECTI64VPROC gl3wGetQueryObjecti64v; +extern PFNGLGETQUERYOBJECTUI64VPROC gl3wGetQueryObjectui64v; +extern PFNGLVERTEXP2UIPROC gl3wVertexP2ui; +extern PFNGLVERTEXP2UIVPROC gl3wVertexP2uiv; +extern PFNGLVERTEXP3UIPROC gl3wVertexP3ui; +extern PFNGLVERTEXP3UIVPROC gl3wVertexP3uiv; +extern PFNGLVERTEXP4UIPROC gl3wVertexP4ui; +extern PFNGLVERTEXP4UIVPROC gl3wVertexP4uiv; +extern PFNGLTEXCOORDP1UIPROC gl3wTexCoordP1ui; +extern PFNGLTEXCOORDP1UIVPROC gl3wTexCoordP1uiv; +extern PFNGLTEXCOORDP2UIPROC gl3wTexCoordP2ui; +extern PFNGLTEXCOORDP2UIVPROC gl3wTexCoordP2uiv; +extern PFNGLTEXCOORDP3UIPROC gl3wTexCoordP3ui; +extern PFNGLTEXCOORDP3UIVPROC gl3wTexCoordP3uiv; +extern PFNGLTEXCOORDP4UIPROC gl3wTexCoordP4ui; +extern PFNGLTEXCOORDP4UIVPROC gl3wTexCoordP4uiv; +extern PFNGLMULTITEXCOORDP1UIPROC gl3wMultiTexCoordP1ui; +extern PFNGLMULTITEXCOORDP1UIVPROC gl3wMultiTexCoordP1uiv; +extern PFNGLMULTITEXCOORDP2UIPROC gl3wMultiTexCoordP2ui; +extern PFNGLMULTITEXCOORDP2UIVPROC gl3wMultiTexCoordP2uiv; +extern PFNGLMULTITEXCOORDP3UIPROC gl3wMultiTexCoordP3ui; +extern PFNGLMULTITEXCOORDP3UIVPROC gl3wMultiTexCoordP3uiv; +extern PFNGLMULTITEXCOORDP4UIPROC gl3wMultiTexCoordP4ui; +extern PFNGLMULTITEXCOORDP4UIVPROC gl3wMultiTexCoordP4uiv; +extern PFNGLNORMALP3UIPROC gl3wNormalP3ui; +extern PFNGLNORMALP3UIVPROC gl3wNormalP3uiv; +extern PFNGLCOLORP3UIPROC gl3wColorP3ui; +extern PFNGLCOLORP3UIVPROC gl3wColorP3uiv; +extern PFNGLCOLORP4UIPROC gl3wColorP4ui; +extern PFNGLCOLORP4UIVPROC gl3wColorP4uiv; +extern PFNGLSECONDARYCOLORP3UIPROC gl3wSecondaryColorP3ui; +extern PFNGLSECONDARYCOLORP3UIVPROC gl3wSecondaryColorP3uiv; +extern PFNGLVERTEXATTRIBP1UIPROC gl3wVertexAttribP1ui; +extern PFNGLVERTEXATTRIBP1UIVPROC gl3wVertexAttribP1uiv; +extern PFNGLVERTEXATTRIBP2UIPROC gl3wVertexAttribP2ui; +extern PFNGLVERTEXATTRIBP2UIVPROC gl3wVertexAttribP2uiv; +extern PFNGLVERTEXATTRIBP3UIPROC gl3wVertexAttribP3ui; +extern PFNGLVERTEXATTRIBP3UIVPROC gl3wVertexAttribP3uiv; +extern PFNGLVERTEXATTRIBP4UIPROC gl3wVertexAttribP4ui; +extern PFNGLVERTEXATTRIBP4UIVPROC gl3wVertexAttribP4uiv; +extern PFNGLDRAWARRAYSINDIRECTPROC gl3wDrawArraysIndirect; +extern PFNGLDRAWELEMENTSINDIRECTPROC gl3wDrawElementsIndirect; +extern PFNGLUNIFORM1DPROC gl3wUniform1d; +extern PFNGLUNIFORM2DPROC gl3wUniform2d; +extern PFNGLUNIFORM3DPROC gl3wUniform3d; +extern PFNGLUNIFORM4DPROC gl3wUniform4d; +extern PFNGLUNIFORM1DVPROC gl3wUniform1dv; +extern PFNGLUNIFORM2DVPROC gl3wUniform2dv; +extern PFNGLUNIFORM3DVPROC gl3wUniform3dv; +extern PFNGLUNIFORM4DVPROC gl3wUniform4dv; +extern PFNGLUNIFORMMATRIX2DVPROC gl3wUniformMatrix2dv; +extern PFNGLUNIFORMMATRIX3DVPROC gl3wUniformMatrix3dv; +extern PFNGLUNIFORMMATRIX4DVPROC gl3wUniformMatrix4dv; +extern PFNGLUNIFORMMATRIX2X3DVPROC gl3wUniformMatrix2x3dv; +extern PFNGLUNIFORMMATRIX2X4DVPROC gl3wUniformMatrix2x4dv; +extern PFNGLUNIFORMMATRIX3X2DVPROC gl3wUniformMatrix3x2dv; +extern PFNGLUNIFORMMATRIX3X4DVPROC gl3wUniformMatrix3x4dv; +extern PFNGLUNIFORMMATRIX4X2DVPROC gl3wUniformMatrix4x2dv; +extern PFNGLUNIFORMMATRIX4X3DVPROC gl3wUniformMatrix4x3dv; +extern PFNGLGETUNIFORMDVPROC gl3wGetUniformdv; +extern PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC gl3wGetSubroutineUniformLocation; +extern PFNGLGETSUBROUTINEINDEXPROC gl3wGetSubroutineIndex; +extern PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC gl3wGetActiveSubroutineUniformiv; +extern PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC gl3wGetActiveSubroutineUniformName; +extern PFNGLGETACTIVESUBROUTINENAMEPROC gl3wGetActiveSubroutineName; +extern PFNGLUNIFORMSUBROUTINESUIVPROC gl3wUniformSubroutinesuiv; +extern PFNGLGETUNIFORMSUBROUTINEUIVPROC gl3wGetUniformSubroutineuiv; +extern PFNGLGETPROGRAMSTAGEIVPROC gl3wGetProgramStageiv; +extern PFNGLPATCHPARAMETERIPROC gl3wPatchParameteri; +extern PFNGLPATCHPARAMETERFVPROC gl3wPatchParameterfv; +extern PFNGLBINDTRANSFORMFEEDBACKPROC gl3wBindTransformFeedback; +extern PFNGLDELETETRANSFORMFEEDBACKSPROC gl3wDeleteTransformFeedbacks; +extern PFNGLGENTRANSFORMFEEDBACKSPROC gl3wGenTransformFeedbacks; +extern PFNGLISTRANSFORMFEEDBACKPROC gl3wIsTransformFeedback; +extern PFNGLPAUSETRANSFORMFEEDBACKPROC gl3wPauseTransformFeedback; +extern PFNGLRESUMETRANSFORMFEEDBACKPROC gl3wResumeTransformFeedback; +extern PFNGLDRAWTRANSFORMFEEDBACKPROC gl3wDrawTransformFeedback; +extern PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC gl3wDrawTransformFeedbackStream; +extern PFNGLBEGINQUERYINDEXEDPROC gl3wBeginQueryIndexed; +extern PFNGLENDQUERYINDEXEDPROC gl3wEndQueryIndexed; +extern PFNGLGETQUERYINDEXEDIVPROC gl3wGetQueryIndexediv; +extern PFNGLRELEASESHADERCOMPILERPROC gl3wReleaseShaderCompiler; +extern PFNGLSHADERBINARYPROC gl3wShaderBinary; +extern PFNGLGETSHADERPRECISIONFORMATPROC gl3wGetShaderPrecisionFormat; +extern PFNGLDEPTHRANGEFPROC gl3wDepthRangef; +extern PFNGLCLEARDEPTHFPROC gl3wClearDepthf; +extern PFNGLGETPROGRAMBINARYPROC gl3wGetProgramBinary; +extern PFNGLPROGRAMBINARYPROC gl3wProgramBinary; +extern PFNGLPROGRAMPARAMETERIPROC gl3wProgramParameteri; +extern PFNGLUSEPROGRAMSTAGESPROC gl3wUseProgramStages; +extern PFNGLACTIVESHADERPROGRAMPROC gl3wActiveShaderProgram; +extern PFNGLCREATESHADERPROGRAMVPROC gl3wCreateShaderProgramv; +extern PFNGLBINDPROGRAMPIPELINEPROC gl3wBindProgramPipeline; +extern PFNGLDELETEPROGRAMPIPELINESPROC gl3wDeleteProgramPipelines; +extern PFNGLGENPROGRAMPIPELINESPROC gl3wGenProgramPipelines; +extern PFNGLISPROGRAMPIPELINEPROC gl3wIsProgramPipeline; +extern PFNGLGETPROGRAMPIPELINEIVPROC gl3wGetProgramPipelineiv; +extern PFNGLPROGRAMUNIFORM1IPROC gl3wProgramUniform1i; +extern PFNGLPROGRAMUNIFORM1IVPROC gl3wProgramUniform1iv; +extern PFNGLPROGRAMUNIFORM1FPROC gl3wProgramUniform1f; +extern PFNGLPROGRAMUNIFORM1FVPROC gl3wProgramUniform1fv; +extern PFNGLPROGRAMUNIFORM1DPROC gl3wProgramUniform1d; +extern PFNGLPROGRAMUNIFORM1DVPROC gl3wProgramUniform1dv; +extern PFNGLPROGRAMUNIFORM1UIPROC gl3wProgramUniform1ui; +extern PFNGLPROGRAMUNIFORM1UIVPROC gl3wProgramUniform1uiv; +extern PFNGLPROGRAMUNIFORM2IPROC gl3wProgramUniform2i; +extern PFNGLPROGRAMUNIFORM2IVPROC gl3wProgramUniform2iv; +extern PFNGLPROGRAMUNIFORM2FPROC gl3wProgramUniform2f; +extern PFNGLPROGRAMUNIFORM2FVPROC gl3wProgramUniform2fv; +extern PFNGLPROGRAMUNIFORM2DPROC gl3wProgramUniform2d; +extern PFNGLPROGRAMUNIFORM2DVPROC gl3wProgramUniform2dv; +extern PFNGLPROGRAMUNIFORM2UIPROC gl3wProgramUniform2ui; +extern PFNGLPROGRAMUNIFORM2UIVPROC gl3wProgramUniform2uiv; +extern PFNGLPROGRAMUNIFORM3IPROC gl3wProgramUniform3i; +extern PFNGLPROGRAMUNIFORM3IVPROC gl3wProgramUniform3iv; +extern PFNGLPROGRAMUNIFORM3FPROC gl3wProgramUniform3f; +extern PFNGLPROGRAMUNIFORM3FVPROC gl3wProgramUniform3fv; +extern PFNGLPROGRAMUNIFORM3DPROC gl3wProgramUniform3d; +extern PFNGLPROGRAMUNIFORM3DVPROC gl3wProgramUniform3dv; +extern PFNGLPROGRAMUNIFORM3UIPROC gl3wProgramUniform3ui; +extern PFNGLPROGRAMUNIFORM3UIVPROC gl3wProgramUniform3uiv; +extern PFNGLPROGRAMUNIFORM4IPROC gl3wProgramUniform4i; +extern PFNGLPROGRAMUNIFORM4IVPROC gl3wProgramUniform4iv; +extern PFNGLPROGRAMUNIFORM4FPROC gl3wProgramUniform4f; +extern PFNGLPROGRAMUNIFORM4FVPROC gl3wProgramUniform4fv; +extern PFNGLPROGRAMUNIFORM4DPROC gl3wProgramUniform4d; +extern PFNGLPROGRAMUNIFORM4DVPROC gl3wProgramUniform4dv; +extern PFNGLPROGRAMUNIFORM4UIPROC gl3wProgramUniform4ui; +extern PFNGLPROGRAMUNIFORM4UIVPROC gl3wProgramUniform4uiv; +extern PFNGLPROGRAMUNIFORMMATRIX2FVPROC gl3wProgramUniformMatrix2fv; +extern PFNGLPROGRAMUNIFORMMATRIX3FVPROC gl3wProgramUniformMatrix3fv; +extern PFNGLPROGRAMUNIFORMMATRIX4FVPROC gl3wProgramUniformMatrix4fv; +extern PFNGLPROGRAMUNIFORMMATRIX2DVPROC gl3wProgramUniformMatrix2dv; +extern PFNGLPROGRAMUNIFORMMATRIX3DVPROC gl3wProgramUniformMatrix3dv; +extern PFNGLPROGRAMUNIFORMMATRIX4DVPROC gl3wProgramUniformMatrix4dv; +extern PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC gl3wProgramUniformMatrix2x3fv; +extern PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC gl3wProgramUniformMatrix3x2fv; +extern PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC gl3wProgramUniformMatrix2x4fv; +extern PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC gl3wProgramUniformMatrix4x2fv; +extern PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC gl3wProgramUniformMatrix3x4fv; +extern PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC gl3wProgramUniformMatrix4x3fv; +extern PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC gl3wProgramUniformMatrix2x3dv; +extern PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC gl3wProgramUniformMatrix3x2dv; +extern PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC gl3wProgramUniformMatrix2x4dv; +extern PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC gl3wProgramUniformMatrix4x2dv; +extern PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC gl3wProgramUniformMatrix3x4dv; +extern PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC gl3wProgramUniformMatrix4x3dv; +extern PFNGLVALIDATEPROGRAMPIPELINEPROC gl3wValidateProgramPipeline; +extern PFNGLGETPROGRAMPIPELINEINFOLOGPROC gl3wGetProgramPipelineInfoLog; +extern PFNGLVERTEXATTRIBL1DPROC gl3wVertexAttribL1d; +extern PFNGLVERTEXATTRIBL2DPROC gl3wVertexAttribL2d; +extern PFNGLVERTEXATTRIBL3DPROC gl3wVertexAttribL3d; +extern PFNGLVERTEXATTRIBL4DPROC gl3wVertexAttribL4d; +extern PFNGLVERTEXATTRIBL1DVPROC gl3wVertexAttribL1dv; +extern PFNGLVERTEXATTRIBL2DVPROC gl3wVertexAttribL2dv; +extern PFNGLVERTEXATTRIBL3DVPROC gl3wVertexAttribL3dv; +extern PFNGLVERTEXATTRIBL4DVPROC gl3wVertexAttribL4dv; +extern PFNGLVERTEXATTRIBLPOINTERPROC gl3wVertexAttribLPointer; +extern PFNGLGETVERTEXATTRIBLDVPROC gl3wGetVertexAttribLdv; +extern PFNGLVIEWPORTARRAYVPROC gl3wViewportArrayv; +extern PFNGLVIEWPORTINDEXEDFPROC gl3wViewportIndexedf; +extern PFNGLVIEWPORTINDEXEDFVPROC gl3wViewportIndexedfv; +extern PFNGLSCISSORARRAYVPROC gl3wScissorArrayv; +extern PFNGLSCISSORINDEXEDPROC gl3wScissorIndexed; +extern PFNGLSCISSORINDEXEDVPROC gl3wScissorIndexedv; +extern PFNGLDEPTHRANGEARRAYVPROC gl3wDepthRangeArrayv; +extern PFNGLDEPTHRANGEINDEXEDPROC gl3wDepthRangeIndexed; +extern PFNGLGETFLOATI_VPROC gl3wGetFloati_v; +extern PFNGLGETDOUBLEI_VPROC gl3wGetDoublei_v; +extern PFNGLCREATESYNCFROMCLEVENTARBPROC gl3wCreateSyncFromCLeventARB; +extern PFNGLDEBUGMESSAGECONTROLARBPROC gl3wDebugMessageControlARB; +extern PFNGLDEBUGMESSAGEINSERTARBPROC gl3wDebugMessageInsertARB; +extern PFNGLDEBUGMESSAGECALLBACKARBPROC gl3wDebugMessageCallbackARB; +extern PFNGLGETDEBUGMESSAGELOGARBPROC gl3wGetDebugMessageLogARB; +extern PFNGLGETGRAPHICSRESETSTATUSARBPROC gl3wGetGraphicsResetStatusARB; +extern PFNGLGETNTEXIMAGEARBPROC gl3wGetnTexImageARB; +extern PFNGLREADNPIXELSARBPROC gl3wReadnPixelsARB; +extern PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC gl3wGetnCompressedTexImageARB; +extern PFNGLGETNUNIFORMFVARBPROC gl3wGetnUniformfvARB; +extern PFNGLGETNUNIFORMIVARBPROC gl3wGetnUniformivARB; +extern PFNGLGETNUNIFORMUIVARBPROC gl3wGetnUniformuivARB; +extern PFNGLGETNUNIFORMDVARBPROC gl3wGetnUniformdvARB; +extern PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC gl3wDrawArraysInstancedBaseInstance; +extern PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC gl3wDrawElementsInstancedBaseInstance; +extern PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC gl3wDrawElementsInstancedBaseVertexBaseInstance; +extern PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC gl3wDrawTransformFeedbackInstanced; +extern PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC gl3wDrawTransformFeedbackStreamInstanced; +extern PFNGLGETINTERNALFORMATIVPROC gl3wGetInternalformativ; +extern PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC gl3wGetActiveAtomicCounterBufferiv; +extern PFNGLBINDIMAGETEXTUREPROC gl3wBindImageTexture; +extern PFNGLMEMORYBARRIERPROC gl3wMemoryBarrier; +extern PFNGLTEXSTORAGE1DPROC gl3wTexStorage1D; +extern PFNGLTEXSTORAGE2DPROC gl3wTexStorage2D; +extern PFNGLTEXSTORAGE3DPROC gl3wTexStorage3D; +extern PFNGLTEXTURESTORAGE1DEXTPROC gl3wTextureStorage1DEXT; +extern PFNGLTEXTURESTORAGE2DEXTPROC gl3wTextureStorage2DEXT; +extern PFNGLTEXTURESTORAGE3DEXTPROC gl3wTextureStorage3DEXT; +extern PFNGLDEBUGMESSAGECONTROLPROC gl3wDebugMessageControl; +extern PFNGLDEBUGMESSAGEINSERTPROC gl3wDebugMessageInsert; +extern PFNGLDEBUGMESSAGECALLBACKPROC gl3wDebugMessageCallback; +extern PFNGLGETDEBUGMESSAGELOGPROC gl3wGetDebugMessageLog; +extern PFNGLPUSHDEBUGGROUPPROC gl3wPushDebugGroup; +extern PFNGLPOPDEBUGGROUPPROC gl3wPopDebugGroup; +extern PFNGLOBJECTLABELPROC gl3wObjectLabel; +extern PFNGLGETOBJECTLABELPROC gl3wGetObjectLabel; +extern PFNGLOBJECTPTRLABELPROC gl3wObjectPtrLabel; +extern PFNGLGETOBJECTPTRLABELPROC gl3wGetObjectPtrLabel; +extern PFNGLCLEARBUFFERDATAPROC gl3wClearBufferData; +extern PFNGLCLEARBUFFERSUBDATAPROC gl3wClearBufferSubData; +extern PFNGLCLEARNAMEDBUFFERDATAEXTPROC gl3wClearNamedBufferDataEXT; +extern PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC gl3wClearNamedBufferSubDataEXT; +extern PFNGLDISPATCHCOMPUTEPROC gl3wDispatchCompute; +extern PFNGLDISPATCHCOMPUTEINDIRECTPROC gl3wDispatchComputeIndirect; +extern PFNGLCOPYIMAGESUBDATAPROC gl3wCopyImageSubData; +extern PFNGLTEXTUREVIEWPROC gl3wTextureView; +extern PFNGLBINDVERTEXBUFFERPROC gl3wBindVertexBuffer; +extern PFNGLVERTEXATTRIBFORMATPROC gl3wVertexAttribFormat; +extern PFNGLVERTEXATTRIBIFORMATPROC gl3wVertexAttribIFormat; +extern PFNGLVERTEXATTRIBLFORMATPROC gl3wVertexAttribLFormat; +extern PFNGLVERTEXATTRIBBINDINGPROC gl3wVertexAttribBinding; +extern PFNGLVERTEXBINDINGDIVISORPROC gl3wVertexBindingDivisor; +extern PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC gl3wVertexArrayBindVertexBufferEXT; +extern PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC gl3wVertexArrayVertexAttribFormatEXT; +extern PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC gl3wVertexArrayVertexAttribIFormatEXT; +extern PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC gl3wVertexArrayVertexAttribLFormatEXT; +extern PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC gl3wVertexArrayVertexAttribBindingEXT; +extern PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC gl3wVertexArrayVertexBindingDivisorEXT; +extern PFNGLFRAMEBUFFERPARAMETERIPROC gl3wFramebufferParameteri; +extern PFNGLGETFRAMEBUFFERPARAMETERIVPROC gl3wGetFramebufferParameteriv; +extern PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC gl3wNamedFramebufferParameteriEXT; +extern PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC gl3wGetNamedFramebufferParameterivEXT; +extern PFNGLGETINTERNALFORMATI64VPROC gl3wGetInternalformati64v; +extern PFNGLINVALIDATETEXSUBIMAGEPROC gl3wInvalidateTexSubImage; +extern PFNGLINVALIDATETEXIMAGEPROC gl3wInvalidateTexImage; +extern PFNGLINVALIDATEBUFFERSUBDATAPROC gl3wInvalidateBufferSubData; +extern PFNGLINVALIDATEBUFFERDATAPROC gl3wInvalidateBufferData; +extern PFNGLINVALIDATEFRAMEBUFFERPROC gl3wInvalidateFramebuffer; +extern PFNGLINVALIDATESUBFRAMEBUFFERPROC gl3wInvalidateSubFramebuffer; +extern PFNGLMULTIDRAWARRAYSINDIRECTPROC gl3wMultiDrawArraysIndirect; +extern PFNGLMULTIDRAWELEMENTSINDIRECTPROC gl3wMultiDrawElementsIndirect; +extern PFNGLGETPROGRAMINTERFACEIVPROC gl3wGetProgramInterfaceiv; +extern PFNGLGETPROGRAMRESOURCEINDEXPROC gl3wGetProgramResourceIndex; +extern PFNGLGETPROGRAMRESOURCENAMEPROC gl3wGetProgramResourceName; +extern PFNGLGETPROGRAMRESOURCEIVPROC gl3wGetProgramResourceiv; +extern PFNGLGETPROGRAMRESOURCELOCATIONPROC gl3wGetProgramResourceLocation; +extern PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC gl3wGetProgramResourceLocationIndex; +extern PFNGLSHADERSTORAGEBLOCKBINDINGPROC gl3wShaderStorageBlockBinding; +extern PFNGLTEXBUFFERRANGEPROC gl3wTexBufferRange; +extern PFNGLTEXTUREBUFFERRANGEEXTPROC gl3wTextureBufferRangeEXT; +extern PFNGLTEXSTORAGE2DMULTISAMPLEPROC gl3wTexStorage2DMultisample; +extern PFNGLTEXSTORAGE3DMULTISAMPLEPROC gl3wTexStorage3DMultisample; +extern PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC gl3wTextureStorage2DMultisampleEXT; +extern PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC gl3wTextureStorage3DMultisampleEXT; + +#define glCullFace gl3wCullFace +#define glFrontFace gl3wFrontFace +#define glHint gl3wHint +#define glLineWidth gl3wLineWidth +#define glPointSize gl3wPointSize +#define glPolygonMode gl3wPolygonMode +#define glScissor gl3wScissor +#define glTexParameterf gl3wTexParameterf +#define glTexParameterfv gl3wTexParameterfv +#define glTexParameteri gl3wTexParameteri +#define glTexParameteriv gl3wTexParameteriv +#define glTexImage1D gl3wTexImage1D +#define glTexImage2D gl3wTexImage2D +#define glDrawBuffer gl3wDrawBuffer +#define glClear gl3wClear +#define glClearColor gl3wClearColor +#define glClearStencil gl3wClearStencil +#define glClearDepth gl3wClearDepth +#define glStencilMask gl3wStencilMask +#define glColorMask gl3wColorMask +#define glDepthMask gl3wDepthMask +#define glDisable gl3wDisable +#define glEnable gl3wEnable +#define glFinish gl3wFinish +#define glFlush gl3wFlush +#define glBlendFunc gl3wBlendFunc +#define glLogicOp gl3wLogicOp +#define glStencilFunc gl3wStencilFunc +#define glStencilOp gl3wStencilOp +#define glDepthFunc gl3wDepthFunc +#define glPixelStoref gl3wPixelStoref +#define glPixelStorei gl3wPixelStorei +#define glReadBuffer gl3wReadBuffer +#define glReadPixels gl3wReadPixels +#define glGetBooleanv gl3wGetBooleanv +#define glGetDoublev gl3wGetDoublev +#define glGetError gl3wGetError +#define glGetFloatv gl3wGetFloatv +#define glGetIntegerv gl3wGetIntegerv +#define glGetString gl3wGetString +#define glGetTexImage gl3wGetTexImage +#define glGetTexParameterfv gl3wGetTexParameterfv +#define glGetTexParameteriv gl3wGetTexParameteriv +#define glGetTexLevelParameterfv gl3wGetTexLevelParameterfv +#define glGetTexLevelParameteriv gl3wGetTexLevelParameteriv +#define glIsEnabled gl3wIsEnabled +#define glDepthRange gl3wDepthRange +#define glViewport gl3wViewport +#define glDrawArrays gl3wDrawArrays +#define glDrawElements gl3wDrawElements +#define glGetPointerv gl3wGetPointerv +#define glPolygonOffset gl3wPolygonOffset +#define glCopyTexImage1D gl3wCopyTexImage1D +#define glCopyTexImage2D gl3wCopyTexImage2D +#define glCopyTexSubImage1D gl3wCopyTexSubImage1D +#define glCopyTexSubImage2D gl3wCopyTexSubImage2D +#define glTexSubImage1D gl3wTexSubImage1D +#define glTexSubImage2D gl3wTexSubImage2D +#define glBindTexture gl3wBindTexture +#define glDeleteTextures gl3wDeleteTextures +#define glGenTextures gl3wGenTextures +#define glIsTexture gl3wIsTexture +#define glBlendColor gl3wBlendColor +#define glBlendEquation gl3wBlendEquation +#define glDrawRangeElements gl3wDrawRangeElements +#define glTexImage3D gl3wTexImage3D +#define glTexSubImage3D gl3wTexSubImage3D +#define glCopyTexSubImage3D gl3wCopyTexSubImage3D +#define glActiveTexture gl3wActiveTexture +#define glSampleCoverage gl3wSampleCoverage +#define glCompressedTexImage3D gl3wCompressedTexImage3D +#define glCompressedTexImage2D gl3wCompressedTexImage2D +#define glCompressedTexImage1D gl3wCompressedTexImage1D +#define glCompressedTexSubImage3D gl3wCompressedTexSubImage3D +#define glCompressedTexSubImage2D gl3wCompressedTexSubImage2D +#define glCompressedTexSubImage1D gl3wCompressedTexSubImage1D +#define glGetCompressedTexImage gl3wGetCompressedTexImage +#define glBlendFuncSeparate gl3wBlendFuncSeparate +#define glMultiDrawArrays gl3wMultiDrawArrays +#define glMultiDrawElements gl3wMultiDrawElements +#define glPointParameterf gl3wPointParameterf +#define glPointParameterfv gl3wPointParameterfv +#define glPointParameteri gl3wPointParameteri +#define glPointParameteriv gl3wPointParameteriv +#define glGenQueries gl3wGenQueries +#define glDeleteQueries gl3wDeleteQueries +#define glIsQuery gl3wIsQuery +#define glBeginQuery gl3wBeginQuery +#define glEndQuery gl3wEndQuery +#define glGetQueryiv gl3wGetQueryiv +#define glGetQueryObjectiv gl3wGetQueryObjectiv +#define glGetQueryObjectuiv gl3wGetQueryObjectuiv +#define glBindBuffer gl3wBindBuffer +#define glDeleteBuffers gl3wDeleteBuffers +#define glGenBuffers gl3wGenBuffers +#define glIsBuffer gl3wIsBuffer +#define glBufferData gl3wBufferData +#define glBufferSubData gl3wBufferSubData +#define glGetBufferSubData gl3wGetBufferSubData +#define glMapBuffer gl3wMapBuffer +#define glUnmapBuffer gl3wUnmapBuffer +#define glGetBufferParameteriv gl3wGetBufferParameteriv +#define glGetBufferPointerv gl3wGetBufferPointerv +#define glBlendEquationSeparate gl3wBlendEquationSeparate +#define glDrawBuffers gl3wDrawBuffers +#define glStencilOpSeparate gl3wStencilOpSeparate +#define glStencilFuncSeparate gl3wStencilFuncSeparate +#define glStencilMaskSeparate gl3wStencilMaskSeparate +#define glAttachShader gl3wAttachShader +#define glBindAttribLocation gl3wBindAttribLocation +#define glCompileShader gl3wCompileShader +#define glCreateProgram gl3wCreateProgram +#define glCreateShader gl3wCreateShader +#define glDeleteProgram gl3wDeleteProgram +#define glDeleteShader gl3wDeleteShader +#define glDetachShader gl3wDetachShader +#define glDisableVertexAttribArray gl3wDisableVertexAttribArray +#define glEnableVertexAttribArray gl3wEnableVertexAttribArray +#define glGetActiveAttrib gl3wGetActiveAttrib +#define glGetActiveUniform gl3wGetActiveUniform +#define glGetAttachedShaders gl3wGetAttachedShaders +#define glGetAttribLocation gl3wGetAttribLocation +#define glGetProgramiv gl3wGetProgramiv +#define glGetProgramInfoLog gl3wGetProgramInfoLog +#define glGetShaderiv gl3wGetShaderiv +#define glGetShaderInfoLog gl3wGetShaderInfoLog +#define glGetShaderSource gl3wGetShaderSource +#define glGetUniformLocation gl3wGetUniformLocation +#define glGetUniformfv gl3wGetUniformfv +#define glGetUniformiv gl3wGetUniformiv +#define glGetVertexAttribdv gl3wGetVertexAttribdv +#define glGetVertexAttribfv gl3wGetVertexAttribfv +#define glGetVertexAttribiv gl3wGetVertexAttribiv +#define glGetVertexAttribPointerv gl3wGetVertexAttribPointerv +#define glIsProgram gl3wIsProgram +#define glIsShader gl3wIsShader +#define glLinkProgram gl3wLinkProgram +#define glShaderSource gl3wShaderSource +#define glUseProgram gl3wUseProgram +#define glUniform1f gl3wUniform1f +#define glUniform2f gl3wUniform2f +#define glUniform3f gl3wUniform3f +#define glUniform4f gl3wUniform4f +#define glUniform1i gl3wUniform1i +#define glUniform2i gl3wUniform2i +#define glUniform3i gl3wUniform3i +#define glUniform4i gl3wUniform4i +#define glUniform1fv gl3wUniform1fv +#define glUniform2fv gl3wUniform2fv +#define glUniform3fv gl3wUniform3fv +#define glUniform4fv gl3wUniform4fv +#define glUniform1iv gl3wUniform1iv +#define glUniform2iv gl3wUniform2iv +#define glUniform3iv gl3wUniform3iv +#define glUniform4iv gl3wUniform4iv +#define glUniformMatrix2fv gl3wUniformMatrix2fv +#define glUniformMatrix3fv gl3wUniformMatrix3fv +#define glUniformMatrix4fv gl3wUniformMatrix4fv +#define glValidateProgram gl3wValidateProgram +#define glVertexAttrib1d gl3wVertexAttrib1d +#define glVertexAttrib1dv gl3wVertexAttrib1dv +#define glVertexAttrib1f gl3wVertexAttrib1f +#define glVertexAttrib1fv gl3wVertexAttrib1fv +#define glVertexAttrib1s gl3wVertexAttrib1s +#define glVertexAttrib1sv gl3wVertexAttrib1sv +#define glVertexAttrib2d gl3wVertexAttrib2d +#define glVertexAttrib2dv gl3wVertexAttrib2dv +#define glVertexAttrib2f gl3wVertexAttrib2f +#define glVertexAttrib2fv gl3wVertexAttrib2fv +#define glVertexAttrib2s gl3wVertexAttrib2s +#define glVertexAttrib2sv gl3wVertexAttrib2sv +#define glVertexAttrib3d gl3wVertexAttrib3d +#define glVertexAttrib3dv gl3wVertexAttrib3dv +#define glVertexAttrib3f gl3wVertexAttrib3f +#define glVertexAttrib3fv gl3wVertexAttrib3fv +#define glVertexAttrib3s gl3wVertexAttrib3s +#define glVertexAttrib3sv gl3wVertexAttrib3sv +#define glVertexAttrib4Nbv gl3wVertexAttrib4Nbv +#define glVertexAttrib4Niv gl3wVertexAttrib4Niv +#define glVertexAttrib4Nsv gl3wVertexAttrib4Nsv +#define glVertexAttrib4Nub gl3wVertexAttrib4Nub +#define glVertexAttrib4Nubv gl3wVertexAttrib4Nubv +#define glVertexAttrib4Nuiv gl3wVertexAttrib4Nuiv +#define glVertexAttrib4Nusv gl3wVertexAttrib4Nusv +#define glVertexAttrib4bv gl3wVertexAttrib4bv +#define glVertexAttrib4d gl3wVertexAttrib4d +#define glVertexAttrib4dv gl3wVertexAttrib4dv +#define glVertexAttrib4f gl3wVertexAttrib4f +#define glVertexAttrib4fv gl3wVertexAttrib4fv +#define glVertexAttrib4iv gl3wVertexAttrib4iv +#define glVertexAttrib4s gl3wVertexAttrib4s +#define glVertexAttrib4sv gl3wVertexAttrib4sv +#define glVertexAttrib4ubv gl3wVertexAttrib4ubv +#define glVertexAttrib4uiv gl3wVertexAttrib4uiv +#define glVertexAttrib4usv gl3wVertexAttrib4usv +#define glVertexAttribPointer gl3wVertexAttribPointer +#define glUniformMatrix2x3fv gl3wUniformMatrix2x3fv +#define glUniformMatrix3x2fv gl3wUniformMatrix3x2fv +#define glUniformMatrix2x4fv gl3wUniformMatrix2x4fv +#define glUniformMatrix4x2fv gl3wUniformMatrix4x2fv +#define glUniformMatrix3x4fv gl3wUniformMatrix3x4fv +#define glUniformMatrix4x3fv gl3wUniformMatrix4x3fv +#define glColorMaski gl3wColorMaski +#define glGetBooleani_v gl3wGetBooleani_v +#define glGetIntegeri_v gl3wGetIntegeri_v +#define glEnablei gl3wEnablei +#define glDisablei gl3wDisablei +#define glIsEnabledi gl3wIsEnabledi +#define glBeginTransformFeedback gl3wBeginTransformFeedback +#define glEndTransformFeedback gl3wEndTransformFeedback +#define glBindBufferRange gl3wBindBufferRange +#define glBindBufferBase gl3wBindBufferBase +#define glTransformFeedbackVaryings gl3wTransformFeedbackVaryings +#define glGetTransformFeedbackVarying gl3wGetTransformFeedbackVarying +#define glClampColor gl3wClampColor +#define glBeginConditionalRender gl3wBeginConditionalRender +#define glEndConditionalRender gl3wEndConditionalRender +#define glVertexAttribIPointer gl3wVertexAttribIPointer +#define glGetVertexAttribIiv gl3wGetVertexAttribIiv +#define glGetVertexAttribIuiv gl3wGetVertexAttribIuiv +#define glVertexAttribI1i gl3wVertexAttribI1i +#define glVertexAttribI2i gl3wVertexAttribI2i +#define glVertexAttribI3i gl3wVertexAttribI3i +#define glVertexAttribI4i gl3wVertexAttribI4i +#define glVertexAttribI1ui gl3wVertexAttribI1ui +#define glVertexAttribI2ui gl3wVertexAttribI2ui +#define glVertexAttribI3ui gl3wVertexAttribI3ui +#define glVertexAttribI4ui gl3wVertexAttribI4ui +#define glVertexAttribI1iv gl3wVertexAttribI1iv +#define glVertexAttribI2iv gl3wVertexAttribI2iv +#define glVertexAttribI3iv gl3wVertexAttribI3iv +#define glVertexAttribI4iv gl3wVertexAttribI4iv +#define glVertexAttribI1uiv gl3wVertexAttribI1uiv +#define glVertexAttribI2uiv gl3wVertexAttribI2uiv +#define glVertexAttribI3uiv gl3wVertexAttribI3uiv +#define glVertexAttribI4uiv gl3wVertexAttribI4uiv +#define glVertexAttribI4bv gl3wVertexAttribI4bv +#define glVertexAttribI4sv gl3wVertexAttribI4sv +#define glVertexAttribI4ubv gl3wVertexAttribI4ubv +#define glVertexAttribI4usv gl3wVertexAttribI4usv +#define glGetUniformuiv gl3wGetUniformuiv +#define glBindFragDataLocation gl3wBindFragDataLocation +#define glGetFragDataLocation gl3wGetFragDataLocation +#define glUniform1ui gl3wUniform1ui +#define glUniform2ui gl3wUniform2ui +#define glUniform3ui gl3wUniform3ui +#define glUniform4ui gl3wUniform4ui +#define glUniform1uiv gl3wUniform1uiv +#define glUniform2uiv gl3wUniform2uiv +#define glUniform3uiv gl3wUniform3uiv +#define glUniform4uiv gl3wUniform4uiv +#define glTexParameterIiv gl3wTexParameterIiv +#define glTexParameterIuiv gl3wTexParameterIuiv +#define glGetTexParameterIiv gl3wGetTexParameterIiv +#define glGetTexParameterIuiv gl3wGetTexParameterIuiv +#define glClearBufferiv gl3wClearBufferiv +#define glClearBufferuiv gl3wClearBufferuiv +#define glClearBufferfv gl3wClearBufferfv +#define glClearBufferfi gl3wClearBufferfi +#define glGetStringi gl3wGetStringi +#define glDrawArraysInstanced gl3wDrawArraysInstanced +#define glDrawElementsInstanced gl3wDrawElementsInstanced +#define glTexBuffer gl3wTexBuffer +#define glPrimitiveRestartIndex gl3wPrimitiveRestartIndex +#define glGetInteger64i_v gl3wGetInteger64i_v +#define glGetBufferParameteri64v gl3wGetBufferParameteri64v +#define glFramebufferTexture gl3wFramebufferTexture +#define glVertexAttribDivisor gl3wVertexAttribDivisor +#define glMinSampleShading gl3wMinSampleShading +#define glBlendEquationi gl3wBlendEquationi +#define glBlendEquationSeparatei gl3wBlendEquationSeparatei +#define glBlendFunci gl3wBlendFunci +#define glBlendFuncSeparatei gl3wBlendFuncSeparatei +#define glIsRenderbuffer gl3wIsRenderbuffer +#define glBindRenderbuffer gl3wBindRenderbuffer +#define glDeleteRenderbuffers gl3wDeleteRenderbuffers +#define glGenRenderbuffers gl3wGenRenderbuffers +#define glRenderbufferStorage gl3wRenderbufferStorage +#define glGetRenderbufferParameteriv gl3wGetRenderbufferParameteriv +#define glIsFramebuffer gl3wIsFramebuffer +#define glBindFramebuffer gl3wBindFramebuffer +#define glDeleteFramebuffers gl3wDeleteFramebuffers +#define glGenFramebuffers gl3wGenFramebuffers +#define glCheckFramebufferStatus gl3wCheckFramebufferStatus +#define glFramebufferTexture1D gl3wFramebufferTexture1D +#define glFramebufferTexture2D gl3wFramebufferTexture2D +#define glFramebufferTexture3D gl3wFramebufferTexture3D +#define glFramebufferRenderbuffer gl3wFramebufferRenderbuffer +#define glGetFramebufferAttachmentParameteriv gl3wGetFramebufferAttachmentParameteriv +#define glGenerateMipmap gl3wGenerateMipmap +#define glBlitFramebuffer gl3wBlitFramebuffer +#define glRenderbufferStorageMultisample gl3wRenderbufferStorageMultisample +#define glFramebufferTextureLayer gl3wFramebufferTextureLayer +#define glMapBufferRange gl3wMapBufferRange +#define glFlushMappedBufferRange gl3wFlushMappedBufferRange +#define glBindVertexArray gl3wBindVertexArray +#define glDeleteVertexArrays gl3wDeleteVertexArrays +#define glGenVertexArrays gl3wGenVertexArrays +#define glIsVertexArray gl3wIsVertexArray +#define glGetUniformIndices gl3wGetUniformIndices +#define glGetActiveUniformsiv gl3wGetActiveUniformsiv +#define glGetActiveUniformName gl3wGetActiveUniformName +#define glGetUniformBlockIndex gl3wGetUniformBlockIndex +#define glGetActiveUniformBlockiv gl3wGetActiveUniformBlockiv +#define glGetActiveUniformBlockName gl3wGetActiveUniformBlockName +#define glUniformBlockBinding gl3wUniformBlockBinding +#define glCopyBufferSubData gl3wCopyBufferSubData +#define glDrawElementsBaseVertex gl3wDrawElementsBaseVertex +#define glDrawRangeElementsBaseVertex gl3wDrawRangeElementsBaseVertex +#define glDrawElementsInstancedBaseVertex gl3wDrawElementsInstancedBaseVertex +#define glMultiDrawElementsBaseVertex gl3wMultiDrawElementsBaseVertex +#define glProvokingVertex gl3wProvokingVertex +#define glFenceSync gl3wFenceSync +#define glIsSync gl3wIsSync +#define glDeleteSync gl3wDeleteSync +#define glClientWaitSync gl3wClientWaitSync +#define glWaitSync gl3wWaitSync +#define glGetInteger64v gl3wGetInteger64v +#define glGetSynciv gl3wGetSynciv +#define glTexImage2DMultisample gl3wTexImage2DMultisample +#define glTexImage3DMultisample gl3wTexImage3DMultisample +#define glGetMultisamplefv gl3wGetMultisamplefv +#define glSampleMaski gl3wSampleMaski +#define glBlendEquationiARB gl3wBlendEquationiARB +#define glBlendEquationSeparateiARB gl3wBlendEquationSeparateiARB +#define glBlendFunciARB gl3wBlendFunciARB +#define glBlendFuncSeparateiARB gl3wBlendFuncSeparateiARB +#define glMinSampleShadingARB gl3wMinSampleShadingARB +#define glNamedStringARB gl3wNamedStringARB +#define glDeleteNamedStringARB gl3wDeleteNamedStringARB +#define glCompileShaderIncludeARB gl3wCompileShaderIncludeARB +#define glIsNamedStringARB gl3wIsNamedStringARB +#define glGetNamedStringARB gl3wGetNamedStringARB +#define glGetNamedStringivARB gl3wGetNamedStringivARB +#define glBindFragDataLocationIndexed gl3wBindFragDataLocationIndexed +#define glGetFragDataIndex gl3wGetFragDataIndex +#define glGenSamplers gl3wGenSamplers +#define glDeleteSamplers gl3wDeleteSamplers +#define glIsSampler gl3wIsSampler +#define glBindSampler gl3wBindSampler +#define glSamplerParameteri gl3wSamplerParameteri +#define glSamplerParameteriv gl3wSamplerParameteriv +#define glSamplerParameterf gl3wSamplerParameterf +#define glSamplerParameterfv gl3wSamplerParameterfv +#define glSamplerParameterIiv gl3wSamplerParameterIiv +#define glSamplerParameterIuiv gl3wSamplerParameterIuiv +#define glGetSamplerParameteriv gl3wGetSamplerParameteriv +#define glGetSamplerParameterIiv gl3wGetSamplerParameterIiv +#define glGetSamplerParameterfv gl3wGetSamplerParameterfv +#define glGetSamplerParameterIuiv gl3wGetSamplerParameterIuiv +#define glQueryCounter gl3wQueryCounter +#define glGetQueryObjecti64v gl3wGetQueryObjecti64v +#define glGetQueryObjectui64v gl3wGetQueryObjectui64v +#define glVertexP2ui gl3wVertexP2ui +#define glVertexP2uiv gl3wVertexP2uiv +#define glVertexP3ui gl3wVertexP3ui +#define glVertexP3uiv gl3wVertexP3uiv +#define glVertexP4ui gl3wVertexP4ui +#define glVertexP4uiv gl3wVertexP4uiv +#define glTexCoordP1ui gl3wTexCoordP1ui +#define glTexCoordP1uiv gl3wTexCoordP1uiv +#define glTexCoordP2ui gl3wTexCoordP2ui +#define glTexCoordP2uiv gl3wTexCoordP2uiv +#define glTexCoordP3ui gl3wTexCoordP3ui +#define glTexCoordP3uiv gl3wTexCoordP3uiv +#define glTexCoordP4ui gl3wTexCoordP4ui +#define glTexCoordP4uiv gl3wTexCoordP4uiv +#define glMultiTexCoordP1ui gl3wMultiTexCoordP1ui +#define glMultiTexCoordP1uiv gl3wMultiTexCoordP1uiv +#define glMultiTexCoordP2ui gl3wMultiTexCoordP2ui +#define glMultiTexCoordP2uiv gl3wMultiTexCoordP2uiv +#define glMultiTexCoordP3ui gl3wMultiTexCoordP3ui +#define glMultiTexCoordP3uiv gl3wMultiTexCoordP3uiv +#define glMultiTexCoordP4ui gl3wMultiTexCoordP4ui +#define glMultiTexCoordP4uiv gl3wMultiTexCoordP4uiv +#define glNormalP3ui gl3wNormalP3ui +#define glNormalP3uiv gl3wNormalP3uiv +#define glColorP3ui gl3wColorP3ui +#define glColorP3uiv gl3wColorP3uiv +#define glColorP4ui gl3wColorP4ui +#define glColorP4uiv gl3wColorP4uiv +#define glSecondaryColorP3ui gl3wSecondaryColorP3ui +#define glSecondaryColorP3uiv gl3wSecondaryColorP3uiv +#define glVertexAttribP1ui gl3wVertexAttribP1ui +#define glVertexAttribP1uiv gl3wVertexAttribP1uiv +#define glVertexAttribP2ui gl3wVertexAttribP2ui +#define glVertexAttribP2uiv gl3wVertexAttribP2uiv +#define glVertexAttribP3ui gl3wVertexAttribP3ui +#define glVertexAttribP3uiv gl3wVertexAttribP3uiv +#define glVertexAttribP4ui gl3wVertexAttribP4ui +#define glVertexAttribP4uiv gl3wVertexAttribP4uiv +#define glDrawArraysIndirect gl3wDrawArraysIndirect +#define glDrawElementsIndirect gl3wDrawElementsIndirect +#define glUniform1d gl3wUniform1d +#define glUniform2d gl3wUniform2d +#define glUniform3d gl3wUniform3d +#define glUniform4d gl3wUniform4d +#define glUniform1dv gl3wUniform1dv +#define glUniform2dv gl3wUniform2dv +#define glUniform3dv gl3wUniform3dv +#define glUniform4dv gl3wUniform4dv +#define glUniformMatrix2dv gl3wUniformMatrix2dv +#define glUniformMatrix3dv gl3wUniformMatrix3dv +#define glUniformMatrix4dv gl3wUniformMatrix4dv +#define glUniformMatrix2x3dv gl3wUniformMatrix2x3dv +#define glUniformMatrix2x4dv gl3wUniformMatrix2x4dv +#define glUniformMatrix3x2dv gl3wUniformMatrix3x2dv +#define glUniformMatrix3x4dv gl3wUniformMatrix3x4dv +#define glUniformMatrix4x2dv gl3wUniformMatrix4x2dv +#define glUniformMatrix4x3dv gl3wUniformMatrix4x3dv +#define glGetUniformdv gl3wGetUniformdv +#define glGetSubroutineUniformLocation gl3wGetSubroutineUniformLocation +#define glGetSubroutineIndex gl3wGetSubroutineIndex +#define glGetActiveSubroutineUniformiv gl3wGetActiveSubroutineUniformiv +#define glGetActiveSubroutineUniformName gl3wGetActiveSubroutineUniformName +#define glGetActiveSubroutineName gl3wGetActiveSubroutineName +#define glUniformSubroutinesuiv gl3wUniformSubroutinesuiv +#define glGetUniformSubroutineuiv gl3wGetUniformSubroutineuiv +#define glGetProgramStageiv gl3wGetProgramStageiv +#define glPatchParameteri gl3wPatchParameteri +#define glPatchParameterfv gl3wPatchParameterfv +#define glBindTransformFeedback gl3wBindTransformFeedback +#define glDeleteTransformFeedbacks gl3wDeleteTransformFeedbacks +#define glGenTransformFeedbacks gl3wGenTransformFeedbacks +#define glIsTransformFeedback gl3wIsTransformFeedback +#define glPauseTransformFeedback gl3wPauseTransformFeedback +#define glResumeTransformFeedback gl3wResumeTransformFeedback +#define glDrawTransformFeedback gl3wDrawTransformFeedback +#define glDrawTransformFeedbackStream gl3wDrawTransformFeedbackStream +#define glBeginQueryIndexed gl3wBeginQueryIndexed +#define glEndQueryIndexed gl3wEndQueryIndexed +#define glGetQueryIndexediv gl3wGetQueryIndexediv +#define glReleaseShaderCompiler gl3wReleaseShaderCompiler +#define glShaderBinary gl3wShaderBinary +#define glGetShaderPrecisionFormat gl3wGetShaderPrecisionFormat +#define glDepthRangef gl3wDepthRangef +#define glClearDepthf gl3wClearDepthf +#define glGetProgramBinary gl3wGetProgramBinary +#define glProgramBinary gl3wProgramBinary +#define glProgramParameteri gl3wProgramParameteri +#define glUseProgramStages gl3wUseProgramStages +#define glActiveShaderProgram gl3wActiveShaderProgram +#define glCreateShaderProgramv gl3wCreateShaderProgramv +#define glBindProgramPipeline gl3wBindProgramPipeline +#define glDeleteProgramPipelines gl3wDeleteProgramPipelines +#define glGenProgramPipelines gl3wGenProgramPipelines +#define glIsProgramPipeline gl3wIsProgramPipeline +#define glGetProgramPipelineiv gl3wGetProgramPipelineiv +#define glProgramUniform1i gl3wProgramUniform1i +#define glProgramUniform1iv gl3wProgramUniform1iv +#define glProgramUniform1f gl3wProgramUniform1f +#define glProgramUniform1fv gl3wProgramUniform1fv +#define glProgramUniform1d gl3wProgramUniform1d +#define glProgramUniform1dv gl3wProgramUniform1dv +#define glProgramUniform1ui gl3wProgramUniform1ui +#define glProgramUniform1uiv gl3wProgramUniform1uiv +#define glProgramUniform2i gl3wProgramUniform2i +#define glProgramUniform2iv gl3wProgramUniform2iv +#define glProgramUniform2f gl3wProgramUniform2f +#define glProgramUniform2fv gl3wProgramUniform2fv +#define glProgramUniform2d gl3wProgramUniform2d +#define glProgramUniform2dv gl3wProgramUniform2dv +#define glProgramUniform2ui gl3wProgramUniform2ui +#define glProgramUniform2uiv gl3wProgramUniform2uiv +#define glProgramUniform3i gl3wProgramUniform3i +#define glProgramUniform3iv gl3wProgramUniform3iv +#define glProgramUniform3f gl3wProgramUniform3f +#define glProgramUniform3fv gl3wProgramUniform3fv +#define glProgramUniform3d gl3wProgramUniform3d +#define glProgramUniform3dv gl3wProgramUniform3dv +#define glProgramUniform3ui gl3wProgramUniform3ui +#define glProgramUniform3uiv gl3wProgramUniform3uiv +#define glProgramUniform4i gl3wProgramUniform4i +#define glProgramUniform4iv gl3wProgramUniform4iv +#define glProgramUniform4f gl3wProgramUniform4f +#define glProgramUniform4fv gl3wProgramUniform4fv +#define glProgramUniform4d gl3wProgramUniform4d +#define glProgramUniform4dv gl3wProgramUniform4dv +#define glProgramUniform4ui gl3wProgramUniform4ui +#define glProgramUniform4uiv gl3wProgramUniform4uiv +#define glProgramUniformMatrix2fv gl3wProgramUniformMatrix2fv +#define glProgramUniformMatrix3fv gl3wProgramUniformMatrix3fv +#define glProgramUniformMatrix4fv gl3wProgramUniformMatrix4fv +#define glProgramUniformMatrix2dv gl3wProgramUniformMatrix2dv +#define glProgramUniformMatrix3dv gl3wProgramUniformMatrix3dv +#define glProgramUniformMatrix4dv gl3wProgramUniformMatrix4dv +#define glProgramUniformMatrix2x3fv gl3wProgramUniformMatrix2x3fv +#define glProgramUniformMatrix3x2fv gl3wProgramUniformMatrix3x2fv +#define glProgramUniformMatrix2x4fv gl3wProgramUniformMatrix2x4fv +#define glProgramUniformMatrix4x2fv gl3wProgramUniformMatrix4x2fv +#define glProgramUniformMatrix3x4fv gl3wProgramUniformMatrix3x4fv +#define glProgramUniformMatrix4x3fv gl3wProgramUniformMatrix4x3fv +#define glProgramUniformMatrix2x3dv gl3wProgramUniformMatrix2x3dv +#define glProgramUniformMatrix3x2dv gl3wProgramUniformMatrix3x2dv +#define glProgramUniformMatrix2x4dv gl3wProgramUniformMatrix2x4dv +#define glProgramUniformMatrix4x2dv gl3wProgramUniformMatrix4x2dv +#define glProgramUniformMatrix3x4dv gl3wProgramUniformMatrix3x4dv +#define glProgramUniformMatrix4x3dv gl3wProgramUniformMatrix4x3dv +#define glValidateProgramPipeline gl3wValidateProgramPipeline +#define glGetProgramPipelineInfoLog gl3wGetProgramPipelineInfoLog +#define glVertexAttribL1d gl3wVertexAttribL1d +#define glVertexAttribL2d gl3wVertexAttribL2d +#define glVertexAttribL3d gl3wVertexAttribL3d +#define glVertexAttribL4d gl3wVertexAttribL4d +#define glVertexAttribL1dv gl3wVertexAttribL1dv +#define glVertexAttribL2dv gl3wVertexAttribL2dv +#define glVertexAttribL3dv gl3wVertexAttribL3dv +#define glVertexAttribL4dv gl3wVertexAttribL4dv +#define glVertexAttribLPointer gl3wVertexAttribLPointer +#define glGetVertexAttribLdv gl3wGetVertexAttribLdv +#define glViewportArrayv gl3wViewportArrayv +#define glViewportIndexedf gl3wViewportIndexedf +#define glViewportIndexedfv gl3wViewportIndexedfv +#define glScissorArrayv gl3wScissorArrayv +#define glScissorIndexed gl3wScissorIndexed +#define glScissorIndexedv gl3wScissorIndexedv +#define glDepthRangeArrayv gl3wDepthRangeArrayv +#define glDepthRangeIndexed gl3wDepthRangeIndexed +#define glGetFloati_v gl3wGetFloati_v +#define glGetDoublei_v gl3wGetDoublei_v +#define glCreateSyncFromCLeventARB gl3wCreateSyncFromCLeventARB +#define glDebugMessageControlARB gl3wDebugMessageControlARB +#define glDebugMessageInsertARB gl3wDebugMessageInsertARB +#define glDebugMessageCallbackARB gl3wDebugMessageCallbackARB +#define glGetDebugMessageLogARB gl3wGetDebugMessageLogARB +#define glGetGraphicsResetStatusARB gl3wGetGraphicsResetStatusARB +#define glGetnTexImageARB gl3wGetnTexImageARB +#define glReadnPixelsARB gl3wReadnPixelsARB +#define glGetnCompressedTexImageARB gl3wGetnCompressedTexImageARB +#define glGetnUniformfvARB gl3wGetnUniformfvARB +#define glGetnUniformivARB gl3wGetnUniformivARB +#define glGetnUniformuivARB gl3wGetnUniformuivARB +#define glGetnUniformdvARB gl3wGetnUniformdvARB +#define glDrawArraysInstancedBaseInstance gl3wDrawArraysInstancedBaseInstance +#define glDrawElementsInstancedBaseInstance gl3wDrawElementsInstancedBaseInstance +#define glDrawElementsInstancedBaseVertexBaseInstance gl3wDrawElementsInstancedBaseVertexBaseInstance +#define glDrawTransformFeedbackInstanced gl3wDrawTransformFeedbackInstanced +#define glDrawTransformFeedbackStreamInstanced gl3wDrawTransformFeedbackStreamInstanced +#define glGetInternalformativ gl3wGetInternalformativ +#define glGetActiveAtomicCounterBufferiv gl3wGetActiveAtomicCounterBufferiv +#define glBindImageTexture gl3wBindImageTexture +#define glMemoryBarrier gl3wMemoryBarrier +#define glTexStorage1D gl3wTexStorage1D +#define glTexStorage2D gl3wTexStorage2D +#define glTexStorage3D gl3wTexStorage3D +#define glTextureStorage1DEXT gl3wTextureStorage1DEXT +#define glTextureStorage2DEXT gl3wTextureStorage2DEXT +#define glTextureStorage3DEXT gl3wTextureStorage3DEXT +#define glDebugMessageControl gl3wDebugMessageControl +#define glDebugMessageInsert gl3wDebugMessageInsert +#define glDebugMessageCallback gl3wDebugMessageCallback +#define glGetDebugMessageLog gl3wGetDebugMessageLog +#define glPushDebugGroup gl3wPushDebugGroup +#define glPopDebugGroup gl3wPopDebugGroup +#define glObjectLabel gl3wObjectLabel +#define glGetObjectLabel gl3wGetObjectLabel +#define glObjectPtrLabel gl3wObjectPtrLabel +#define glGetObjectPtrLabel gl3wGetObjectPtrLabel +#define glClearBufferData gl3wClearBufferData +#define glClearBufferSubData gl3wClearBufferSubData +#define glClearNamedBufferDataEXT gl3wClearNamedBufferDataEXT +#define glClearNamedBufferSubDataEXT gl3wClearNamedBufferSubDataEXT +#define glDispatchCompute gl3wDispatchCompute +#define glDispatchComputeIndirect gl3wDispatchComputeIndirect +#define glCopyImageSubData gl3wCopyImageSubData +#define glTextureView gl3wTextureView +#define glBindVertexBuffer gl3wBindVertexBuffer +#define glVertexAttribFormat gl3wVertexAttribFormat +#define glVertexAttribIFormat gl3wVertexAttribIFormat +#define glVertexAttribLFormat gl3wVertexAttribLFormat +#define glVertexAttribBinding gl3wVertexAttribBinding +#define glVertexBindingDivisor gl3wVertexBindingDivisor +#define glVertexArrayBindVertexBufferEXT gl3wVertexArrayBindVertexBufferEXT +#define glVertexArrayVertexAttribFormatEXT gl3wVertexArrayVertexAttribFormatEXT +#define glVertexArrayVertexAttribIFormatEXT gl3wVertexArrayVertexAttribIFormatEXT +#define glVertexArrayVertexAttribLFormatEXT gl3wVertexArrayVertexAttribLFormatEXT +#define glVertexArrayVertexAttribBindingEXT gl3wVertexArrayVertexAttribBindingEXT +#define glVertexArrayVertexBindingDivisorEXT gl3wVertexArrayVertexBindingDivisorEXT +#define glFramebufferParameteri gl3wFramebufferParameteri +#define glGetFramebufferParameteriv gl3wGetFramebufferParameteriv +#define glNamedFramebufferParameteriEXT gl3wNamedFramebufferParameteriEXT +#define glGetNamedFramebufferParameterivEXT gl3wGetNamedFramebufferParameterivEXT +#define glGetInternalformati64v gl3wGetInternalformati64v +#define glInvalidateTexSubImage gl3wInvalidateTexSubImage +#define glInvalidateTexImage gl3wInvalidateTexImage +#define glInvalidateBufferSubData gl3wInvalidateBufferSubData +#define glInvalidateBufferData gl3wInvalidateBufferData +#define glInvalidateFramebuffer gl3wInvalidateFramebuffer +#define glInvalidateSubFramebuffer gl3wInvalidateSubFramebuffer +#define glMultiDrawArraysIndirect gl3wMultiDrawArraysIndirect +#define glMultiDrawElementsIndirect gl3wMultiDrawElementsIndirect +#define glGetProgramInterfaceiv gl3wGetProgramInterfaceiv +#define glGetProgramResourceIndex gl3wGetProgramResourceIndex +#define glGetProgramResourceName gl3wGetProgramResourceName +#define glGetProgramResourceiv gl3wGetProgramResourceiv +#define glGetProgramResourceLocation gl3wGetProgramResourceLocation +#define glGetProgramResourceLocationIndex gl3wGetProgramResourceLocationIndex +#define glShaderStorageBlockBinding gl3wShaderStorageBlockBinding +#define glTexBufferRange gl3wTexBufferRange +#define glTextureBufferRangeEXT gl3wTextureBufferRangeEXT +#define glTexStorage2DMultisample gl3wTexStorage2DMultisample +#define glTexStorage3DMultisample gl3wTexStorage3DMultisample +#define glTextureStorage2DMultisampleEXT gl3wTextureStorage2DMultisampleEXT +#define glTextureStorage3DMultisampleEXT gl3wTextureStorage3DMultisampleEXT + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/apps/exampleViewer/common/gl3w/GL/glcorearb.h b/apps/exampleViewer/common/gl3w/GL/glcorearb.h new file mode 100644 index 0000000000..07cb03e13a --- /dev/null +++ b/apps/exampleViewer/common/gl3w/GL/glcorearb.h @@ -0,0 +1,4533 @@ +#ifndef __glcorearb_h_ +#define __glcorearb_h_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* +** Copyright (c) 2007-2012 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are furnished to do so, subject to +** the following conditions: +** +** The above copyright notice and this permission notice shall be included +** in all copies or substantial portions of the Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* glcorearb.h replaces gl3.h. It is for use with OpenGL core + * profile implementations. + * + * glcorearb.h last updated on $Date: 2012-09-19 19:02:24 -0700 (Wed, 19 Sep 2012) $ + * + * RELEASE NOTES - 2012/09/19 + * + * glcorearb.h should be placed in the same directory as gl.h and + * included as + * ''. + * + * glcorearb.h includes only APIs in the latest OpenGL core profile + * implementation together with APIs in newer ARB extensions which can be + * can be supported by the core profile. It does not, and never will + * include functionality removed from the core profile, such as + * fixed-function vertex and fragment processing. + * + * It is not possible to #include both and either of + * or in the same source file. + * + * Feedback can be given by registering for the Khronos Bugzilla + * (www.khronos.org/bugzilla) and filing issues there under product + * "OpenGL", category "Registry". + */ + +/* Function declaration macros - to move into glplatform.h */ + +#if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) && !defined(__SCITECH_SNAP__) +#define WIN32_LEAN_AND_MEAN 1 +#include +#endif + +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif +#ifndef GLAPI +#define GLAPI extern +#endif + +/* Base GL types */ + +typedef unsigned int GLenum; +typedef unsigned char GLboolean; +typedef unsigned int GLbitfield; +typedef signed char GLbyte; +typedef short GLshort; +typedef int GLint; +typedef int GLsizei; +typedef unsigned char GLubyte; +typedef unsigned short GLushort; +typedef unsigned int GLuint; +typedef unsigned short GLhalf; +typedef float GLfloat; +typedef float GLclampf; +typedef double GLdouble; +typedef double GLclampd; +typedef void GLvoid; + +/*************************************************************/ + +#ifndef GL_VERSION_1_1 +/* AttribMask */ +#define GL_DEPTH_BUFFER_BIT 0x00000100 +#define GL_STENCIL_BUFFER_BIT 0x00000400 +#define GL_COLOR_BUFFER_BIT 0x00004000 +/* Boolean */ +#define GL_FALSE 0 +#define GL_TRUE 1 +/* BeginMode */ +#define GL_POINTS 0x0000 +#define GL_LINES 0x0001 +#define GL_LINE_LOOP 0x0002 +#define GL_LINE_STRIP 0x0003 +#define GL_TRIANGLES 0x0004 +#define GL_TRIANGLE_STRIP 0x0005 +#define GL_TRIANGLE_FAN 0x0006 +#define GL_QUADS 0x0007 +/* AlphaFunction */ +#define GL_NEVER 0x0200 +#define GL_LESS 0x0201 +#define GL_EQUAL 0x0202 +#define GL_LEQUAL 0x0203 +#define GL_GREATER 0x0204 +#define GL_NOTEQUAL 0x0205 +#define GL_GEQUAL 0x0206 +#define GL_ALWAYS 0x0207 +/* BlendingFactorDest */ +#define GL_ZERO 0 +#define GL_ONE 1 +#define GL_SRC_COLOR 0x0300 +#define GL_ONE_MINUS_SRC_COLOR 0x0301 +#define GL_SRC_ALPHA 0x0302 +#define GL_ONE_MINUS_SRC_ALPHA 0x0303 +#define GL_DST_ALPHA 0x0304 +#define GL_ONE_MINUS_DST_ALPHA 0x0305 +/* BlendingFactorSrc */ +#define GL_DST_COLOR 0x0306 +#define GL_ONE_MINUS_DST_COLOR 0x0307 +#define GL_SRC_ALPHA_SATURATE 0x0308 +/* DrawBufferMode */ +#define GL_NONE 0 +#define GL_FRONT_LEFT 0x0400 +#define GL_FRONT_RIGHT 0x0401 +#define GL_BACK_LEFT 0x0402 +#define GL_BACK_RIGHT 0x0403 +#define GL_FRONT 0x0404 +#define GL_BACK 0x0405 +#define GL_LEFT 0x0406 +#define GL_RIGHT 0x0407 +#define GL_FRONT_AND_BACK 0x0408 +/* ErrorCode */ +#define GL_NO_ERROR 0 +#define GL_INVALID_ENUM 0x0500 +#define GL_INVALID_VALUE 0x0501 +#define GL_INVALID_OPERATION 0x0502 +#define GL_OUT_OF_MEMORY 0x0505 +/* FrontFaceDirection */ +#define GL_CW 0x0900 +#define GL_CCW 0x0901 +/* GetPName */ +#define GL_POINT_SIZE 0x0B11 +#define GL_POINT_SIZE_RANGE 0x0B12 +#define GL_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_LINE_SMOOTH 0x0B20 +#define GL_LINE_WIDTH 0x0B21 +#define GL_LINE_WIDTH_RANGE 0x0B22 +#define GL_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_POLYGON_MODE 0x0B40 +#define GL_POLYGON_SMOOTH 0x0B41 +#define GL_CULL_FACE 0x0B44 +#define GL_CULL_FACE_MODE 0x0B45 +#define GL_FRONT_FACE 0x0B46 +#define GL_DEPTH_RANGE 0x0B70 +#define GL_DEPTH_TEST 0x0B71 +#define GL_DEPTH_WRITEMASK 0x0B72 +#define GL_DEPTH_CLEAR_VALUE 0x0B73 +#define GL_DEPTH_FUNC 0x0B74 +#define GL_STENCIL_TEST 0x0B90 +#define GL_STENCIL_CLEAR_VALUE 0x0B91 +#define GL_STENCIL_FUNC 0x0B92 +#define GL_STENCIL_VALUE_MASK 0x0B93 +#define GL_STENCIL_FAIL 0x0B94 +#define GL_STENCIL_PASS_DEPTH_FAIL 0x0B95 +#define GL_STENCIL_PASS_DEPTH_PASS 0x0B96 +#define GL_STENCIL_REF 0x0B97 +#define GL_STENCIL_WRITEMASK 0x0B98 +#define GL_VIEWPORT 0x0BA2 +#define GL_DITHER 0x0BD0 +#define GL_BLEND_DST 0x0BE0 +#define GL_BLEND_SRC 0x0BE1 +#define GL_BLEND 0x0BE2 +#define GL_LOGIC_OP_MODE 0x0BF0 +#define GL_COLOR_LOGIC_OP 0x0BF2 +#define GL_DRAW_BUFFER 0x0C01 +#define GL_READ_BUFFER 0x0C02 +#define GL_SCISSOR_BOX 0x0C10 +#define GL_SCISSOR_TEST 0x0C11 +#define GL_COLOR_CLEAR_VALUE 0x0C22 +#define GL_COLOR_WRITEMASK 0x0C23 +#define GL_DOUBLEBUFFER 0x0C32 +#define GL_STEREO 0x0C33 +#define GL_LINE_SMOOTH_HINT 0x0C52 +#define GL_POLYGON_SMOOTH_HINT 0x0C53 +#define GL_UNPACK_SWAP_BYTES 0x0CF0 +#define GL_UNPACK_LSB_FIRST 0x0CF1 +#define GL_UNPACK_ROW_LENGTH 0x0CF2 +#define GL_UNPACK_SKIP_ROWS 0x0CF3 +#define GL_UNPACK_SKIP_PIXELS 0x0CF4 +#define GL_UNPACK_ALIGNMENT 0x0CF5 +#define GL_PACK_SWAP_BYTES 0x0D00 +#define GL_PACK_LSB_FIRST 0x0D01 +#define GL_PACK_ROW_LENGTH 0x0D02 +#define GL_PACK_SKIP_ROWS 0x0D03 +#define GL_PACK_SKIP_PIXELS 0x0D04 +#define GL_PACK_ALIGNMENT 0x0D05 +#define GL_MAX_TEXTURE_SIZE 0x0D33 +#define GL_MAX_VIEWPORT_DIMS 0x0D3A +#define GL_SUBPIXEL_BITS 0x0D50 +#define GL_TEXTURE_1D 0x0DE0 +#define GL_TEXTURE_2D 0x0DE1 +#define GL_POLYGON_OFFSET_UNITS 0x2A00 +#define GL_POLYGON_OFFSET_POINT 0x2A01 +#define GL_POLYGON_OFFSET_LINE 0x2A02 +#define GL_POLYGON_OFFSET_FILL 0x8037 +#define GL_POLYGON_OFFSET_FACTOR 0x8038 +#define GL_TEXTURE_BINDING_1D 0x8068 +#define GL_TEXTURE_BINDING_2D 0x8069 +/* GetTextureParameter */ +#define GL_TEXTURE_WIDTH 0x1000 +#define GL_TEXTURE_HEIGHT 0x1001 +#define GL_TEXTURE_INTERNAL_FORMAT 0x1003 +#define GL_TEXTURE_BORDER_COLOR 0x1004 +#define GL_TEXTURE_RED_SIZE 0x805C +#define GL_TEXTURE_GREEN_SIZE 0x805D +#define GL_TEXTURE_BLUE_SIZE 0x805E +#define GL_TEXTURE_ALPHA_SIZE 0x805F +/* HintMode */ +#define GL_DONT_CARE 0x1100 +#define GL_FASTEST 0x1101 +#define GL_NICEST 0x1102 +/* DataType */ +#define GL_BYTE 0x1400 +#define GL_UNSIGNED_BYTE 0x1401 +#define GL_SHORT 0x1402 +#define GL_UNSIGNED_SHORT 0x1403 +#define GL_INT 0x1404 +#define GL_UNSIGNED_INT 0x1405 +#define GL_FLOAT 0x1406 +#define GL_DOUBLE 0x140A +/* ErrorCode */ +#define GL_STACK_OVERFLOW 0x0503 +#define GL_STACK_UNDERFLOW 0x0504 +/* LogicOp */ +#define GL_CLEAR 0x1500 +#define GL_AND 0x1501 +#define GL_AND_REVERSE 0x1502 +#define GL_COPY 0x1503 +#define GL_AND_INVERTED 0x1504 +#define GL_NOOP 0x1505 +#define GL_XOR 0x1506 +#define GL_OR 0x1507 +#define GL_NOR 0x1508 +#define GL_EQUIV 0x1509 +#define GL_INVERT 0x150A +#define GL_OR_REVERSE 0x150B +#define GL_COPY_INVERTED 0x150C +#define GL_OR_INVERTED 0x150D +#define GL_NAND 0x150E +#define GL_SET 0x150F +/* MatrixMode (for gl3.h, FBO attachment type) */ +#define GL_TEXTURE 0x1702 +/* PixelCopyType */ +#define GL_COLOR 0x1800 +#define GL_DEPTH 0x1801 +#define GL_STENCIL 0x1802 +/* PixelFormat */ +#define GL_STENCIL_INDEX 0x1901 +#define GL_DEPTH_COMPONENT 0x1902 +#define GL_RED 0x1903 +#define GL_GREEN 0x1904 +#define GL_BLUE 0x1905 +#define GL_ALPHA 0x1906 +#define GL_RGB 0x1907 +#define GL_RGBA 0x1908 +/* PolygonMode */ +#define GL_POINT 0x1B00 +#define GL_LINE 0x1B01 +#define GL_FILL 0x1B02 +/* StencilOp */ +#define GL_KEEP 0x1E00 +#define GL_REPLACE 0x1E01 +#define GL_INCR 0x1E02 +#define GL_DECR 0x1E03 +/* StringName */ +#define GL_VENDOR 0x1F00 +#define GL_RENDERER 0x1F01 +#define GL_VERSION 0x1F02 +#define GL_EXTENSIONS 0x1F03 +/* TextureMagFilter */ +#define GL_NEAREST 0x2600 +#define GL_LINEAR 0x2601 +/* TextureMinFilter */ +#define GL_NEAREST_MIPMAP_NEAREST 0x2700 +#define GL_LINEAR_MIPMAP_NEAREST 0x2701 +#define GL_NEAREST_MIPMAP_LINEAR 0x2702 +#define GL_LINEAR_MIPMAP_LINEAR 0x2703 +/* TextureParameterName */ +#define GL_TEXTURE_MAG_FILTER 0x2800 +#define GL_TEXTURE_MIN_FILTER 0x2801 +#define GL_TEXTURE_WRAP_S 0x2802 +#define GL_TEXTURE_WRAP_T 0x2803 +/* TextureTarget */ +#define GL_PROXY_TEXTURE_1D 0x8063 +#define GL_PROXY_TEXTURE_2D 0x8064 +/* TextureWrapMode */ +#define GL_REPEAT 0x2901 +/* PixelInternalFormat */ +#define GL_R3_G3_B2 0x2A10 +#define GL_RGB4 0x804F +#define GL_RGB5 0x8050 +#define GL_RGB8 0x8051 +#define GL_RGB10 0x8052 +#define GL_RGB12 0x8053 +#define GL_RGB16 0x8054 +#define GL_RGBA2 0x8055 +#define GL_RGBA4 0x8056 +#define GL_RGB5_A1 0x8057 +#define GL_RGBA8 0x8058 +#define GL_RGB10_A2 0x8059 +#define GL_RGBA12 0x805A +#define GL_RGBA16 0x805B +#endif + +#ifndef GL_VERSION_1_2 +#define GL_UNSIGNED_BYTE_3_3_2 0x8032 +#define GL_UNSIGNED_SHORT_4_4_4_4 0x8033 +#define GL_UNSIGNED_SHORT_5_5_5_1 0x8034 +#define GL_UNSIGNED_INT_8_8_8_8 0x8035 +#define GL_UNSIGNED_INT_10_10_10_2 0x8036 +#define GL_TEXTURE_BINDING_3D 0x806A +#define GL_PACK_SKIP_IMAGES 0x806B +#define GL_PACK_IMAGE_HEIGHT 0x806C +#define GL_UNPACK_SKIP_IMAGES 0x806D +#define GL_UNPACK_IMAGE_HEIGHT 0x806E +#define GL_TEXTURE_3D 0x806F +#define GL_PROXY_TEXTURE_3D 0x8070 +#define GL_TEXTURE_DEPTH 0x8071 +#define GL_TEXTURE_WRAP_R 0x8072 +#define GL_MAX_3D_TEXTURE_SIZE 0x8073 +#define GL_UNSIGNED_BYTE_2_3_3_REV 0x8362 +#define GL_UNSIGNED_SHORT_5_6_5 0x8363 +#define GL_UNSIGNED_SHORT_5_6_5_REV 0x8364 +#define GL_UNSIGNED_SHORT_4_4_4_4_REV 0x8365 +#define GL_UNSIGNED_SHORT_1_5_5_5_REV 0x8366 +#define GL_UNSIGNED_INT_8_8_8_8_REV 0x8367 +#define GL_UNSIGNED_INT_2_10_10_10_REV 0x8368 +#define GL_BGR 0x80E0 +#define GL_BGRA 0x80E1 +#define GL_MAX_ELEMENTS_VERTICES 0x80E8 +#define GL_MAX_ELEMENTS_INDICES 0x80E9 +#define GL_CLAMP_TO_EDGE 0x812F +#define GL_TEXTURE_MIN_LOD 0x813A +#define GL_TEXTURE_MAX_LOD 0x813B +#define GL_TEXTURE_BASE_LEVEL 0x813C +#define GL_TEXTURE_MAX_LEVEL 0x813D +#define GL_SMOOTH_POINT_SIZE_RANGE 0x0B12 +#define GL_SMOOTH_POINT_SIZE_GRANULARITY 0x0B13 +#define GL_SMOOTH_LINE_WIDTH_RANGE 0x0B22 +#define GL_SMOOTH_LINE_WIDTH_GRANULARITY 0x0B23 +#define GL_ALIASED_LINE_WIDTH_RANGE 0x846E +#endif + +#ifndef GL_ARB_imaging +#define GL_CONSTANT_COLOR 0x8001 +#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002 +#define GL_CONSTANT_ALPHA 0x8003 +#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004 +#define GL_BLEND_COLOR 0x8005 +#define GL_FUNC_ADD 0x8006 +#define GL_MIN 0x8007 +#define GL_MAX 0x8008 +#define GL_BLEND_EQUATION 0x8009 +#define GL_FUNC_SUBTRACT 0x800A +#define GL_FUNC_REVERSE_SUBTRACT 0x800B +#endif + +#ifndef GL_VERSION_1_3 +#define GL_TEXTURE0 0x84C0 +#define GL_TEXTURE1 0x84C1 +#define GL_TEXTURE2 0x84C2 +#define GL_TEXTURE3 0x84C3 +#define GL_TEXTURE4 0x84C4 +#define GL_TEXTURE5 0x84C5 +#define GL_TEXTURE6 0x84C6 +#define GL_TEXTURE7 0x84C7 +#define GL_TEXTURE8 0x84C8 +#define GL_TEXTURE9 0x84C9 +#define GL_TEXTURE10 0x84CA +#define GL_TEXTURE11 0x84CB +#define GL_TEXTURE12 0x84CC +#define GL_TEXTURE13 0x84CD +#define GL_TEXTURE14 0x84CE +#define GL_TEXTURE15 0x84CF +#define GL_TEXTURE16 0x84D0 +#define GL_TEXTURE17 0x84D1 +#define GL_TEXTURE18 0x84D2 +#define GL_TEXTURE19 0x84D3 +#define GL_TEXTURE20 0x84D4 +#define GL_TEXTURE21 0x84D5 +#define GL_TEXTURE22 0x84D6 +#define GL_TEXTURE23 0x84D7 +#define GL_TEXTURE24 0x84D8 +#define GL_TEXTURE25 0x84D9 +#define GL_TEXTURE26 0x84DA +#define GL_TEXTURE27 0x84DB +#define GL_TEXTURE28 0x84DC +#define GL_TEXTURE29 0x84DD +#define GL_TEXTURE30 0x84DE +#define GL_TEXTURE31 0x84DF +#define GL_ACTIVE_TEXTURE 0x84E0 +#define GL_MULTISAMPLE 0x809D +#define GL_SAMPLE_ALPHA_TO_COVERAGE 0x809E +#define GL_SAMPLE_ALPHA_TO_ONE 0x809F +#define GL_SAMPLE_COVERAGE 0x80A0 +#define GL_SAMPLE_BUFFERS 0x80A8 +#define GL_SAMPLES 0x80A9 +#define GL_SAMPLE_COVERAGE_VALUE 0x80AA +#define GL_SAMPLE_COVERAGE_INVERT 0x80AB +#define GL_TEXTURE_CUBE_MAP 0x8513 +#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_X 0x8515 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X 0x8516 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y 0x8517 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y 0x8518 +#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z 0x8519 +#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z 0x851A +#define GL_PROXY_TEXTURE_CUBE_MAP 0x851B +#define GL_MAX_CUBE_MAP_TEXTURE_SIZE 0x851C +#define GL_COMPRESSED_RGB 0x84ED +#define GL_COMPRESSED_RGBA 0x84EE +#define GL_TEXTURE_COMPRESSION_HINT 0x84EF +#define GL_TEXTURE_COMPRESSED_IMAGE_SIZE 0x86A0 +#define GL_TEXTURE_COMPRESSED 0x86A1 +#define GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2 +#define GL_COMPRESSED_TEXTURE_FORMATS 0x86A3 +#define GL_CLAMP_TO_BORDER 0x812D +#endif + +#ifndef GL_VERSION_1_4 +#define GL_BLEND_DST_RGB 0x80C8 +#define GL_BLEND_SRC_RGB 0x80C9 +#define GL_BLEND_DST_ALPHA 0x80CA +#define GL_BLEND_SRC_ALPHA 0x80CB +#define GL_POINT_FADE_THRESHOLD_SIZE 0x8128 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#define GL_MIRRORED_REPEAT 0x8370 +#define GL_MAX_TEXTURE_LOD_BIAS 0x84FD +#define GL_TEXTURE_LOD_BIAS 0x8501 +#define GL_INCR_WRAP 0x8507 +#define GL_DECR_WRAP 0x8508 +#define GL_TEXTURE_DEPTH_SIZE 0x884A +#define GL_TEXTURE_COMPARE_MODE 0x884C +#define GL_TEXTURE_COMPARE_FUNC 0x884D +#endif + +#ifndef GL_VERSION_1_5 +#define GL_BUFFER_SIZE 0x8764 +#define GL_BUFFER_USAGE 0x8765 +#define GL_QUERY_COUNTER_BITS 0x8864 +#define GL_CURRENT_QUERY 0x8865 +#define GL_QUERY_RESULT 0x8866 +#define GL_QUERY_RESULT_AVAILABLE 0x8867 +#define GL_ARRAY_BUFFER 0x8892 +#define GL_ELEMENT_ARRAY_BUFFER 0x8893 +#define GL_ARRAY_BUFFER_BINDING 0x8894 +#define GL_ELEMENT_ARRAY_BUFFER_BINDING 0x8895 +#define GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F +#define GL_READ_ONLY 0x88B8 +#define GL_WRITE_ONLY 0x88B9 +#define GL_READ_WRITE 0x88BA +#define GL_BUFFER_ACCESS 0x88BB +#define GL_BUFFER_MAPPED 0x88BC +#define GL_BUFFER_MAP_POINTER 0x88BD +#define GL_STREAM_DRAW 0x88E0 +#define GL_STREAM_READ 0x88E1 +#define GL_STREAM_COPY 0x88E2 +#define GL_STATIC_DRAW 0x88E4 +#define GL_STATIC_READ 0x88E5 +#define GL_STATIC_COPY 0x88E6 +#define GL_DYNAMIC_DRAW 0x88E8 +#define GL_DYNAMIC_READ 0x88E9 +#define GL_DYNAMIC_COPY 0x88EA +#define GL_SAMPLES_PASSED 0x8914 +#define GL_SRC1_ALPHA 0x8589 +#endif + +#ifndef GL_VERSION_2_0 +#define GL_BLEND_EQUATION_RGB 0x8009 +#define GL_VERTEX_ATTRIB_ARRAY_ENABLED 0x8622 +#define GL_VERTEX_ATTRIB_ARRAY_SIZE 0x8623 +#define GL_VERTEX_ATTRIB_ARRAY_STRIDE 0x8624 +#define GL_VERTEX_ATTRIB_ARRAY_TYPE 0x8625 +#define GL_CURRENT_VERTEX_ATTRIB 0x8626 +#define GL_VERTEX_PROGRAM_POINT_SIZE 0x8642 +#define GL_VERTEX_ATTRIB_ARRAY_POINTER 0x8645 +#define GL_STENCIL_BACK_FUNC 0x8800 +#define GL_STENCIL_BACK_FAIL 0x8801 +#define GL_STENCIL_BACK_PASS_DEPTH_FAIL 0x8802 +#define GL_STENCIL_BACK_PASS_DEPTH_PASS 0x8803 +#define GL_MAX_DRAW_BUFFERS 0x8824 +#define GL_DRAW_BUFFER0 0x8825 +#define GL_DRAW_BUFFER1 0x8826 +#define GL_DRAW_BUFFER2 0x8827 +#define GL_DRAW_BUFFER3 0x8828 +#define GL_DRAW_BUFFER4 0x8829 +#define GL_DRAW_BUFFER5 0x882A +#define GL_DRAW_BUFFER6 0x882B +#define GL_DRAW_BUFFER7 0x882C +#define GL_DRAW_BUFFER8 0x882D +#define GL_DRAW_BUFFER9 0x882E +#define GL_DRAW_BUFFER10 0x882F +#define GL_DRAW_BUFFER11 0x8830 +#define GL_DRAW_BUFFER12 0x8831 +#define GL_DRAW_BUFFER13 0x8832 +#define GL_DRAW_BUFFER14 0x8833 +#define GL_DRAW_BUFFER15 0x8834 +#define GL_BLEND_EQUATION_ALPHA 0x883D +#define GL_MAX_VERTEX_ATTRIBS 0x8869 +#define GL_VERTEX_ATTRIB_ARRAY_NORMALIZED 0x886A +#define GL_MAX_TEXTURE_IMAGE_UNITS 0x8872 +#define GL_FRAGMENT_SHADER 0x8B30 +#define GL_VERTEX_SHADER 0x8B31 +#define GL_MAX_FRAGMENT_UNIFORM_COMPONENTS 0x8B49 +#define GL_MAX_VERTEX_UNIFORM_COMPONENTS 0x8B4A +#define GL_MAX_VARYING_FLOATS 0x8B4B +#define GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS 0x8B4C +#define GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D +#define GL_SHADER_TYPE 0x8B4F +#define GL_FLOAT_VEC2 0x8B50 +#define GL_FLOAT_VEC3 0x8B51 +#define GL_FLOAT_VEC4 0x8B52 +#define GL_INT_VEC2 0x8B53 +#define GL_INT_VEC3 0x8B54 +#define GL_INT_VEC4 0x8B55 +#define GL_BOOL 0x8B56 +#define GL_BOOL_VEC2 0x8B57 +#define GL_BOOL_VEC3 0x8B58 +#define GL_BOOL_VEC4 0x8B59 +#define GL_FLOAT_MAT2 0x8B5A +#define GL_FLOAT_MAT3 0x8B5B +#define GL_FLOAT_MAT4 0x8B5C +#define GL_SAMPLER_1D 0x8B5D +#define GL_SAMPLER_2D 0x8B5E +#define GL_SAMPLER_3D 0x8B5F +#define GL_SAMPLER_CUBE 0x8B60 +#define GL_SAMPLER_1D_SHADOW 0x8B61 +#define GL_SAMPLER_2D_SHADOW 0x8B62 +#define GL_DELETE_STATUS 0x8B80 +#define GL_COMPILE_STATUS 0x8B81 +#define GL_LINK_STATUS 0x8B82 +#define GL_VALIDATE_STATUS 0x8B83 +#define GL_INFO_LOG_LENGTH 0x8B84 +#define GL_ATTACHED_SHADERS 0x8B85 +#define GL_ACTIVE_UNIFORMS 0x8B86 +#define GL_ACTIVE_UNIFORM_MAX_LENGTH 0x8B87 +#define GL_SHADER_SOURCE_LENGTH 0x8B88 +#define GL_ACTIVE_ATTRIBUTES 0x8B89 +#define GL_ACTIVE_ATTRIBUTE_MAX_LENGTH 0x8B8A +#define GL_FRAGMENT_SHADER_DERIVATIVE_HINT 0x8B8B +#define GL_SHADING_LANGUAGE_VERSION 0x8B8C +#define GL_CURRENT_PROGRAM 0x8B8D +#define GL_POINT_SPRITE_COORD_ORIGIN 0x8CA0 +#define GL_LOWER_LEFT 0x8CA1 +#define GL_UPPER_LEFT 0x8CA2 +#define GL_STENCIL_BACK_REF 0x8CA3 +#define GL_STENCIL_BACK_VALUE_MASK 0x8CA4 +#define GL_STENCIL_BACK_WRITEMASK 0x8CA5 +#endif + +#ifndef GL_VERSION_2_1 +#define GL_PIXEL_PACK_BUFFER 0x88EB +#define GL_PIXEL_UNPACK_BUFFER 0x88EC +#define GL_PIXEL_PACK_BUFFER_BINDING 0x88ED +#define GL_PIXEL_UNPACK_BUFFER_BINDING 0x88EF +#define GL_FLOAT_MAT2x3 0x8B65 +#define GL_FLOAT_MAT2x4 0x8B66 +#define GL_FLOAT_MAT3x2 0x8B67 +#define GL_FLOAT_MAT3x4 0x8B68 +#define GL_FLOAT_MAT4x2 0x8B69 +#define GL_FLOAT_MAT4x3 0x8B6A +#define GL_SRGB 0x8C40 +#define GL_SRGB8 0x8C41 +#define GL_SRGB_ALPHA 0x8C42 +#define GL_SRGB8_ALPHA8 0x8C43 +#define GL_COMPRESSED_SRGB 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA 0x8C49 +#endif + +#ifndef GL_VERSION_3_0 +#define GL_COMPARE_REF_TO_TEXTURE 0x884E +#define GL_CLIP_DISTANCE0 0x3000 +#define GL_CLIP_DISTANCE1 0x3001 +#define GL_CLIP_DISTANCE2 0x3002 +#define GL_CLIP_DISTANCE3 0x3003 +#define GL_CLIP_DISTANCE4 0x3004 +#define GL_CLIP_DISTANCE5 0x3005 +#define GL_CLIP_DISTANCE6 0x3006 +#define GL_CLIP_DISTANCE7 0x3007 +#define GL_MAX_CLIP_DISTANCES 0x0D32 +#define GL_MAJOR_VERSION 0x821B +#define GL_MINOR_VERSION 0x821C +#define GL_NUM_EXTENSIONS 0x821D +#define GL_CONTEXT_FLAGS 0x821E +#define GL_COMPRESSED_RED 0x8225 +#define GL_COMPRESSED_RG 0x8226 +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x0001 +#define GL_RGBA32F 0x8814 +#define GL_RGB32F 0x8815 +#define GL_RGBA16F 0x881A +#define GL_RGB16F 0x881B +#define GL_VERTEX_ATTRIB_ARRAY_INTEGER 0x88FD +#define GL_MAX_ARRAY_TEXTURE_LAYERS 0x88FF +#define GL_MIN_PROGRAM_TEXEL_OFFSET 0x8904 +#define GL_MAX_PROGRAM_TEXEL_OFFSET 0x8905 +#define GL_CLAMP_READ_COLOR 0x891C +#define GL_FIXED_ONLY 0x891D +#define GL_MAX_VARYING_COMPONENTS 0x8B4B +#define GL_TEXTURE_1D_ARRAY 0x8C18 +#define GL_PROXY_TEXTURE_1D_ARRAY 0x8C19 +#define GL_TEXTURE_2D_ARRAY 0x8C1A +#define GL_PROXY_TEXTURE_2D_ARRAY 0x8C1B +#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C +#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D +#define GL_R11F_G11F_B10F 0x8C3A +#define GL_UNSIGNED_INT_10F_11F_11F_REV 0x8C3B +#define GL_RGB9_E5 0x8C3D +#define GL_UNSIGNED_INT_5_9_9_9_REV 0x8C3E +#define GL_TEXTURE_SHARED_SIZE 0x8C3F +#define GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH 0x8C76 +#define GL_TRANSFORM_FEEDBACK_BUFFER_MODE 0x8C7F +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_COMPONENTS 0x8C80 +#define GL_TRANSFORM_FEEDBACK_VARYINGS 0x8C83 +#define GL_TRANSFORM_FEEDBACK_BUFFER_START 0x8C84 +#define GL_TRANSFORM_FEEDBACK_BUFFER_SIZE 0x8C85 +#define GL_PRIMITIVES_GENERATED 0x8C87 +#define GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88 +#define GL_RASTERIZER_DISCARD 0x8C89 +#define GL_MAX_TRANSFORM_FEEDBACK_INTERLEAVED_COMPONENTS 0x8C8A +#define GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS 0x8C8B +#define GL_INTERLEAVED_ATTRIBS 0x8C8C +#define GL_SEPARATE_ATTRIBS 0x8C8D +#define GL_TRANSFORM_FEEDBACK_BUFFER 0x8C8E +#define GL_TRANSFORM_FEEDBACK_BUFFER_BINDING 0x8C8F +#define GL_RGBA32UI 0x8D70 +#define GL_RGB32UI 0x8D71 +#define GL_RGBA16UI 0x8D76 +#define GL_RGB16UI 0x8D77 +#define GL_RGBA8UI 0x8D7C +#define GL_RGB8UI 0x8D7D +#define GL_RGBA32I 0x8D82 +#define GL_RGB32I 0x8D83 +#define GL_RGBA16I 0x8D88 +#define GL_RGB16I 0x8D89 +#define GL_RGBA8I 0x8D8E +#define GL_RGB8I 0x8D8F +#define GL_RED_INTEGER 0x8D94 +#define GL_GREEN_INTEGER 0x8D95 +#define GL_BLUE_INTEGER 0x8D96 +#define GL_RGB_INTEGER 0x8D98 +#define GL_RGBA_INTEGER 0x8D99 +#define GL_BGR_INTEGER 0x8D9A +#define GL_BGRA_INTEGER 0x8D9B +#define GL_SAMPLER_1D_ARRAY 0x8DC0 +#define GL_SAMPLER_2D_ARRAY 0x8DC1 +#define GL_SAMPLER_1D_ARRAY_SHADOW 0x8DC3 +#define GL_SAMPLER_2D_ARRAY_SHADOW 0x8DC4 +#define GL_SAMPLER_CUBE_SHADOW 0x8DC5 +#define GL_UNSIGNED_INT_VEC2 0x8DC6 +#define GL_UNSIGNED_INT_VEC3 0x8DC7 +#define GL_UNSIGNED_INT_VEC4 0x8DC8 +#define GL_INT_SAMPLER_1D 0x8DC9 +#define GL_INT_SAMPLER_2D 0x8DCA +#define GL_INT_SAMPLER_3D 0x8DCB +#define GL_INT_SAMPLER_CUBE 0x8DCC +#define GL_INT_SAMPLER_1D_ARRAY 0x8DCE +#define GL_INT_SAMPLER_2D_ARRAY 0x8DCF +#define GL_UNSIGNED_INT_SAMPLER_1D 0x8DD1 +#define GL_UNSIGNED_INT_SAMPLER_2D 0x8DD2 +#define GL_UNSIGNED_INT_SAMPLER_3D 0x8DD3 +#define GL_UNSIGNED_INT_SAMPLER_CUBE 0x8DD4 +#define GL_UNSIGNED_INT_SAMPLER_1D_ARRAY 0x8DD6 +#define GL_UNSIGNED_INT_SAMPLER_2D_ARRAY 0x8DD7 +#define GL_QUERY_WAIT 0x8E13 +#define GL_QUERY_NO_WAIT 0x8E14 +#define GL_QUERY_BY_REGION_WAIT 0x8E15 +#define GL_QUERY_BY_REGION_NO_WAIT 0x8E16 +#define GL_BUFFER_ACCESS_FLAGS 0x911F +#define GL_BUFFER_MAP_LENGTH 0x9120 +#define GL_BUFFER_MAP_OFFSET 0x9121 +/* Reuse tokens from ARB_depth_buffer_float */ +/* reuse GL_DEPTH_COMPONENT32F */ +/* reuse GL_DEPTH32F_STENCIL8 */ +/* reuse GL_FLOAT_32_UNSIGNED_INT_24_8_REV */ +/* Reuse tokens from ARB_framebuffer_object */ +/* reuse GL_INVALID_FRAMEBUFFER_OPERATION */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE */ +/* reuse GL_FRAMEBUFFER_DEFAULT */ +/* reuse GL_FRAMEBUFFER_UNDEFINED */ +/* reuse GL_DEPTH_STENCIL_ATTACHMENT */ +/* reuse GL_INDEX */ +/* reuse GL_MAX_RENDERBUFFER_SIZE */ +/* reuse GL_DEPTH_STENCIL */ +/* reuse GL_UNSIGNED_INT_24_8 */ +/* reuse GL_DEPTH24_STENCIL8 */ +/* reuse GL_TEXTURE_STENCIL_SIZE */ +/* reuse GL_TEXTURE_RED_TYPE */ +/* reuse GL_TEXTURE_GREEN_TYPE */ +/* reuse GL_TEXTURE_BLUE_TYPE */ +/* reuse GL_TEXTURE_ALPHA_TYPE */ +/* reuse GL_TEXTURE_DEPTH_TYPE */ +/* reuse GL_UNSIGNED_NORMALIZED */ +/* reuse GL_FRAMEBUFFER_BINDING */ +/* reuse GL_DRAW_FRAMEBUFFER_BINDING */ +/* reuse GL_RENDERBUFFER_BINDING */ +/* reuse GL_READ_FRAMEBUFFER */ +/* reuse GL_DRAW_FRAMEBUFFER */ +/* reuse GL_READ_FRAMEBUFFER_BINDING */ +/* reuse GL_RENDERBUFFER_SAMPLES */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER */ +/* reuse GL_FRAMEBUFFER_COMPLETE */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER */ +/* reuse GL_FRAMEBUFFER_UNSUPPORTED */ +/* reuse GL_MAX_COLOR_ATTACHMENTS */ +/* reuse GL_COLOR_ATTACHMENT0 */ +/* reuse GL_COLOR_ATTACHMENT1 */ +/* reuse GL_COLOR_ATTACHMENT2 */ +/* reuse GL_COLOR_ATTACHMENT3 */ +/* reuse GL_COLOR_ATTACHMENT4 */ +/* reuse GL_COLOR_ATTACHMENT5 */ +/* reuse GL_COLOR_ATTACHMENT6 */ +/* reuse GL_COLOR_ATTACHMENT7 */ +/* reuse GL_COLOR_ATTACHMENT8 */ +/* reuse GL_COLOR_ATTACHMENT9 */ +/* reuse GL_COLOR_ATTACHMENT10 */ +/* reuse GL_COLOR_ATTACHMENT11 */ +/* reuse GL_COLOR_ATTACHMENT12 */ +/* reuse GL_COLOR_ATTACHMENT13 */ +/* reuse GL_COLOR_ATTACHMENT14 */ +/* reuse GL_COLOR_ATTACHMENT15 */ +/* reuse GL_DEPTH_ATTACHMENT */ +/* reuse GL_STENCIL_ATTACHMENT */ +/* reuse GL_FRAMEBUFFER */ +/* reuse GL_RENDERBUFFER */ +/* reuse GL_RENDERBUFFER_WIDTH */ +/* reuse GL_RENDERBUFFER_HEIGHT */ +/* reuse GL_RENDERBUFFER_INTERNAL_FORMAT */ +/* reuse GL_STENCIL_INDEX1 */ +/* reuse GL_STENCIL_INDEX4 */ +/* reuse GL_STENCIL_INDEX8 */ +/* reuse GL_STENCIL_INDEX16 */ +/* reuse GL_RENDERBUFFER_RED_SIZE */ +/* reuse GL_RENDERBUFFER_GREEN_SIZE */ +/* reuse GL_RENDERBUFFER_BLUE_SIZE */ +/* reuse GL_RENDERBUFFER_ALPHA_SIZE */ +/* reuse GL_RENDERBUFFER_DEPTH_SIZE */ +/* reuse GL_RENDERBUFFER_STENCIL_SIZE */ +/* reuse GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE */ +/* reuse GL_MAX_SAMPLES */ +/* Reuse tokens from ARB_framebuffer_sRGB */ +/* reuse GL_FRAMEBUFFER_SRGB */ +/* Reuse tokens from ARB_half_float_vertex */ +/* reuse GL_HALF_FLOAT */ +/* Reuse tokens from ARB_map_buffer_range */ +/* reuse GL_MAP_READ_BIT */ +/* reuse GL_MAP_WRITE_BIT */ +/* reuse GL_MAP_INVALIDATE_RANGE_BIT */ +/* reuse GL_MAP_INVALIDATE_BUFFER_BIT */ +/* reuse GL_MAP_FLUSH_EXPLICIT_BIT */ +/* reuse GL_MAP_UNSYNCHRONIZED_BIT */ +/* Reuse tokens from ARB_texture_compression_rgtc */ +/* reuse GL_COMPRESSED_RED_RGTC1 */ +/* reuse GL_COMPRESSED_SIGNED_RED_RGTC1 */ +/* reuse GL_COMPRESSED_RG_RGTC2 */ +/* reuse GL_COMPRESSED_SIGNED_RG_RGTC2 */ +/* Reuse tokens from ARB_texture_rg */ +/* reuse GL_RG */ +/* reuse GL_RG_INTEGER */ +/* reuse GL_R8 */ +/* reuse GL_R16 */ +/* reuse GL_RG8 */ +/* reuse GL_RG16 */ +/* reuse GL_R16F */ +/* reuse GL_R32F */ +/* reuse GL_RG16F */ +/* reuse GL_RG32F */ +/* reuse GL_R8I */ +/* reuse GL_R8UI */ +/* reuse GL_R16I */ +/* reuse GL_R16UI */ +/* reuse GL_R32I */ +/* reuse GL_R32UI */ +/* reuse GL_RG8I */ +/* reuse GL_RG8UI */ +/* reuse GL_RG16I */ +/* reuse GL_RG16UI */ +/* reuse GL_RG32I */ +/* reuse GL_RG32UI */ +/* Reuse tokens from ARB_vertex_array_object */ +/* reuse GL_VERTEX_ARRAY_BINDING */ +#endif + +#ifndef GL_VERSION_3_1 +#define GL_SAMPLER_2D_RECT 0x8B63 +#define GL_SAMPLER_2D_RECT_SHADOW 0x8B64 +#define GL_SAMPLER_BUFFER 0x8DC2 +#define GL_INT_SAMPLER_2D_RECT 0x8DCD +#define GL_INT_SAMPLER_BUFFER 0x8DD0 +#define GL_UNSIGNED_INT_SAMPLER_2D_RECT 0x8DD5 +#define GL_UNSIGNED_INT_SAMPLER_BUFFER 0x8DD8 +#define GL_TEXTURE_BUFFER 0x8C2A +#define GL_MAX_TEXTURE_BUFFER_SIZE 0x8C2B +#define GL_TEXTURE_BINDING_BUFFER 0x8C2C +#define GL_TEXTURE_BUFFER_DATA_STORE_BINDING 0x8C2D +#define GL_TEXTURE_RECTANGLE 0x84F5 +#define GL_TEXTURE_BINDING_RECTANGLE 0x84F6 +#define GL_PROXY_TEXTURE_RECTANGLE 0x84F7 +#define GL_MAX_RECTANGLE_TEXTURE_SIZE 0x84F8 +#define GL_RED_SNORM 0x8F90 +#define GL_RG_SNORM 0x8F91 +#define GL_RGB_SNORM 0x8F92 +#define GL_RGBA_SNORM 0x8F93 +#define GL_R8_SNORM 0x8F94 +#define GL_RG8_SNORM 0x8F95 +#define GL_RGB8_SNORM 0x8F96 +#define GL_RGBA8_SNORM 0x8F97 +#define GL_R16_SNORM 0x8F98 +#define GL_RG16_SNORM 0x8F99 +#define GL_RGB16_SNORM 0x8F9A +#define GL_RGBA16_SNORM 0x8F9B +#define GL_SIGNED_NORMALIZED 0x8F9C +#define GL_PRIMITIVE_RESTART 0x8F9D +#define GL_PRIMITIVE_RESTART_INDEX 0x8F9E +/* Reuse tokens from ARB_copy_buffer */ +/* reuse GL_COPY_READ_BUFFER */ +/* reuse GL_COPY_WRITE_BUFFER */ +/* Reuse tokens from ARB_draw_instanced (none) */ +/* Reuse tokens from ARB_uniform_buffer_object */ +/* reuse GL_UNIFORM_BUFFER */ +/* reuse GL_UNIFORM_BUFFER_BINDING */ +/* reuse GL_UNIFORM_BUFFER_START */ +/* reuse GL_UNIFORM_BUFFER_SIZE */ +/* reuse GL_MAX_VERTEX_UNIFORM_BLOCKS */ +/* reuse GL_MAX_FRAGMENT_UNIFORM_BLOCKS */ +/* reuse GL_MAX_COMBINED_UNIFORM_BLOCKS */ +/* reuse GL_MAX_UNIFORM_BUFFER_BINDINGS */ +/* reuse GL_MAX_UNIFORM_BLOCK_SIZE */ +/* reuse GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS */ +/* reuse GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT */ +/* reuse GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH */ +/* reuse GL_ACTIVE_UNIFORM_BLOCKS */ +/* reuse GL_UNIFORM_TYPE */ +/* reuse GL_UNIFORM_SIZE */ +/* reuse GL_UNIFORM_NAME_LENGTH */ +/* reuse GL_UNIFORM_BLOCK_INDEX */ +/* reuse GL_UNIFORM_OFFSET */ +/* reuse GL_UNIFORM_ARRAY_STRIDE */ +/* reuse GL_UNIFORM_MATRIX_STRIDE */ +/* reuse GL_UNIFORM_IS_ROW_MAJOR */ +/* reuse GL_UNIFORM_BLOCK_BINDING */ +/* reuse GL_UNIFORM_BLOCK_DATA_SIZE */ +/* reuse GL_UNIFORM_BLOCK_NAME_LENGTH */ +/* reuse GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS */ +/* reuse GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER */ +/* reuse GL_INVALID_INDEX */ +#endif + +#ifndef GL_VERSION_3_2 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_LINES_ADJACENCY 0x000A +#define GL_LINE_STRIP_ADJACENCY 0x000B +#define GL_TRIANGLES_ADJACENCY 0x000C +#define GL_TRIANGLE_STRIP_ADJACENCY 0x000D +#define GL_PROGRAM_POINT_SIZE 0x8642 +#define GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS 0x8C29 +#define GL_FRAMEBUFFER_ATTACHMENT_LAYERED 0x8DA7 +#define GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS 0x8DA8 +#define GL_GEOMETRY_SHADER 0x8DD9 +#define GL_GEOMETRY_VERTICES_OUT 0x8916 +#define GL_GEOMETRY_INPUT_TYPE 0x8917 +#define GL_GEOMETRY_OUTPUT_TYPE 0x8918 +#define GL_MAX_GEOMETRY_UNIFORM_COMPONENTS 0x8DDF +#define GL_MAX_GEOMETRY_OUTPUT_VERTICES 0x8DE0 +#define GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS 0x8DE1 +#define GL_MAX_VERTEX_OUTPUT_COMPONENTS 0x9122 +#define GL_MAX_GEOMETRY_INPUT_COMPONENTS 0x9123 +#define GL_MAX_GEOMETRY_OUTPUT_COMPONENTS 0x9124 +#define GL_MAX_FRAGMENT_INPUT_COMPONENTS 0x9125 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +/* reuse GL_MAX_VARYING_COMPONENTS */ +/* reuse GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER */ +/* Reuse tokens from ARB_depth_clamp */ +/* reuse GL_DEPTH_CLAMP */ +/* Reuse tokens from ARB_draw_elements_base_vertex (none) */ +/* Reuse tokens from ARB_fragment_coord_conventions (none) */ +/* Reuse tokens from ARB_provoking_vertex */ +/* reuse GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION */ +/* reuse GL_FIRST_VERTEX_CONVENTION */ +/* reuse GL_LAST_VERTEX_CONVENTION */ +/* reuse GL_PROVOKING_VERTEX */ +/* Reuse tokens from ARB_seamless_cube_map */ +/* reuse GL_TEXTURE_CUBE_MAP_SEAMLESS */ +/* Reuse tokens from ARB_sync */ +/* reuse GL_MAX_SERVER_WAIT_TIMEOUT */ +/* reuse GL_OBJECT_TYPE */ +/* reuse GL_SYNC_CONDITION */ +/* reuse GL_SYNC_STATUS */ +/* reuse GL_SYNC_FLAGS */ +/* reuse GL_SYNC_FENCE */ +/* reuse GL_SYNC_GPU_COMMANDS_COMPLETE */ +/* reuse GL_UNSIGNALED */ +/* reuse GL_SIGNALED */ +/* reuse GL_ALREADY_SIGNALED */ +/* reuse GL_TIMEOUT_EXPIRED */ +/* reuse GL_CONDITION_SATISFIED */ +/* reuse GL_WAIT_FAILED */ +/* reuse GL_TIMEOUT_IGNORED */ +/* reuse GL_SYNC_FLUSH_COMMANDS_BIT */ +/* reuse GL_TIMEOUT_IGNORED */ +/* Reuse tokens from ARB_texture_multisample */ +/* reuse GL_SAMPLE_POSITION */ +/* reuse GL_SAMPLE_MASK */ +/* reuse GL_SAMPLE_MASK_VALUE */ +/* reuse GL_MAX_SAMPLE_MASK_WORDS */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_PROXY_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_BINDING_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_SAMPLES */ +/* reuse GL_TEXTURE_FIXED_SAMPLE_LOCATIONS */ +/* reuse GL_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_INT_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE */ +/* reuse GL_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_MAX_COLOR_TEXTURE_SAMPLES */ +/* reuse GL_MAX_DEPTH_TEXTURE_SAMPLES */ +/* reuse GL_MAX_INTEGER_SAMPLES */ +/* Don't need to reuse tokens from ARB_vertex_array_bgra since they're already in 1.2 core */ +#endif + +#ifndef GL_VERSION_3_3 +#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE +/* Reuse tokens from ARB_blend_func_extended */ +/* reuse GL_SRC1_COLOR */ +/* reuse GL_ONE_MINUS_SRC1_COLOR */ +/* reuse GL_ONE_MINUS_SRC1_ALPHA */ +/* reuse GL_MAX_DUAL_SOURCE_DRAW_BUFFERS */ +/* Reuse tokens from ARB_explicit_attrib_location (none) */ +/* Reuse tokens from ARB_occlusion_query2 */ +/* reuse GL_ANY_SAMPLES_PASSED */ +/* Reuse tokens from ARB_sampler_objects */ +/* reuse GL_SAMPLER_BINDING */ +/* Reuse tokens from ARB_shader_bit_encoding (none) */ +/* Reuse tokens from ARB_texture_rgb10_a2ui */ +/* reuse GL_RGB10_A2UI */ +/* Reuse tokens from ARB_texture_swizzle */ +/* reuse GL_TEXTURE_SWIZZLE_R */ +/* reuse GL_TEXTURE_SWIZZLE_G */ +/* reuse GL_TEXTURE_SWIZZLE_B */ +/* reuse GL_TEXTURE_SWIZZLE_A */ +/* reuse GL_TEXTURE_SWIZZLE_RGBA */ +/* Reuse tokens from ARB_timer_query */ +/* reuse GL_TIME_ELAPSED */ +/* reuse GL_TIMESTAMP */ +/* Reuse tokens from ARB_vertex_type_2_10_10_10_rev */ +/* reuse GL_INT_2_10_10_10_REV */ +#endif + +#ifndef GL_VERSION_4_0 +#define GL_SAMPLE_SHADING 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE 0x8C37 +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET 0x8E5F +#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY 0x900F +/* Reuse tokens from ARB_texture_query_lod (none) */ +/* Reuse tokens from ARB_draw_buffers_blend (none) */ +/* Reuse tokens from ARB_draw_indirect */ +/* reuse GL_DRAW_INDIRECT_BUFFER */ +/* reuse GL_DRAW_INDIRECT_BUFFER_BINDING */ +/* Reuse tokens from ARB_gpu_shader5 */ +/* reuse GL_GEOMETRY_SHADER_INVOCATIONS */ +/* reuse GL_MAX_GEOMETRY_SHADER_INVOCATIONS */ +/* reuse GL_MIN_FRAGMENT_INTERPOLATION_OFFSET */ +/* reuse GL_MAX_FRAGMENT_INTERPOLATION_OFFSET */ +/* reuse GL_FRAGMENT_INTERPOLATION_OFFSET_BITS */ +/* reuse GL_MAX_VERTEX_STREAMS */ +/* Reuse tokens from ARB_gpu_shader_fp64 */ +/* reuse GL_DOUBLE_VEC2 */ +/* reuse GL_DOUBLE_VEC3 */ +/* reuse GL_DOUBLE_VEC4 */ +/* reuse GL_DOUBLE_MAT2 */ +/* reuse GL_DOUBLE_MAT3 */ +/* reuse GL_DOUBLE_MAT4 */ +/* reuse GL_DOUBLE_MAT2x3 */ +/* reuse GL_DOUBLE_MAT2x4 */ +/* reuse GL_DOUBLE_MAT3x2 */ +/* reuse GL_DOUBLE_MAT3x4 */ +/* reuse GL_DOUBLE_MAT4x2 */ +/* reuse GL_DOUBLE_MAT4x3 */ +/* Reuse tokens from ARB_shader_subroutine */ +/* reuse GL_ACTIVE_SUBROUTINES */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORMS */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS */ +/* reuse GL_ACTIVE_SUBROUTINE_MAX_LENGTH */ +/* reuse GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH */ +/* reuse GL_MAX_SUBROUTINES */ +/* reuse GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS */ +/* reuse GL_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_COMPATIBLE_SUBROUTINES */ +/* Reuse tokens from ARB_tessellation_shader */ +/* reuse GL_PATCHES */ +/* reuse GL_PATCH_VERTICES */ +/* reuse GL_PATCH_DEFAULT_INNER_LEVEL */ +/* reuse GL_PATCH_DEFAULT_OUTER_LEVEL */ +/* reuse GL_TESS_CONTROL_OUTPUT_VERTICES */ +/* reuse GL_TESS_GEN_MODE */ +/* reuse GL_TESS_GEN_SPACING */ +/* reuse GL_TESS_GEN_VERTEX_ORDER */ +/* reuse GL_TESS_GEN_POINT_MODE */ +/* reuse GL_ISOLINES */ +/* reuse GL_FRACTIONAL_ODD */ +/* reuse GL_FRACTIONAL_EVEN */ +/* reuse GL_MAX_PATCH_VERTICES */ +/* reuse GL_MAX_TESS_GEN_LEVEL */ +/* reuse GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_PATCH_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS */ +/* reuse GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS */ +/* reuse GL_MAX_TESS_CONTROL_INPUT_COMPONENTS */ +/* reuse GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS */ +/* reuse GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER */ +/* reuse GL_TESS_EVALUATION_SHADER */ +/* reuse GL_TESS_CONTROL_SHADER */ +/* Reuse tokens from ARB_texture_buffer_object_rgb32 (none) */ +/* Reuse tokens from ARB_transform_feedback2 */ +/* reuse GL_TRANSFORM_FEEDBACK */ +/* reuse GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED */ +/* reuse GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE */ +/* reuse GL_TRANSFORM_FEEDBACK_BINDING */ +/* Reuse tokens from ARB_transform_feedback3 */ +/* reuse GL_MAX_TRANSFORM_FEEDBACK_BUFFERS */ +/* reuse GL_MAX_VERTEX_STREAMS */ +#endif + +#ifndef GL_VERSION_4_1 +/* Reuse tokens from ARB_ES2_compatibility */ +/* reuse GL_FIXED */ +/* reuse GL_IMPLEMENTATION_COLOR_READ_TYPE */ +/* reuse GL_IMPLEMENTATION_COLOR_READ_FORMAT */ +/* reuse GL_LOW_FLOAT */ +/* reuse GL_MEDIUM_FLOAT */ +/* reuse GL_HIGH_FLOAT */ +/* reuse GL_LOW_INT */ +/* reuse GL_MEDIUM_INT */ +/* reuse GL_HIGH_INT */ +/* reuse GL_SHADER_COMPILER */ +/* reuse GL_SHADER_BINARY_FORMATS */ +/* reuse GL_NUM_SHADER_BINARY_FORMATS */ +/* reuse GL_MAX_VERTEX_UNIFORM_VECTORS */ +/* reuse GL_MAX_VARYING_VECTORS */ +/* reuse GL_MAX_FRAGMENT_UNIFORM_VECTORS */ +/* reuse GL_RGB565 */ +/* Reuse tokens from ARB_get_program_binary */ +/* reuse GL_PROGRAM_BINARY_RETRIEVABLE_HINT */ +/* reuse GL_PROGRAM_BINARY_LENGTH */ +/* reuse GL_NUM_PROGRAM_BINARY_FORMATS */ +/* reuse GL_PROGRAM_BINARY_FORMATS */ +/* Reuse tokens from ARB_separate_shader_objects */ +/* reuse GL_VERTEX_SHADER_BIT */ +/* reuse GL_FRAGMENT_SHADER_BIT */ +/* reuse GL_GEOMETRY_SHADER_BIT */ +/* reuse GL_TESS_CONTROL_SHADER_BIT */ +/* reuse GL_TESS_EVALUATION_SHADER_BIT */ +/* reuse GL_ALL_SHADER_BITS */ +/* reuse GL_PROGRAM_SEPARABLE */ +/* reuse GL_ACTIVE_PROGRAM */ +/* reuse GL_PROGRAM_PIPELINE_BINDING */ +/* Reuse tokens from ARB_shader_precision (none) */ +/* Reuse tokens from ARB_vertex_attrib_64bit - all are in GL 3.0 and 4.0 already */ +/* Reuse tokens from ARB_viewport_array - some are in GL 1.1 and ARB_provoking_vertex already */ +/* reuse GL_MAX_VIEWPORTS */ +/* reuse GL_VIEWPORT_SUBPIXEL_BITS */ +/* reuse GL_VIEWPORT_BOUNDS_RANGE */ +/* reuse GL_LAYER_PROVOKING_VERTEX */ +/* reuse GL_VIEWPORT_INDEX_PROVOKING_VERTEX */ +/* reuse GL_UNDEFINED_VERTEX */ +#endif + +#ifndef GL_VERSION_4_2 +/* Reuse tokens from ARB_base_instance (none) */ +/* Reuse tokens from ARB_shading_language_420pack (none) */ +/* Reuse tokens from ARB_transform_feedback_instanced (none) */ +/* Reuse tokens from ARB_compressed_texture_pixel_storage */ +/* reuse GL_UNPACK_COMPRESSED_BLOCK_WIDTH */ +/* reuse GL_UNPACK_COMPRESSED_BLOCK_HEIGHT */ +/* reuse GL_UNPACK_COMPRESSED_BLOCK_DEPTH */ +/* reuse GL_UNPACK_COMPRESSED_BLOCK_SIZE */ +/* reuse GL_PACK_COMPRESSED_BLOCK_WIDTH */ +/* reuse GL_PACK_COMPRESSED_BLOCK_HEIGHT */ +/* reuse GL_PACK_COMPRESSED_BLOCK_DEPTH */ +/* reuse GL_PACK_COMPRESSED_BLOCK_SIZE */ +/* Reuse tokens from ARB_conservative_depth (none) */ +/* Reuse tokens from ARB_internalformat_query */ +/* reuse GL_NUM_SAMPLE_COUNTS */ +/* Reuse tokens from ARB_map_buffer_alignment */ +/* reuse GL_MIN_MAP_BUFFER_ALIGNMENT */ +/* Reuse tokens from ARB_shader_atomic_counters */ +/* reuse GL_ATOMIC_COUNTER_BUFFER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_BINDING */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_START */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_SIZE */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER */ +/* reuse GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_VERTEX_ATOMIC_COUNTERS */ +/* reuse GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS */ +/* reuse GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS */ +/* reuse GL_MAX_GEOMETRY_ATOMIC_COUNTERS */ +/* reuse GL_MAX_FRAGMENT_ATOMIC_COUNTERS */ +/* reuse GL_MAX_COMBINED_ATOMIC_COUNTERS */ +/* reuse GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE */ +/* reuse GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS */ +/* reuse GL_ACTIVE_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX */ +/* reuse GL_UNSIGNED_INT_ATOMIC_COUNTER */ +/* Reuse tokens from ARB_shader_image_load_store */ +/* reuse GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT */ +/* reuse GL_ELEMENT_ARRAY_BARRIER_BIT */ +/* reuse GL_UNIFORM_BARRIER_BIT */ +/* reuse GL_TEXTURE_FETCH_BARRIER_BIT */ +/* reuse GL_SHADER_IMAGE_ACCESS_BARRIER_BIT */ +/* reuse GL_COMMAND_BARRIER_BIT */ +/* reuse GL_PIXEL_BUFFER_BARRIER_BIT */ +/* reuse GL_TEXTURE_UPDATE_BARRIER_BIT */ +/* reuse GL_BUFFER_UPDATE_BARRIER_BIT */ +/* reuse GL_FRAMEBUFFER_BARRIER_BIT */ +/* reuse GL_TRANSFORM_FEEDBACK_BARRIER_BIT */ +/* reuse GL_ATOMIC_COUNTER_BARRIER_BIT */ +/* reuse GL_ALL_BARRIER_BITS */ +/* reuse GL_MAX_IMAGE_UNITS */ +/* reuse GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS */ +/* reuse GL_IMAGE_BINDING_NAME */ +/* reuse GL_IMAGE_BINDING_LEVEL */ +/* reuse GL_IMAGE_BINDING_LAYERED */ +/* reuse GL_IMAGE_BINDING_LAYER */ +/* reuse GL_IMAGE_BINDING_ACCESS */ +/* reuse GL_IMAGE_1D */ +/* reuse GL_IMAGE_2D */ +/* reuse GL_IMAGE_3D */ +/* reuse GL_IMAGE_2D_RECT */ +/* reuse GL_IMAGE_CUBE */ +/* reuse GL_IMAGE_BUFFER */ +/* reuse GL_IMAGE_1D_ARRAY */ +/* reuse GL_IMAGE_2D_ARRAY */ +/* reuse GL_IMAGE_CUBE_MAP_ARRAY */ +/* reuse GL_IMAGE_2D_MULTISAMPLE */ +/* reuse GL_IMAGE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_INT_IMAGE_1D */ +/* reuse GL_INT_IMAGE_2D */ +/* reuse GL_INT_IMAGE_3D */ +/* reuse GL_INT_IMAGE_2D_RECT */ +/* reuse GL_INT_IMAGE_CUBE */ +/* reuse GL_INT_IMAGE_BUFFER */ +/* reuse GL_INT_IMAGE_1D_ARRAY */ +/* reuse GL_INT_IMAGE_2D_ARRAY */ +/* reuse GL_INT_IMAGE_CUBE_MAP_ARRAY */ +/* reuse GL_INT_IMAGE_2D_MULTISAMPLE */ +/* reuse GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_UNSIGNED_INT_IMAGE_1D */ +/* reuse GL_UNSIGNED_INT_IMAGE_2D */ +/* reuse GL_UNSIGNED_INT_IMAGE_3D */ +/* reuse GL_UNSIGNED_INT_IMAGE_2D_RECT */ +/* reuse GL_UNSIGNED_INT_IMAGE_CUBE */ +/* reuse GL_UNSIGNED_INT_IMAGE_BUFFER */ +/* reuse GL_UNSIGNED_INT_IMAGE_1D_ARRAY */ +/* reuse GL_UNSIGNED_INT_IMAGE_2D_ARRAY */ +/* reuse GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY */ +/* reuse GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE */ +/* reuse GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_MAX_IMAGE_SAMPLES */ +/* reuse GL_IMAGE_BINDING_FORMAT */ +/* reuse GL_IMAGE_FORMAT_COMPATIBILITY_TYPE */ +/* reuse GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE */ +/* reuse GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS */ +/* reuse GL_MAX_VERTEX_IMAGE_UNIFORMS */ +/* reuse GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS */ +/* reuse GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS */ +/* reuse GL_MAX_GEOMETRY_IMAGE_UNIFORMS */ +/* reuse GL_MAX_FRAGMENT_IMAGE_UNIFORMS */ +/* reuse GL_MAX_COMBINED_IMAGE_UNIFORMS */ +/* Reuse tokens from ARB_shading_language_packing (none) */ +/* Reuse tokens from ARB_texture_storage */ +/* reuse GL_TEXTURE_IMMUTABLE_FORMAT */ +#endif + +#ifndef GL_VERSION_4_3 +#define GL_NUM_SHADING_LANGUAGE_VERSIONS 0x82E9 +#define GL_VERTEX_ATTRIB_ARRAY_LONG 0x874E +/* Reuse tokens from ARB_arrays_of_arrays (none, GLSL only) */ +/* Reuse tokens from ARB_fragment_layer_viewport (none, GLSL only) */ +/* Reuse tokens from ARB_shader_image_size (none, GLSL only) */ +/* Reuse tokens from ARB_ES3_compatibility */ +/* reuse GL_COMPRESSED_RGB8_ETC2 */ +/* reuse GL_COMPRESSED_SRGB8_ETC2 */ +/* reuse GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 */ +/* reuse GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 */ +/* reuse GL_COMPRESSED_RGBA8_ETC2_EAC */ +/* reuse GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC */ +/* reuse GL_COMPRESSED_R11_EAC */ +/* reuse GL_COMPRESSED_SIGNED_R11_EAC */ +/* reuse GL_COMPRESSED_RG11_EAC */ +/* reuse GL_COMPRESSED_SIGNED_RG11_EAC */ +/* reuse GL_PRIMITIVE_RESTART_FIXED_INDEX */ +/* reuse GL_ANY_SAMPLES_PASSED_CONSERVATIVE */ +/* reuse GL_MAX_ELEMENT_INDEX */ +/* Reuse tokens from ARB_clear_buffer_object (none) */ +/* Reuse tokens from ARB_compute_shader */ +/* reuse GL_COMPUTE_SHADER */ +/* reuse GL_MAX_COMPUTE_UNIFORM_BLOCKS */ +/* reuse GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS */ +/* reuse GL_MAX_COMPUTE_IMAGE_UNIFORMS */ +/* reuse GL_MAX_COMPUTE_SHARED_MEMORY_SIZE */ +/* reuse GL_MAX_COMPUTE_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS */ +/* reuse GL_MAX_COMPUTE_ATOMIC_COUNTERS */ +/* reuse GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS */ +/* reuse GL_MAX_COMPUTE_LOCAL_INVOCATIONS */ +/* reuse GL_MAX_COMPUTE_WORK_GROUP_COUNT */ +/* reuse GL_MAX_COMPUTE_WORK_GROUP_SIZE */ +/* reuse GL_COMPUTE_LOCAL_WORK_SIZE */ +/* reuse GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_DISPATCH_INDIRECT_BUFFER */ +/* reuse GL_DISPATCH_INDIRECT_BUFFER_BINDING */ +/* Reuse tokens from ARB_copy_image (none) */ +/* Reuse tokens from KHR_debug */ +/* reuse GL_DEBUG_OUTPUT_SYNCHRONOUS */ +/* reuse GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH */ +/* reuse GL_DEBUG_CALLBACK_FUNCTION */ +/* reuse GL_DEBUG_CALLBACK_USER_PARAM */ +/* reuse GL_DEBUG_SOURCE_API */ +/* reuse GL_DEBUG_SOURCE_WINDOW_SYSTEM */ +/* reuse GL_DEBUG_SOURCE_SHADER_COMPILER */ +/* reuse GL_DEBUG_SOURCE_THIRD_PARTY */ +/* reuse GL_DEBUG_SOURCE_APPLICATION */ +/* reuse GL_DEBUG_SOURCE_OTHER */ +/* reuse GL_DEBUG_TYPE_ERROR */ +/* reuse GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR */ +/* reuse GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR */ +/* reuse GL_DEBUG_TYPE_PORTABILITY */ +/* reuse GL_DEBUG_TYPE_PERFORMANCE */ +/* reuse GL_DEBUG_TYPE_OTHER */ +/* reuse GL_MAX_DEBUG_MESSAGE_LENGTH */ +/* reuse GL_MAX_DEBUG_LOGGED_MESSAGES */ +/* reuse GL_DEBUG_LOGGED_MESSAGES */ +/* reuse GL_DEBUG_SEVERITY_HIGH */ +/* reuse GL_DEBUG_SEVERITY_MEDIUM */ +/* reuse GL_DEBUG_SEVERITY_LOW */ +/* reuse GL_DEBUG_TYPE_MARKER */ +/* reuse GL_DEBUG_TYPE_PUSH_GROUP */ +/* reuse GL_DEBUG_TYPE_POP_GROUP */ +/* reuse GL_DEBUG_SEVERITY_NOTIFICATION */ +/* reuse GL_MAX_DEBUG_GROUP_STACK_DEPTH */ +/* reuse GL_DEBUG_GROUP_STACK_DEPTH */ +/* reuse GL_BUFFER */ +/* reuse GL_SHADER */ +/* reuse GL_PROGRAM */ +/* reuse GL_QUERY */ +/* reuse GL_PROGRAM_PIPELINE */ +/* reuse GL_SAMPLER */ +/* reuse GL_DISPLAY_LIST */ +/* reuse GL_MAX_LABEL_LENGTH */ +/* reuse GL_DEBUG_OUTPUT */ +/* reuse GL_CONTEXT_FLAG_DEBUG_BIT */ +/* reuse GL_STACK_UNDERFLOW */ +/* reuse GL_STACK_OVERFLOW */ +/* Reuse tokens from ARB_explicit_uniform_location */ +/* reuse GL_MAX_UNIFORM_LOCATIONS */ +/* Reuse tokens from ARB_framebuffer_no_attachments */ +/* reuse GL_FRAMEBUFFER_DEFAULT_WIDTH */ +/* reuse GL_FRAMEBUFFER_DEFAULT_HEIGHT */ +/* reuse GL_FRAMEBUFFER_DEFAULT_LAYERS */ +/* reuse GL_FRAMEBUFFER_DEFAULT_SAMPLES */ +/* reuse GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS */ +/* reuse GL_MAX_FRAMEBUFFER_WIDTH */ +/* reuse GL_MAX_FRAMEBUFFER_HEIGHT */ +/* reuse GL_MAX_FRAMEBUFFER_LAYERS */ +/* reuse GL_MAX_FRAMEBUFFER_SAMPLES */ +/* Reuse tokens from ARB_internalformat_query2 */ +/* reuse GL_INTERNALFORMAT_SUPPORTED */ +/* reuse GL_INTERNALFORMAT_PREFERRED */ +/* reuse GL_INTERNALFORMAT_RED_SIZE */ +/* reuse GL_INTERNALFORMAT_GREEN_SIZE */ +/* reuse GL_INTERNALFORMAT_BLUE_SIZE */ +/* reuse GL_INTERNALFORMAT_ALPHA_SIZE */ +/* reuse GL_INTERNALFORMAT_DEPTH_SIZE */ +/* reuse GL_INTERNALFORMAT_STENCIL_SIZE */ +/* reuse GL_INTERNALFORMAT_SHARED_SIZE */ +/* reuse GL_INTERNALFORMAT_RED_TYPE */ +/* reuse GL_INTERNALFORMAT_GREEN_TYPE */ +/* reuse GL_INTERNALFORMAT_BLUE_TYPE */ +/* reuse GL_INTERNALFORMAT_ALPHA_TYPE */ +/* reuse GL_INTERNALFORMAT_DEPTH_TYPE */ +/* reuse GL_INTERNALFORMAT_STENCIL_TYPE */ +/* reuse GL_MAX_WIDTH */ +/* reuse GL_MAX_HEIGHT */ +/* reuse GL_MAX_DEPTH */ +/* reuse GL_MAX_LAYERS */ +/* reuse GL_MAX_COMBINED_DIMENSIONS */ +/* reuse GL_COLOR_COMPONENTS */ +/* reuse GL_DEPTH_COMPONENTS */ +/* reuse GL_STENCIL_COMPONENTS */ +/* reuse GL_COLOR_RENDERABLE */ +/* reuse GL_DEPTH_RENDERABLE */ +/* reuse GL_STENCIL_RENDERABLE */ +/* reuse GL_FRAMEBUFFER_RENDERABLE */ +/* reuse GL_FRAMEBUFFER_RENDERABLE_LAYERED */ +/* reuse GL_FRAMEBUFFER_BLEND */ +/* reuse GL_READ_PIXELS */ +/* reuse GL_READ_PIXELS_FORMAT */ +/* reuse GL_READ_PIXELS_TYPE */ +/* reuse GL_TEXTURE_IMAGE_FORMAT */ +/* reuse GL_TEXTURE_IMAGE_TYPE */ +/* reuse GL_GET_TEXTURE_IMAGE_FORMAT */ +/* reuse GL_GET_TEXTURE_IMAGE_TYPE */ +/* reuse GL_MIPMAP */ +/* reuse GL_MANUAL_GENERATE_MIPMAP */ +/* reuse GL_AUTO_GENERATE_MIPMAP */ +/* reuse GL_COLOR_ENCODING */ +/* reuse GL_SRGB_READ */ +/* reuse GL_SRGB_WRITE */ +/* reuse GL_FILTER */ +/* reuse GL_VERTEX_TEXTURE */ +/* reuse GL_TESS_CONTROL_TEXTURE */ +/* reuse GL_TESS_EVALUATION_TEXTURE */ +/* reuse GL_GEOMETRY_TEXTURE */ +/* reuse GL_FRAGMENT_TEXTURE */ +/* reuse GL_COMPUTE_TEXTURE */ +/* reuse GL_TEXTURE_SHADOW */ +/* reuse GL_TEXTURE_GATHER */ +/* reuse GL_TEXTURE_GATHER_SHADOW */ +/* reuse GL_SHADER_IMAGE_LOAD */ +/* reuse GL_SHADER_IMAGE_STORE */ +/* reuse GL_SHADER_IMAGE_ATOMIC */ +/* reuse GL_IMAGE_TEXEL_SIZE */ +/* reuse GL_IMAGE_COMPATIBILITY_CLASS */ +/* reuse GL_IMAGE_PIXEL_FORMAT */ +/* reuse GL_IMAGE_PIXEL_TYPE */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE */ +/* reuse GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_WIDTH */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT */ +/* reuse GL_TEXTURE_COMPRESSED_BLOCK_SIZE */ +/* reuse GL_CLEAR_BUFFER */ +/* reuse GL_TEXTURE_VIEW */ +/* reuse GL_VIEW_COMPATIBILITY_CLASS */ +/* reuse GL_FULL_SUPPORT */ +/* reuse GL_CAVEAT_SUPPORT */ +/* reuse GL_IMAGE_CLASS_4_X_32 */ +/* reuse GL_IMAGE_CLASS_2_X_32 */ +/* reuse GL_IMAGE_CLASS_1_X_32 */ +/* reuse GL_IMAGE_CLASS_4_X_16 */ +/* reuse GL_IMAGE_CLASS_2_X_16 */ +/* reuse GL_IMAGE_CLASS_1_X_16 */ +/* reuse GL_IMAGE_CLASS_4_X_8 */ +/* reuse GL_IMAGE_CLASS_2_X_8 */ +/* reuse GL_IMAGE_CLASS_1_X_8 */ +/* reuse GL_IMAGE_CLASS_11_11_10 */ +/* reuse GL_IMAGE_CLASS_10_10_10_2 */ +/* reuse GL_VIEW_CLASS_128_BITS */ +/* reuse GL_VIEW_CLASS_96_BITS */ +/* reuse GL_VIEW_CLASS_64_BITS */ +/* reuse GL_VIEW_CLASS_48_BITS */ +/* reuse GL_VIEW_CLASS_32_BITS */ +/* reuse GL_VIEW_CLASS_24_BITS */ +/* reuse GL_VIEW_CLASS_16_BITS */ +/* reuse GL_VIEW_CLASS_8_BITS */ +/* reuse GL_VIEW_CLASS_S3TC_DXT1_RGB */ +/* reuse GL_VIEW_CLASS_S3TC_DXT1_RGBA */ +/* reuse GL_VIEW_CLASS_S3TC_DXT3_RGBA */ +/* reuse GL_VIEW_CLASS_S3TC_DXT5_RGBA */ +/* reuse GL_VIEW_CLASS_RGTC1_RED */ +/* reuse GL_VIEW_CLASS_RGTC2_RG */ +/* reuse GL_VIEW_CLASS_BPTC_UNORM */ +/* reuse GL_VIEW_CLASS_BPTC_FLOAT */ +/* Reuse tokens from ARB_invalidate_subdata (none) */ +/* Reuse tokens from ARB_multi_draw_indirect (none) */ +/* Reuse tokens from ARB_program_interface_query */ +/* reuse GL_UNIFORM */ +/* reuse GL_UNIFORM_BLOCK */ +/* reuse GL_PROGRAM_INPUT */ +/* reuse GL_PROGRAM_OUTPUT */ +/* reuse GL_BUFFER_VARIABLE */ +/* reuse GL_SHADER_STORAGE_BLOCK */ +/* reuse GL_VERTEX_SUBROUTINE */ +/* reuse GL_TESS_CONTROL_SUBROUTINE */ +/* reuse GL_TESS_EVALUATION_SUBROUTINE */ +/* reuse GL_GEOMETRY_SUBROUTINE */ +/* reuse GL_FRAGMENT_SUBROUTINE */ +/* reuse GL_COMPUTE_SUBROUTINE */ +/* reuse GL_VERTEX_SUBROUTINE_UNIFORM */ +/* reuse GL_TESS_CONTROL_SUBROUTINE_UNIFORM */ +/* reuse GL_TESS_EVALUATION_SUBROUTINE_UNIFORM */ +/* reuse GL_GEOMETRY_SUBROUTINE_UNIFORM */ +/* reuse GL_FRAGMENT_SUBROUTINE_UNIFORM */ +/* reuse GL_COMPUTE_SUBROUTINE_UNIFORM */ +/* reuse GL_TRANSFORM_FEEDBACK_VARYING */ +/* reuse GL_ACTIVE_RESOURCES */ +/* reuse GL_MAX_NAME_LENGTH */ +/* reuse GL_MAX_NUM_ACTIVE_VARIABLES */ +/* reuse GL_MAX_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_NAME_LENGTH */ +/* reuse GL_TYPE */ +/* reuse GL_ARRAY_SIZE */ +/* reuse GL_OFFSET */ +/* reuse GL_BLOCK_INDEX */ +/* reuse GL_ARRAY_STRIDE */ +/* reuse GL_MATRIX_STRIDE */ +/* reuse GL_IS_ROW_MAJOR */ +/* reuse GL_ATOMIC_COUNTER_BUFFER_INDEX */ +/* reuse GL_BUFFER_BINDING */ +/* reuse GL_BUFFER_DATA_SIZE */ +/* reuse GL_NUM_ACTIVE_VARIABLES */ +/* reuse GL_ACTIVE_VARIABLES */ +/* reuse GL_REFERENCED_BY_VERTEX_SHADER */ +/* reuse GL_REFERENCED_BY_TESS_CONTROL_SHADER */ +/* reuse GL_REFERENCED_BY_TESS_EVALUATION_SHADER */ +/* reuse GL_REFERENCED_BY_GEOMETRY_SHADER */ +/* reuse GL_REFERENCED_BY_FRAGMENT_SHADER */ +/* reuse GL_REFERENCED_BY_COMPUTE_SHADER */ +/* reuse GL_TOP_LEVEL_ARRAY_SIZE */ +/* reuse GL_TOP_LEVEL_ARRAY_STRIDE */ +/* reuse GL_LOCATION */ +/* reuse GL_LOCATION_INDEX */ +/* reuse GL_IS_PER_PATCH */ +/* Reuse tokens from ARB_robust_buffer_access_behavior (none) */ +/* Reuse tokens from ARB_shader_storage_buffer_object */ +/* reuse GL_SHADER_STORAGE_BUFFER */ +/* reuse GL_SHADER_STORAGE_BUFFER_BINDING */ +/* reuse GL_SHADER_STORAGE_BUFFER_START */ +/* reuse GL_SHADER_STORAGE_BUFFER_SIZE */ +/* reuse GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS */ +/* reuse GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS */ +/* reuse GL_MAX_SHADER_STORAGE_BLOCK_SIZE */ +/* reuse GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT */ +/* reuse GL_SHADER_STORAGE_BARRIER_BIT */ +/* reuse GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES */ +/* Reuse tokens from ARB_stencil_texturing */ +/* reuse GL_DEPTH_STENCIL_TEXTURE_MODE */ +/* Reuse tokens from ARB_texture_buffer_range */ +/* reuse GL_TEXTURE_BUFFER_OFFSET */ +/* reuse GL_TEXTURE_BUFFER_SIZE */ +/* reuse GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT */ +/* Reuse tokens from ARB_texture_query_levels (none) */ +/* Reuse tokens from ARB_texture_storage_multisample (none) */ +/* Reuse tokens from ARB_texture_view */ +/* reuse GL_TEXTURE_VIEW_MIN_LEVEL */ +/* reuse GL_TEXTURE_VIEW_NUM_LEVELS */ +/* reuse GL_TEXTURE_VIEW_MIN_LAYER */ +/* reuse GL_TEXTURE_VIEW_NUM_LAYERS */ +/* reuse GL_TEXTURE_IMMUTABLE_LEVELS */ +/* Reuse tokens from ARB_vertex_attrib_binding */ +/* reuse GL_VERTEX_ATTRIB_BINDING */ +/* reuse GL_VERTEX_ATTRIB_RELATIVE_OFFSET */ +/* reuse GL_VERTEX_BINDING_DIVISOR */ +/* reuse GL_VERTEX_BINDING_OFFSET */ +/* reuse GL_VERTEX_BINDING_STRIDE */ +/* reuse GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET */ +/* reuse GL_MAX_VERTEX_ATTRIB_BINDINGS */ +#endif + +#ifndef GL_ARB_depth_buffer_float +#define GL_DEPTH_COMPONENT32F 0x8CAC +#define GL_DEPTH32F_STENCIL8 0x8CAD +#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV 0x8DAD +#endif + +#ifndef GL_ARB_framebuffer_object +#define GL_INVALID_FRAMEBUFFER_OPERATION 0x0506 +#define GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING 0x8210 +#define GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE 0x8211 +#define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 +#define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 +#define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 +#define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 +#define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 +#define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 +#define GL_FRAMEBUFFER_DEFAULT 0x8218 +#define GL_FRAMEBUFFER_UNDEFINED 0x8219 +#define GL_DEPTH_STENCIL_ATTACHMENT 0x821A +#define GL_MAX_RENDERBUFFER_SIZE 0x84E8 +#define GL_DEPTH_STENCIL 0x84F9 +#define GL_UNSIGNED_INT_24_8 0x84FA +#define GL_DEPTH24_STENCIL8 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE 0x88F1 +#define GL_TEXTURE_RED_TYPE 0x8C10 +#define GL_TEXTURE_GREEN_TYPE 0x8C11 +#define GL_TEXTURE_BLUE_TYPE 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE 0x8C13 +#define GL_TEXTURE_DEPTH_TYPE 0x8C16 +#define GL_UNSIGNED_NORMALIZED 0x8C17 +#define GL_FRAMEBUFFER_BINDING 0x8CA6 +#define GL_DRAW_FRAMEBUFFER_BINDING GL_FRAMEBUFFER_BINDING +#define GL_RENDERBUFFER_BINDING 0x8CA7 +#define GL_READ_FRAMEBUFFER 0x8CA8 +#define GL_DRAW_FRAMEBUFFER 0x8CA9 +#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA +#define GL_RENDERBUFFER_SAMPLES 0x8CAB +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER 0x8CD4 +#define GL_FRAMEBUFFER_COMPLETE 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED 0x8CDD +#define GL_MAX_COLOR_ATTACHMENTS 0x8CDF +#define GL_COLOR_ATTACHMENT0 0x8CE0 +#define GL_COLOR_ATTACHMENT1 0x8CE1 +#define GL_COLOR_ATTACHMENT2 0x8CE2 +#define GL_COLOR_ATTACHMENT3 0x8CE3 +#define GL_COLOR_ATTACHMENT4 0x8CE4 +#define GL_COLOR_ATTACHMENT5 0x8CE5 +#define GL_COLOR_ATTACHMENT6 0x8CE6 +#define GL_COLOR_ATTACHMENT7 0x8CE7 +#define GL_COLOR_ATTACHMENT8 0x8CE8 +#define GL_COLOR_ATTACHMENT9 0x8CE9 +#define GL_COLOR_ATTACHMENT10 0x8CEA +#define GL_COLOR_ATTACHMENT11 0x8CEB +#define GL_COLOR_ATTACHMENT12 0x8CEC +#define GL_COLOR_ATTACHMENT13 0x8CED +#define GL_COLOR_ATTACHMENT14 0x8CEE +#define GL_COLOR_ATTACHMENT15 0x8CEF +#define GL_DEPTH_ATTACHMENT 0x8D00 +#define GL_STENCIL_ATTACHMENT 0x8D20 +#define GL_FRAMEBUFFER 0x8D40 +#define GL_RENDERBUFFER 0x8D41 +#define GL_RENDERBUFFER_WIDTH 0x8D42 +#define GL_RENDERBUFFER_HEIGHT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT 0x8D44 +#define GL_STENCIL_INDEX1 0x8D46 +#define GL_STENCIL_INDEX4 0x8D47 +#define GL_STENCIL_INDEX8 0x8D48 +#define GL_STENCIL_INDEX16 0x8D49 +#define GL_RENDERBUFFER_RED_SIZE 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55 +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56 +#define GL_MAX_SAMPLES 0x8D57 +#endif + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_FRAMEBUFFER_SRGB 0x8DB9 +#endif + +#ifndef GL_ARB_half_float_vertex +#define GL_HALF_FLOAT 0x140B +#endif + +#ifndef GL_ARB_map_buffer_range +#define GL_MAP_READ_BIT 0x0001 +#define GL_MAP_WRITE_BIT 0x0002 +#define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 +#define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 +#define GL_MAP_FLUSH_EXPLICIT_BIT 0x0010 +#define GL_MAP_UNSYNCHRONIZED_BIT 0x0020 +#endif + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_COMPRESSED_RED_RGTC1 0x8DBB +#define GL_COMPRESSED_SIGNED_RED_RGTC1 0x8DBC +#define GL_COMPRESSED_RG_RGTC2 0x8DBD +#define GL_COMPRESSED_SIGNED_RG_RGTC2 0x8DBE +#endif + +#ifndef GL_ARB_texture_rg +#define GL_RG 0x8227 +#define GL_RG_INTEGER 0x8228 +#define GL_R8 0x8229 +#define GL_R16 0x822A +#define GL_RG8 0x822B +#define GL_RG16 0x822C +#define GL_R16F 0x822D +#define GL_R32F 0x822E +#define GL_RG16F 0x822F +#define GL_RG32F 0x8230 +#define GL_R8I 0x8231 +#define GL_R8UI 0x8232 +#define GL_R16I 0x8233 +#define GL_R16UI 0x8234 +#define GL_R32I 0x8235 +#define GL_R32UI 0x8236 +#define GL_RG8I 0x8237 +#define GL_RG8UI 0x8238 +#define GL_RG16I 0x8239 +#define GL_RG16UI 0x823A +#define GL_RG32I 0x823B +#define GL_RG32UI 0x823C +#endif + +#ifndef GL_ARB_vertex_array_object +#define GL_VERTEX_ARRAY_BINDING 0x85B5 +#endif + +#ifndef GL_ARB_uniform_buffer_object +#define GL_UNIFORM_BUFFER 0x8A11 +#define GL_UNIFORM_BUFFER_BINDING 0x8A28 +#define GL_UNIFORM_BUFFER_START 0x8A29 +#define GL_UNIFORM_BUFFER_SIZE 0x8A2A +#define GL_MAX_VERTEX_UNIFORM_BLOCKS 0x8A2B +#define GL_MAX_GEOMETRY_UNIFORM_BLOCKS 0x8A2C +#define GL_MAX_FRAGMENT_UNIFORM_BLOCKS 0x8A2D +#define GL_MAX_COMBINED_UNIFORM_BLOCKS 0x8A2E +#define GL_MAX_UNIFORM_BUFFER_BINDINGS 0x8A2F +#define GL_MAX_UNIFORM_BLOCK_SIZE 0x8A30 +#define GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS 0x8A31 +#define GL_MAX_COMBINED_GEOMETRY_UNIFORM_COMPONENTS 0x8A32 +#define GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS 0x8A33 +#define GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT 0x8A34 +#define GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH 0x8A35 +#define GL_ACTIVE_UNIFORM_BLOCKS 0x8A36 +#define GL_UNIFORM_TYPE 0x8A37 +#define GL_UNIFORM_SIZE 0x8A38 +#define GL_UNIFORM_NAME_LENGTH 0x8A39 +#define GL_UNIFORM_BLOCK_INDEX 0x8A3A +#define GL_UNIFORM_OFFSET 0x8A3B +#define GL_UNIFORM_ARRAY_STRIDE 0x8A3C +#define GL_UNIFORM_MATRIX_STRIDE 0x8A3D +#define GL_UNIFORM_IS_ROW_MAJOR 0x8A3E +#define GL_UNIFORM_BLOCK_BINDING 0x8A3F +#define GL_UNIFORM_BLOCK_DATA_SIZE 0x8A40 +#define GL_UNIFORM_BLOCK_NAME_LENGTH 0x8A41 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS 0x8A42 +#define GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES 0x8A43 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER 0x8A44 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER 0x8A45 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER 0x8A46 +#define GL_INVALID_INDEX 0xFFFFFFFFu +#endif + +#ifndef GL_ARB_copy_buffer +#define GL_COPY_READ_BUFFER_BINDING 0x8F36 +#define GL_COPY_READ_BUFFER GL_COPY_READ_BUFFER_BINDING +#define GL_COPY_WRITE_BUFFER_BINDING 0x8F37 +#define GL_COPY_WRITE_BUFFER GL_COPY_WRITE_BUFFER_BINDING +#endif + +#ifndef GL_ARB_depth_clamp +#define GL_DEPTH_CLAMP 0x864F +#endif + +#ifndef GL_ARB_draw_elements_base_vertex +#endif + +#ifndef GL_ARB_fragment_coord_conventions +#endif + +#ifndef GL_ARB_provoking_vertex +#define GL_QUADS_FOLLOW_PROVOKING_VERTEX_CONVENTION 0x8E4C +#define GL_FIRST_VERTEX_CONVENTION 0x8E4D +#define GL_LAST_VERTEX_CONVENTION 0x8E4E +#define GL_PROVOKING_VERTEX 0x8E4F +#endif + +#ifndef GL_ARB_seamless_cube_map +#define GL_TEXTURE_CUBE_MAP_SEAMLESS 0x884F +#endif + +#ifndef GL_ARB_sync +#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111 +#define GL_OBJECT_TYPE 0x9112 +#define GL_SYNC_CONDITION 0x9113 +#define GL_SYNC_STATUS 0x9114 +#define GL_SYNC_FLAGS 0x9115 +#define GL_SYNC_FENCE 0x9116 +#define GL_SYNC_GPU_COMMANDS_COMPLETE 0x9117 +#define GL_UNSIGNALED 0x9118 +#define GL_SIGNALED 0x9119 +#define GL_ALREADY_SIGNALED 0x911A +#define GL_TIMEOUT_EXPIRED 0x911B +#define GL_CONDITION_SATISFIED 0x911C +#define GL_WAIT_FAILED 0x911D +#define GL_SYNC_FLUSH_COMMANDS_BIT 0x00000001 +#define GL_TIMEOUT_IGNORED 0xFFFFFFFFFFFFFFFFull +#endif + +#ifndef GL_ARB_texture_multisample +#define GL_SAMPLE_POSITION 0x8E50 +#define GL_SAMPLE_MASK 0x8E51 +#define GL_SAMPLE_MASK_VALUE 0x8E52 +#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59 +#define GL_TEXTURE_2D_MULTISAMPLE 0x9100 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE 0x9101 +#define GL_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9102 +#define GL_PROXY_TEXTURE_2D_MULTISAMPLE_ARRAY 0x9103 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE 0x9104 +#define GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY 0x9105 +#define GL_TEXTURE_SAMPLES 0x9106 +#define GL_TEXTURE_FIXED_SAMPLE_LOCATIONS 0x9107 +#define GL_SAMPLER_2D_MULTISAMPLE 0x9108 +#define GL_INT_SAMPLER_2D_MULTISAMPLE 0x9109 +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE 0x910A +#define GL_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910B +#define GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910C +#define GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY 0x910D +#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E +#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F +#define GL_MAX_INTEGER_SAMPLES 0x9110 +#endif + +#ifndef GL_ARB_vertex_array_bgra +/* reuse GL_BGRA */ +#endif + +#ifndef GL_ARB_draw_buffers_blend +#endif + +#ifndef GL_ARB_sample_shading +#define GL_SAMPLE_SHADING_ARB 0x8C36 +#define GL_MIN_SAMPLE_SHADING_VALUE_ARB 0x8C37 +#endif + +#ifndef GL_ARB_texture_cube_map_array +#define GL_TEXTURE_CUBE_MAP_ARRAY_ARB 0x9009 +#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY_ARB 0x900A +#define GL_PROXY_TEXTURE_CUBE_MAP_ARRAY_ARB 0x900B +#define GL_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900C +#define GL_SAMPLER_CUBE_MAP_ARRAY_SHADOW_ARB 0x900D +#define GL_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900E +#define GL_UNSIGNED_INT_SAMPLER_CUBE_MAP_ARRAY_ARB 0x900F +#endif + +#ifndef GL_ARB_texture_gather +#define GL_MIN_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5E +#define GL_MAX_PROGRAM_TEXTURE_GATHER_OFFSET_ARB 0x8E5F +#define GL_MAX_PROGRAM_TEXTURE_GATHER_COMPONENTS_ARB 0x8F9F +#endif + +#ifndef GL_ARB_texture_query_lod +#endif + +#ifndef GL_ARB_shading_language_include +#define GL_SHADER_INCLUDE_ARB 0x8DAE +#define GL_NAMED_STRING_LENGTH_ARB 0x8DE9 +#define GL_NAMED_STRING_TYPE_ARB 0x8DEA +#endif + +#ifndef GL_ARB_texture_compression_bptc +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif + +#ifndef GL_ARB_blend_func_extended +#define GL_SRC1_COLOR 0x88F9 +/* reuse GL_SRC1_ALPHA */ +#define GL_ONE_MINUS_SRC1_COLOR 0x88FA +#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB +#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC +#endif + +#ifndef GL_ARB_explicit_attrib_location +#endif + +#ifndef GL_ARB_occlusion_query2 +#define GL_ANY_SAMPLES_PASSED 0x8C2F +#endif + +#ifndef GL_ARB_sampler_objects +#define GL_SAMPLER_BINDING 0x8919 +#endif + +#ifndef GL_ARB_shader_bit_encoding +#endif + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_RGB10_A2UI 0x906F +#endif + +#ifndef GL_ARB_texture_swizzle +#define GL_TEXTURE_SWIZZLE_R 0x8E42 +#define GL_TEXTURE_SWIZZLE_G 0x8E43 +#define GL_TEXTURE_SWIZZLE_B 0x8E44 +#define GL_TEXTURE_SWIZZLE_A 0x8E45 +#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 +#endif + +#ifndef GL_ARB_timer_query +#define GL_TIME_ELAPSED 0x88BF +#define GL_TIMESTAMP 0x8E28 +#endif + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +/* reuse GL_UNSIGNED_INT_2_10_10_10_REV */ +#define GL_INT_2_10_10_10_REV 0x8D9F +#endif + +#ifndef GL_ARB_draw_indirect +#define GL_DRAW_INDIRECT_BUFFER 0x8F3F +#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43 +#endif + +#ifndef GL_ARB_gpu_shader5 +#define GL_GEOMETRY_SHADER_INVOCATIONS 0x887F +#define GL_MAX_GEOMETRY_SHADER_INVOCATIONS 0x8E5A +#define GL_MIN_FRAGMENT_INTERPOLATION_OFFSET 0x8E5B +#define GL_MAX_FRAGMENT_INTERPOLATION_OFFSET 0x8E5C +#define GL_FRAGMENT_INTERPOLATION_OFFSET_BITS 0x8E5D +/* reuse GL_MAX_VERTEX_STREAMS */ +#endif + +#ifndef GL_ARB_gpu_shader_fp64 +/* reuse GL_DOUBLE */ +#define GL_DOUBLE_VEC2 0x8FFC +#define GL_DOUBLE_VEC3 0x8FFD +#define GL_DOUBLE_VEC4 0x8FFE +#define GL_DOUBLE_MAT2 0x8F46 +#define GL_DOUBLE_MAT3 0x8F47 +#define GL_DOUBLE_MAT4 0x8F48 +#define GL_DOUBLE_MAT2x3 0x8F49 +#define GL_DOUBLE_MAT2x4 0x8F4A +#define GL_DOUBLE_MAT3x2 0x8F4B +#define GL_DOUBLE_MAT3x4 0x8F4C +#define GL_DOUBLE_MAT4x2 0x8F4D +#define GL_DOUBLE_MAT4x3 0x8F4E +#endif + +#ifndef GL_ARB_shader_subroutine +#define GL_ACTIVE_SUBROUTINES 0x8DE5 +#define GL_ACTIVE_SUBROUTINE_UNIFORMS 0x8DE6 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_LOCATIONS 0x8E47 +#define GL_ACTIVE_SUBROUTINE_MAX_LENGTH 0x8E48 +#define GL_ACTIVE_SUBROUTINE_UNIFORM_MAX_LENGTH 0x8E49 +#define GL_MAX_SUBROUTINES 0x8DE7 +#define GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS 0x8DE8 +#define GL_NUM_COMPATIBLE_SUBROUTINES 0x8E4A +#define GL_COMPATIBLE_SUBROUTINES 0x8E4B +/* reuse GL_UNIFORM_SIZE */ +/* reuse GL_UNIFORM_NAME_LENGTH */ +#endif + +#ifndef GL_ARB_tessellation_shader +#define GL_PATCHES 0x000E +#define GL_PATCH_VERTICES 0x8E72 +#define GL_PATCH_DEFAULT_INNER_LEVEL 0x8E73 +#define GL_PATCH_DEFAULT_OUTER_LEVEL 0x8E74 +#define GL_TESS_CONTROL_OUTPUT_VERTICES 0x8E75 +#define GL_TESS_GEN_MODE 0x8E76 +#define GL_TESS_GEN_SPACING 0x8E77 +#define GL_TESS_GEN_VERTEX_ORDER 0x8E78 +#define GL_TESS_GEN_POINT_MODE 0x8E79 +/* reuse GL_TRIANGLES */ +/* reuse GL_QUADS */ +#define GL_ISOLINES 0x8E7A +/* reuse GL_EQUAL */ +#define GL_FRACTIONAL_ODD 0x8E7B +#define GL_FRACTIONAL_EVEN 0x8E7C +/* reuse GL_CCW */ +/* reuse GL_CW */ +#define GL_MAX_PATCH_VERTICES 0x8E7D +#define GL_MAX_TESS_GEN_LEVEL 0x8E7E +#define GL_MAX_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E7F +#define GL_MAX_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E80 +#define GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS 0x8E81 +#define GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS 0x8E82 +#define GL_MAX_TESS_CONTROL_OUTPUT_COMPONENTS 0x8E83 +#define GL_MAX_TESS_PATCH_COMPONENTS 0x8E84 +#define GL_MAX_TESS_CONTROL_TOTAL_OUTPUT_COMPONENTS 0x8E85 +#define GL_MAX_TESS_EVALUATION_OUTPUT_COMPONENTS 0x8E86 +#define GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS 0x8E89 +#define GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS 0x8E8A +#define GL_MAX_TESS_CONTROL_INPUT_COMPONENTS 0x886C +#define GL_MAX_TESS_EVALUATION_INPUT_COMPONENTS 0x886D +#define GL_MAX_COMBINED_TESS_CONTROL_UNIFORM_COMPONENTS 0x8E1E +#define GL_MAX_COMBINED_TESS_EVALUATION_UNIFORM_COMPONENTS 0x8E1F +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_CONTROL_SHADER 0x84F0 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_TESS_EVALUATION_SHADER 0x84F1 +#define GL_TESS_EVALUATION_SHADER 0x8E87 +#define GL_TESS_CONTROL_SHADER 0x8E88 +#endif + +#ifndef GL_ARB_texture_buffer_object_rgb32 +/* reuse GL_RGB32F */ +/* reuse GL_RGB32UI */ +/* reuse GL_RGB32I */ +#endif + +#ifndef GL_ARB_transform_feedback2 +#define GL_TRANSFORM_FEEDBACK 0x8E22 +#define GL_TRANSFORM_FEEDBACK_PAUSED 0x8E23 +#define GL_TRANSFORM_FEEDBACK_BUFFER_PAUSED GL_TRANSFORM_FEEDBACK_PAUSED +#define GL_TRANSFORM_FEEDBACK_ACTIVE 0x8E24 +#define GL_TRANSFORM_FEEDBACK_BUFFER_ACTIVE GL_TRANSFORM_FEEDBACK_ACTIVE +#define GL_TRANSFORM_FEEDBACK_BINDING 0x8E25 +#endif + +#ifndef GL_ARB_transform_feedback3 +#define GL_MAX_TRANSFORM_FEEDBACK_BUFFERS 0x8E70 +#define GL_MAX_VERTEX_STREAMS 0x8E71 +#endif + +#ifndef GL_ARB_ES2_compatibility +#define GL_FIXED 0x140C +#define GL_IMPLEMENTATION_COLOR_READ_TYPE 0x8B9A +#define GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B +#define GL_LOW_FLOAT 0x8DF0 +#define GL_MEDIUM_FLOAT 0x8DF1 +#define GL_HIGH_FLOAT 0x8DF2 +#define GL_LOW_INT 0x8DF3 +#define GL_MEDIUM_INT 0x8DF4 +#define GL_HIGH_INT 0x8DF5 +#define GL_SHADER_COMPILER 0x8DFA +#define GL_SHADER_BINARY_FORMATS 0x8DF8 +#define GL_NUM_SHADER_BINARY_FORMATS 0x8DF9 +#define GL_MAX_VERTEX_UNIFORM_VECTORS 0x8DFB +#define GL_MAX_VARYING_VECTORS 0x8DFC +#define GL_MAX_FRAGMENT_UNIFORM_VECTORS 0x8DFD +#define GL_RGB565 0x8D62 +#endif + +#ifndef GL_ARB_get_program_binary +#define GL_PROGRAM_BINARY_RETRIEVABLE_HINT 0x8257 +#define GL_PROGRAM_BINARY_LENGTH 0x8741 +#define GL_NUM_PROGRAM_BINARY_FORMATS 0x87FE +#define GL_PROGRAM_BINARY_FORMATS 0x87FF +#endif + +#ifndef GL_ARB_separate_shader_objects +#define GL_VERTEX_SHADER_BIT 0x00000001 +#define GL_FRAGMENT_SHADER_BIT 0x00000002 +#define GL_GEOMETRY_SHADER_BIT 0x00000004 +#define GL_TESS_CONTROL_SHADER_BIT 0x00000008 +#define GL_TESS_EVALUATION_SHADER_BIT 0x00000010 +#define GL_ALL_SHADER_BITS 0xFFFFFFFF +#define GL_PROGRAM_SEPARABLE 0x8258 +#define GL_ACTIVE_PROGRAM 0x8259 +#define GL_PROGRAM_PIPELINE_BINDING 0x825A +#endif + +#ifndef GL_ARB_shader_precision +#endif + +#ifndef GL_ARB_vertex_attrib_64bit +/* reuse GL_RGB32I */ +/* reuse GL_DOUBLE_VEC2 */ +/* reuse GL_DOUBLE_VEC3 */ +/* reuse GL_DOUBLE_VEC4 */ +/* reuse GL_DOUBLE_MAT2 */ +/* reuse GL_DOUBLE_MAT3 */ +/* reuse GL_DOUBLE_MAT4 */ +/* reuse GL_DOUBLE_MAT2x3 */ +/* reuse GL_DOUBLE_MAT2x4 */ +/* reuse GL_DOUBLE_MAT3x2 */ +/* reuse GL_DOUBLE_MAT3x4 */ +/* reuse GL_DOUBLE_MAT4x2 */ +/* reuse GL_DOUBLE_MAT4x3 */ +#endif + +#ifndef GL_ARB_viewport_array +/* reuse GL_SCISSOR_BOX */ +/* reuse GL_VIEWPORT */ +/* reuse GL_DEPTH_RANGE */ +/* reuse GL_SCISSOR_TEST */ +#define GL_MAX_VIEWPORTS 0x825B +#define GL_VIEWPORT_SUBPIXEL_BITS 0x825C +#define GL_VIEWPORT_BOUNDS_RANGE 0x825D +#define GL_LAYER_PROVOKING_VERTEX 0x825E +#define GL_VIEWPORT_INDEX_PROVOKING_VERTEX 0x825F +#define GL_UNDEFINED_VERTEX 0x8260 +/* reuse GL_FIRST_VERTEX_CONVENTION */ +/* reuse GL_LAST_VERTEX_CONVENTION */ +/* reuse GL_PROVOKING_VERTEX */ +#endif + +#ifndef GL_ARB_cl_event +#define GL_SYNC_CL_EVENT_ARB 0x8240 +#define GL_SYNC_CL_EVENT_COMPLETE_ARB 0x8241 +#endif + +#ifndef GL_ARB_debug_output +#define GL_DEBUG_OUTPUT_SYNCHRONOUS_ARB 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH_ARB 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION_ARB 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM_ARB 0x8245 +#define GL_DEBUG_SOURCE_API_ARB 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM_ARB 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER_ARB 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY_ARB 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION_ARB 0x824A +#define GL_DEBUG_SOURCE_OTHER_ARB 0x824B +#define GL_DEBUG_TYPE_ERROR_ARB 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR_ARB 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR_ARB 0x824E +#define GL_DEBUG_TYPE_PORTABILITY_ARB 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE_ARB 0x8250 +#define GL_DEBUG_TYPE_OTHER_ARB 0x8251 +#define GL_MAX_DEBUG_MESSAGE_LENGTH_ARB 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES_ARB 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES_ARB 0x9145 +#define GL_DEBUG_SEVERITY_HIGH_ARB 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM_ARB 0x9147 +#define GL_DEBUG_SEVERITY_LOW_ARB 0x9148 +#endif + +#ifndef GL_ARB_robustness +/* reuse GL_NO_ERROR */ +#define GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 +#define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 +#define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +#endif + +#ifndef GL_ARB_shader_stencil_export +#endif + +#ifndef GL_ARB_base_instance +#endif + +#ifndef GL_ARB_shading_language_420pack +#endif + +#ifndef GL_ARB_transform_feedback_instanced +#endif + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_UNPACK_COMPRESSED_BLOCK_WIDTH 0x9127 +#define GL_UNPACK_COMPRESSED_BLOCK_HEIGHT 0x9128 +#define GL_UNPACK_COMPRESSED_BLOCK_DEPTH 0x9129 +#define GL_UNPACK_COMPRESSED_BLOCK_SIZE 0x912A +#define GL_PACK_COMPRESSED_BLOCK_WIDTH 0x912B +#define GL_PACK_COMPRESSED_BLOCK_HEIGHT 0x912C +#define GL_PACK_COMPRESSED_BLOCK_DEPTH 0x912D +#define GL_PACK_COMPRESSED_BLOCK_SIZE 0x912E +#endif + +#ifndef GL_ARB_conservative_depth +#endif + +#ifndef GL_ARB_internalformat_query +#define GL_NUM_SAMPLE_COUNTS 0x9380 +#endif + +#ifndef GL_ARB_map_buffer_alignment +#define GL_MIN_MAP_BUFFER_ALIGNMENT 0x90BC +#endif + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ATOMIC_COUNTER_BUFFER 0x92C0 +#define GL_ATOMIC_COUNTER_BUFFER_BINDING 0x92C1 +#define GL_ATOMIC_COUNTER_BUFFER_START 0x92C2 +#define GL_ATOMIC_COUNTER_BUFFER_SIZE 0x92C3 +#define GL_ATOMIC_COUNTER_BUFFER_DATA_SIZE 0x92C4 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTERS 0x92C5 +#define GL_ATOMIC_COUNTER_BUFFER_ACTIVE_ATOMIC_COUNTER_INDICES 0x92C6 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_VERTEX_SHADER 0x92C7 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_CONTROL_SHADER 0x92C8 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_TESS_EVALUATION_SHADER 0x92C9 +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_GEOMETRY_SHADER 0x92CA +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_FRAGMENT_SHADER 0x92CB +#define GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS 0x92CC +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS 0x92CD +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS 0x92CE +#define GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS 0x92CF +#define GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS 0x92D0 +#define GL_MAX_COMBINED_ATOMIC_COUNTER_BUFFERS 0x92D1 +#define GL_MAX_VERTEX_ATOMIC_COUNTERS 0x92D2 +#define GL_MAX_TESS_CONTROL_ATOMIC_COUNTERS 0x92D3 +#define GL_MAX_TESS_EVALUATION_ATOMIC_COUNTERS 0x92D4 +#define GL_MAX_GEOMETRY_ATOMIC_COUNTERS 0x92D5 +#define GL_MAX_FRAGMENT_ATOMIC_COUNTERS 0x92D6 +#define GL_MAX_COMBINED_ATOMIC_COUNTERS 0x92D7 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_SIZE 0x92D8 +#define GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS 0x92DC +#define GL_ACTIVE_ATOMIC_COUNTER_BUFFERS 0x92D9 +#define GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX 0x92DA +#define GL_UNSIGNED_INT_ATOMIC_COUNTER 0x92DB +#endif + +#ifndef GL_ARB_shader_image_load_store +#define GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT 0x00000001 +#define GL_ELEMENT_ARRAY_BARRIER_BIT 0x00000002 +#define GL_UNIFORM_BARRIER_BIT 0x00000004 +#define GL_TEXTURE_FETCH_BARRIER_BIT 0x00000008 +#define GL_SHADER_IMAGE_ACCESS_BARRIER_BIT 0x00000020 +#define GL_COMMAND_BARRIER_BIT 0x00000040 +#define GL_PIXEL_BUFFER_BARRIER_BIT 0x00000080 +#define GL_TEXTURE_UPDATE_BARRIER_BIT 0x00000100 +#define GL_BUFFER_UPDATE_BARRIER_BIT 0x00000200 +#define GL_FRAMEBUFFER_BARRIER_BIT 0x00000400 +#define GL_TRANSFORM_FEEDBACK_BARRIER_BIT 0x00000800 +#define GL_ATOMIC_COUNTER_BARRIER_BIT 0x00001000 +#define GL_ALL_BARRIER_BITS 0xFFFFFFFF +#define GL_MAX_IMAGE_UNITS 0x8F38 +#define GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS 0x8F39 +#define GL_IMAGE_BINDING_NAME 0x8F3A +#define GL_IMAGE_BINDING_LEVEL 0x8F3B +#define GL_IMAGE_BINDING_LAYERED 0x8F3C +#define GL_IMAGE_BINDING_LAYER 0x8F3D +#define GL_IMAGE_BINDING_ACCESS 0x8F3E +#define GL_IMAGE_1D 0x904C +#define GL_IMAGE_2D 0x904D +#define GL_IMAGE_3D 0x904E +#define GL_IMAGE_2D_RECT 0x904F +#define GL_IMAGE_CUBE 0x9050 +#define GL_IMAGE_BUFFER 0x9051 +#define GL_IMAGE_1D_ARRAY 0x9052 +#define GL_IMAGE_2D_ARRAY 0x9053 +#define GL_IMAGE_CUBE_MAP_ARRAY 0x9054 +#define GL_IMAGE_2D_MULTISAMPLE 0x9055 +#define GL_IMAGE_2D_MULTISAMPLE_ARRAY 0x9056 +#define GL_INT_IMAGE_1D 0x9057 +#define GL_INT_IMAGE_2D 0x9058 +#define GL_INT_IMAGE_3D 0x9059 +#define GL_INT_IMAGE_2D_RECT 0x905A +#define GL_INT_IMAGE_CUBE 0x905B +#define GL_INT_IMAGE_BUFFER 0x905C +#define GL_INT_IMAGE_1D_ARRAY 0x905D +#define GL_INT_IMAGE_2D_ARRAY 0x905E +#define GL_INT_IMAGE_CUBE_MAP_ARRAY 0x905F +#define GL_INT_IMAGE_2D_MULTISAMPLE 0x9060 +#define GL_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x9061 +#define GL_UNSIGNED_INT_IMAGE_1D 0x9062 +#define GL_UNSIGNED_INT_IMAGE_2D 0x9063 +#define GL_UNSIGNED_INT_IMAGE_3D 0x9064 +#define GL_UNSIGNED_INT_IMAGE_2D_RECT 0x9065 +#define GL_UNSIGNED_INT_IMAGE_CUBE 0x9066 +#define GL_UNSIGNED_INT_IMAGE_BUFFER 0x9067 +#define GL_UNSIGNED_INT_IMAGE_1D_ARRAY 0x9068 +#define GL_UNSIGNED_INT_IMAGE_2D_ARRAY 0x9069 +#define GL_UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY 0x906A +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE 0x906B +#define GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY 0x906C +#define GL_MAX_IMAGE_SAMPLES 0x906D +#define GL_IMAGE_BINDING_FORMAT 0x906E +#define GL_IMAGE_FORMAT_COMPATIBILITY_TYPE 0x90C7 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_SIZE 0x90C8 +#define GL_IMAGE_FORMAT_COMPATIBILITY_BY_CLASS 0x90C9 +#define GL_MAX_VERTEX_IMAGE_UNIFORMS 0x90CA +#define GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS 0x90CB +#define GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS 0x90CC +#define GL_MAX_GEOMETRY_IMAGE_UNIFORMS 0x90CD +#define GL_MAX_FRAGMENT_IMAGE_UNIFORMS 0x90CE +#define GL_MAX_COMBINED_IMAGE_UNIFORMS 0x90CF +#endif + +#ifndef GL_ARB_shading_language_packing +#endif + +#ifndef GL_ARB_texture_storage +#define GL_TEXTURE_IMMUTABLE_FORMAT 0x912F +#endif + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR 0x93B0 +#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR 0x93B1 +#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR 0x93B2 +#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR 0x93B3 +#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR 0x93B4 +#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR 0x93B5 +#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR 0x93B6 +#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR 0x93B7 +#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR 0x93B8 +#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR 0x93B9 +#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR 0x93BA +#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR 0x93BB +#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR 0x93BC +#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR 0x93BD +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR 0x93D0 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR 0x93D1 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR 0x93D2 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR 0x93D3 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR 0x93D4 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR 0x93D5 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR 0x93D6 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR 0x93D7 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR 0x93D8 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR 0x93D9 +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR 0x93DA +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR 0x93DB +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR 0x93DC +#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR 0x93DD +#endif + +#ifndef GL_KHR_debug +#define GL_DEBUG_OUTPUT_SYNCHRONOUS 0x8242 +#define GL_DEBUG_NEXT_LOGGED_MESSAGE_LENGTH 0x8243 +#define GL_DEBUG_CALLBACK_FUNCTION 0x8244 +#define GL_DEBUG_CALLBACK_USER_PARAM 0x8245 +#define GL_DEBUG_SOURCE_API 0x8246 +#define GL_DEBUG_SOURCE_WINDOW_SYSTEM 0x8247 +#define GL_DEBUG_SOURCE_SHADER_COMPILER 0x8248 +#define GL_DEBUG_SOURCE_THIRD_PARTY 0x8249 +#define GL_DEBUG_SOURCE_APPLICATION 0x824A +#define GL_DEBUG_SOURCE_OTHER 0x824B +#define GL_DEBUG_TYPE_ERROR 0x824C +#define GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR 0x824D +#define GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR 0x824E +#define GL_DEBUG_TYPE_PORTABILITY 0x824F +#define GL_DEBUG_TYPE_PERFORMANCE 0x8250 +#define GL_DEBUG_TYPE_OTHER 0x8251 +#define GL_DEBUG_TYPE_MARKER 0x8268 +#define GL_DEBUG_TYPE_PUSH_GROUP 0x8269 +#define GL_DEBUG_TYPE_POP_GROUP 0x826A +#define GL_DEBUG_SEVERITY_NOTIFICATION 0x826B +#define GL_MAX_DEBUG_GROUP_STACK_DEPTH 0x826C +#define GL_DEBUG_GROUP_STACK_DEPTH 0x826D +#define GL_BUFFER 0x82E0 +#define GL_SHADER 0x82E1 +#define GL_PROGRAM 0x82E2 +#define GL_QUERY 0x82E3 +#define GL_PROGRAM_PIPELINE 0x82E4 +#define GL_SAMPLER 0x82E6 +#define GL_DISPLAY_LIST 0x82E7 +/* DISPLAY_LIST used in compatibility profile only */ +#define GL_MAX_LABEL_LENGTH 0x82E8 +#define GL_MAX_DEBUG_MESSAGE_LENGTH 0x9143 +#define GL_MAX_DEBUG_LOGGED_MESSAGES 0x9144 +#define GL_DEBUG_LOGGED_MESSAGES 0x9145 +#define GL_DEBUG_SEVERITY_HIGH 0x9146 +#define GL_DEBUG_SEVERITY_MEDIUM 0x9147 +#define GL_DEBUG_SEVERITY_LOW 0x9148 +#define GL_DEBUG_OUTPUT 0x92E0 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +/* reuse GL_STACK_UNDERFLOW */ +/* reuse GL_STACK_OVERFLOW */ +#endif + +#ifndef GL_ARB_arrays_of_arrays +#endif + +#ifndef GL_ARB_clear_buffer_object +#endif + +#ifndef GL_ARB_compute_shader +#define GL_COMPUTE_SHADER 0x91B9 +#define GL_MAX_COMPUTE_UNIFORM_BLOCKS 0x91BB +#define GL_MAX_COMPUTE_TEXTURE_IMAGE_UNITS 0x91BC +#define GL_MAX_COMPUTE_IMAGE_UNIFORMS 0x91BD +#define GL_MAX_COMPUTE_SHARED_MEMORY_SIZE 0x8262 +#define GL_MAX_COMPUTE_UNIFORM_COMPONENTS 0x8263 +#define GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS 0x8264 +#define GL_MAX_COMPUTE_ATOMIC_COUNTERS 0x8265 +#define GL_MAX_COMBINED_COMPUTE_UNIFORM_COMPONENTS 0x8266 +#define GL_MAX_COMPUTE_LOCAL_INVOCATIONS 0x90EB +#define GL_MAX_COMPUTE_WORK_GROUP_COUNT 0x91BE +#define GL_MAX_COMPUTE_WORK_GROUP_SIZE 0x91BF +#define GL_COMPUTE_LOCAL_WORK_SIZE 0x8267 +#define GL_UNIFORM_BLOCK_REFERENCED_BY_COMPUTE_SHADER 0x90EC +#define GL_ATOMIC_COUNTER_BUFFER_REFERENCED_BY_COMPUTE_SHADER 0x90ED +#define GL_DISPATCH_INDIRECT_BUFFER 0x90EE +#define GL_DISPATCH_INDIRECT_BUFFER_BINDING 0x90EF +#define GL_COMPUTE_SHADER_BIT 0x00000020 +#endif + +#ifndef GL_ARB_copy_image +#endif + +#ifndef GL_ARB_texture_view +#define GL_TEXTURE_VIEW_MIN_LEVEL 0x82DB +#define GL_TEXTURE_VIEW_NUM_LEVELS 0x82DC +#define GL_TEXTURE_VIEW_MIN_LAYER 0x82DD +#define GL_TEXTURE_VIEW_NUM_LAYERS 0x82DE +#define GL_TEXTURE_IMMUTABLE_LEVELS 0x82DF +#endif + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_VERTEX_ATTRIB_BINDING 0x82D4 +#define GL_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D5 +#define GL_VERTEX_BINDING_DIVISOR 0x82D6 +#define GL_VERTEX_BINDING_OFFSET 0x82D7 +#define GL_VERTEX_BINDING_STRIDE 0x82D8 +#define GL_MAX_VERTEX_ATTRIB_RELATIVE_OFFSET 0x82D9 +#define GL_MAX_VERTEX_ATTRIB_BINDINGS 0x82DA +#endif + +#ifndef GL_ARB_robustness_isolation +#endif + +#ifndef GL_ARB_ES3_compatibility +#define GL_COMPRESSED_RGB8_ETC2 0x9274 +#define GL_COMPRESSED_SRGB8_ETC2 0x9275 +#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9276 +#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 0x9277 +#define GL_COMPRESSED_RGBA8_ETC2_EAC 0x9278 +#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC 0x9279 +#define GL_COMPRESSED_R11_EAC 0x9270 +#define GL_COMPRESSED_SIGNED_R11_EAC 0x9271 +#define GL_COMPRESSED_RG11_EAC 0x9272 +#define GL_COMPRESSED_SIGNED_RG11_EAC 0x9273 +#define GL_PRIMITIVE_RESTART_FIXED_INDEX 0x8D69 +#define GL_ANY_SAMPLES_PASSED_CONSERVATIVE 0x8D6A +#define GL_MAX_ELEMENT_INDEX 0x8D6B +#endif + +#ifndef GL_ARB_explicit_uniform_location +#define GL_MAX_UNIFORM_LOCATIONS 0x826E +#endif + +#ifndef GL_ARB_fragment_layer_viewport +#endif + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_FRAMEBUFFER_DEFAULT_WIDTH 0x9310 +#define GL_FRAMEBUFFER_DEFAULT_HEIGHT 0x9311 +#define GL_FRAMEBUFFER_DEFAULT_LAYERS 0x9312 +#define GL_FRAMEBUFFER_DEFAULT_SAMPLES 0x9313 +#define GL_FRAMEBUFFER_DEFAULT_FIXED_SAMPLE_LOCATIONS 0x9314 +#define GL_MAX_FRAMEBUFFER_WIDTH 0x9315 +#define GL_MAX_FRAMEBUFFER_HEIGHT 0x9316 +#define GL_MAX_FRAMEBUFFER_LAYERS 0x9317 +#define GL_MAX_FRAMEBUFFER_SAMPLES 0x9318 +#endif + +#ifndef GL_ARB_internalformat_query2 +/* reuse GL_IMAGE_FORMAT_COMPATIBILITY_TYPE */ +/* reuse GL_NUM_SAMPLE_COUNTS */ +/* reuse GL_RENDERBUFFER */ +/* reuse GL_SAMPLES */ +/* reuse GL_TEXTURE_1D */ +/* reuse GL_TEXTURE_1D_ARRAY */ +/* reuse GL_TEXTURE_2D */ +/* reuse GL_TEXTURE_2D_ARRAY */ +/* reuse GL_TEXTURE_3D */ +/* reuse GL_TEXTURE_CUBE_MAP */ +/* reuse GL_TEXTURE_CUBE_MAP_ARRAY */ +/* reuse GL_TEXTURE_RECTANGLE */ +/* reuse GL_TEXTURE_BUFFER */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE */ +/* reuse GL_TEXTURE_2D_MULTISAMPLE_ARRAY */ +/* reuse GL_TEXTURE_COMPRESSED */ +#define GL_INTERNALFORMAT_SUPPORTED 0x826F +#define GL_INTERNALFORMAT_PREFERRED 0x8270 +#define GL_INTERNALFORMAT_RED_SIZE 0x8271 +#define GL_INTERNALFORMAT_GREEN_SIZE 0x8272 +#define GL_INTERNALFORMAT_BLUE_SIZE 0x8273 +#define GL_INTERNALFORMAT_ALPHA_SIZE 0x8274 +#define GL_INTERNALFORMAT_DEPTH_SIZE 0x8275 +#define GL_INTERNALFORMAT_STENCIL_SIZE 0x8276 +#define GL_INTERNALFORMAT_SHARED_SIZE 0x8277 +#define GL_INTERNALFORMAT_RED_TYPE 0x8278 +#define GL_INTERNALFORMAT_GREEN_TYPE 0x8279 +#define GL_INTERNALFORMAT_BLUE_TYPE 0x827A +#define GL_INTERNALFORMAT_ALPHA_TYPE 0x827B +#define GL_INTERNALFORMAT_DEPTH_TYPE 0x827C +#define GL_INTERNALFORMAT_STENCIL_TYPE 0x827D +#define GL_MAX_WIDTH 0x827E +#define GL_MAX_HEIGHT 0x827F +#define GL_MAX_DEPTH 0x8280 +#define GL_MAX_LAYERS 0x8281 +#define GL_MAX_COMBINED_DIMENSIONS 0x8282 +#define GL_COLOR_COMPONENTS 0x8283 +#define GL_DEPTH_COMPONENTS 0x8284 +#define GL_STENCIL_COMPONENTS 0x8285 +#define GL_COLOR_RENDERABLE 0x8286 +#define GL_DEPTH_RENDERABLE 0x8287 +#define GL_STENCIL_RENDERABLE 0x8288 +#define GL_FRAMEBUFFER_RENDERABLE 0x8289 +#define GL_FRAMEBUFFER_RENDERABLE_LAYERED 0x828A +#define GL_FRAMEBUFFER_BLEND 0x828B +#define GL_READ_PIXELS 0x828C +#define GL_READ_PIXELS_FORMAT 0x828D +#define GL_READ_PIXELS_TYPE 0x828E +#define GL_TEXTURE_IMAGE_FORMAT 0x828F +#define GL_TEXTURE_IMAGE_TYPE 0x8290 +#define GL_GET_TEXTURE_IMAGE_FORMAT 0x8291 +#define GL_GET_TEXTURE_IMAGE_TYPE 0x8292 +#define GL_MIPMAP 0x8293 +#define GL_MANUAL_GENERATE_MIPMAP 0x8294 +#define GL_AUTO_GENERATE_MIPMAP 0x8295 +#define GL_COLOR_ENCODING 0x8296 +#define GL_SRGB_READ 0x8297 +#define GL_SRGB_WRITE 0x8298 +#define GL_SRGB_DECODE_ARB 0x8299 +#define GL_FILTER 0x829A +#define GL_VERTEX_TEXTURE 0x829B +#define GL_TESS_CONTROL_TEXTURE 0x829C +#define GL_TESS_EVALUATION_TEXTURE 0x829D +#define GL_GEOMETRY_TEXTURE 0x829E +#define GL_FRAGMENT_TEXTURE 0x829F +#define GL_COMPUTE_TEXTURE 0x82A0 +#define GL_TEXTURE_SHADOW 0x82A1 +#define GL_TEXTURE_GATHER 0x82A2 +#define GL_TEXTURE_GATHER_SHADOW 0x82A3 +#define GL_SHADER_IMAGE_LOAD 0x82A4 +#define GL_SHADER_IMAGE_STORE 0x82A5 +#define GL_SHADER_IMAGE_ATOMIC 0x82A6 +#define GL_IMAGE_TEXEL_SIZE 0x82A7 +#define GL_IMAGE_COMPATIBILITY_CLASS 0x82A8 +#define GL_IMAGE_PIXEL_FORMAT 0x82A9 +#define GL_IMAGE_PIXEL_TYPE 0x82AA +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_TEST 0x82AC +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_TEST 0x82AD +#define GL_SIMULTANEOUS_TEXTURE_AND_DEPTH_WRITE 0x82AE +#define GL_SIMULTANEOUS_TEXTURE_AND_STENCIL_WRITE 0x82AF +#define GL_TEXTURE_COMPRESSED_BLOCK_WIDTH 0x82B1 +#define GL_TEXTURE_COMPRESSED_BLOCK_HEIGHT 0x82B2 +#define GL_TEXTURE_COMPRESSED_BLOCK_SIZE 0x82B3 +#define GL_CLEAR_BUFFER 0x82B4 +#define GL_TEXTURE_VIEW 0x82B5 +#define GL_VIEW_COMPATIBILITY_CLASS 0x82B6 +#define GL_FULL_SUPPORT 0x82B7 +#define GL_CAVEAT_SUPPORT 0x82B8 +#define GL_IMAGE_CLASS_4_X_32 0x82B9 +#define GL_IMAGE_CLASS_2_X_32 0x82BA +#define GL_IMAGE_CLASS_1_X_32 0x82BB +#define GL_IMAGE_CLASS_4_X_16 0x82BC +#define GL_IMAGE_CLASS_2_X_16 0x82BD +#define GL_IMAGE_CLASS_1_X_16 0x82BE +#define GL_IMAGE_CLASS_4_X_8 0x82BF +#define GL_IMAGE_CLASS_2_X_8 0x82C0 +#define GL_IMAGE_CLASS_1_X_8 0x82C1 +#define GL_IMAGE_CLASS_11_11_10 0x82C2 +#define GL_IMAGE_CLASS_10_10_10_2 0x82C3 +#define GL_VIEW_CLASS_128_BITS 0x82C4 +#define GL_VIEW_CLASS_96_BITS 0x82C5 +#define GL_VIEW_CLASS_64_BITS 0x82C6 +#define GL_VIEW_CLASS_48_BITS 0x82C7 +#define GL_VIEW_CLASS_32_BITS 0x82C8 +#define GL_VIEW_CLASS_24_BITS 0x82C9 +#define GL_VIEW_CLASS_16_BITS 0x82CA +#define GL_VIEW_CLASS_8_BITS 0x82CB +#define GL_VIEW_CLASS_S3TC_DXT1_RGB 0x82CC +#define GL_VIEW_CLASS_S3TC_DXT1_RGBA 0x82CD +#define GL_VIEW_CLASS_S3TC_DXT3_RGBA 0x82CE +#define GL_VIEW_CLASS_S3TC_DXT5_RGBA 0x82CF +#define GL_VIEW_CLASS_RGTC1_RED 0x82D0 +#define GL_VIEW_CLASS_RGTC2_RG 0x82D1 +#define GL_VIEW_CLASS_BPTC_UNORM 0x82D2 +#define GL_VIEW_CLASS_BPTC_FLOAT 0x82D3 +#endif + +#ifndef GL_ARB_invalidate_subdata +#endif + +#ifndef GL_ARB_multi_draw_indirect +#endif + +#ifndef GL_ARB_program_interface_query +#define GL_UNIFORM 0x92E1 +#define GL_UNIFORM_BLOCK 0x92E2 +#define GL_PROGRAM_INPUT 0x92E3 +#define GL_PROGRAM_OUTPUT 0x92E4 +#define GL_BUFFER_VARIABLE 0x92E5 +#define GL_SHADER_STORAGE_BLOCK 0x92E6 +/* reuse GL_ATOMIC_COUNTER_BUFFER */ +#define GL_VERTEX_SUBROUTINE 0x92E8 +#define GL_TESS_CONTROL_SUBROUTINE 0x92E9 +#define GL_TESS_EVALUATION_SUBROUTINE 0x92EA +#define GL_GEOMETRY_SUBROUTINE 0x92EB +#define GL_FRAGMENT_SUBROUTINE 0x92EC +#define GL_COMPUTE_SUBROUTINE 0x92ED +#define GL_VERTEX_SUBROUTINE_UNIFORM 0x92EE +#define GL_TESS_CONTROL_SUBROUTINE_UNIFORM 0x92EF +#define GL_TESS_EVALUATION_SUBROUTINE_UNIFORM 0x92F0 +#define GL_GEOMETRY_SUBROUTINE_UNIFORM 0x92F1 +#define GL_FRAGMENT_SUBROUTINE_UNIFORM 0x92F2 +#define GL_COMPUTE_SUBROUTINE_UNIFORM 0x92F3 +#define GL_TRANSFORM_FEEDBACK_VARYING 0x92F4 +#define GL_ACTIVE_RESOURCES 0x92F5 +#define GL_MAX_NAME_LENGTH 0x92F6 +#define GL_MAX_NUM_ACTIVE_VARIABLES 0x92F7 +#define GL_MAX_NUM_COMPATIBLE_SUBROUTINES 0x92F8 +#define GL_NAME_LENGTH 0x92F9 +#define GL_TYPE 0x92FA +#define GL_ARRAY_SIZE 0x92FB +#define GL_OFFSET 0x92FC +#define GL_BLOCK_INDEX 0x92FD +#define GL_ARRAY_STRIDE 0x92FE +#define GL_MATRIX_STRIDE 0x92FF +#define GL_IS_ROW_MAJOR 0x9300 +#define GL_ATOMIC_COUNTER_BUFFER_INDEX 0x9301 +#define GL_BUFFER_BINDING 0x9302 +#define GL_BUFFER_DATA_SIZE 0x9303 +#define GL_NUM_ACTIVE_VARIABLES 0x9304 +#define GL_ACTIVE_VARIABLES 0x9305 +#define GL_REFERENCED_BY_VERTEX_SHADER 0x9306 +#define GL_REFERENCED_BY_TESS_CONTROL_SHADER 0x9307 +#define GL_REFERENCED_BY_TESS_EVALUATION_SHADER 0x9308 +#define GL_REFERENCED_BY_GEOMETRY_SHADER 0x9309 +#define GL_REFERENCED_BY_FRAGMENT_SHADER 0x930A +#define GL_REFERENCED_BY_COMPUTE_SHADER 0x930B +#define GL_TOP_LEVEL_ARRAY_SIZE 0x930C +#define GL_TOP_LEVEL_ARRAY_STRIDE 0x930D +#define GL_LOCATION 0x930E +#define GL_LOCATION_INDEX 0x930F +#define GL_IS_PER_PATCH 0x92E7 +/* reuse GL_NUM_COMPATIBLE_SUBROUTINES */ +/* reuse GL_COMPATIBLE_SUBROUTINES */ +#endif + +#ifndef GL_ARB_robust_buffer_access_behavior +#endif + +#ifndef GL_ARB_shader_image_size +#endif + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_SHADER_STORAGE_BUFFER 0x90D2 +#define GL_SHADER_STORAGE_BUFFER_BINDING 0x90D3 +#define GL_SHADER_STORAGE_BUFFER_START 0x90D4 +#define GL_SHADER_STORAGE_BUFFER_SIZE 0x90D5 +#define GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS 0x90D6 +#define GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS 0x90D7 +#define GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS 0x90D8 +#define GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS 0x90D9 +#define GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS 0x90DA +#define GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS 0x90DB +#define GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS 0x90DC +#define GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS 0x90DD +#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE +#define GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT 0x90DF +#define GL_SHADER_STORAGE_BARRIER_BIT 0x2000 +#define GL_MAX_COMBINED_SHADER_OUTPUT_RESOURCES GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS +/* reuse GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS */ +#endif + +#ifndef GL_ARB_stencil_texturing +#define GL_DEPTH_STENCIL_TEXTURE_MODE 0x90EA +#endif + +#ifndef GL_ARB_texture_buffer_range +#define GL_TEXTURE_BUFFER_OFFSET 0x919D +#define GL_TEXTURE_BUFFER_SIZE 0x919E +#define GL_TEXTURE_BUFFER_OFFSET_ALIGNMENT 0x919F +#endif + +#ifndef GL_ARB_texture_query_levels +#endif + +#ifndef GL_ARB_texture_storage_multisample +#endif + + +/*************************************************************/ + +#include +#ifndef GL_VERSION_2_0 +/* GL type for program/shader text */ +typedef char GLchar; +#endif + +#ifndef GL_VERSION_1_5 +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptr; +typedef ptrdiff_t GLsizeiptr; +#endif + +#ifndef GL_ARB_vertex_buffer_object +/* GL types for handling large vertex buffer objects */ +typedef ptrdiff_t GLintptrARB; +typedef ptrdiff_t GLsizeiptrARB; +#endif + +#ifndef GL_ARB_shader_objects +/* GL types for program/shader text and shader object handles */ +typedef char GLcharARB; +typedef unsigned int GLhandleARB; +#endif + +/* GL type for "half" precision (s10e5) float data in host memory */ +#ifndef GL_ARB_half_float_pixel +typedef unsigned short GLhalfARB; +#endif + +#ifndef GL_NV_half_float +typedef unsigned short GLhalfNV; +#endif + +#ifndef GLEXT_64_TYPES_DEFINED +/* This code block is duplicated in glxext.h, so must be protected */ +#define GLEXT_64_TYPES_DEFINED +/* Define int32_t, int64_t, and uint64_t types for UST/MSC */ +/* (as used in the GL_EXT_timer_query extension). */ +#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L +#include +#elif defined(__sun__) || defined(__digital__) +#include +#if defined(__STDC__) +#if defined(__arch64__) || defined(_LP64) +typedef long int int64_t; +typedef unsigned long int uint64_t; +#else +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#endif /* __arch64__ */ +#endif /* __STDC__ */ +#elif defined( __VMS ) || defined(__sgi) +#include +#elif defined(__SCO__) || defined(__USLC__) +#include +#elif defined(__UNIXOS2__) || defined(__SOL64__) +typedef long int int32_t; +typedef long long int int64_t; +typedef unsigned long long int uint64_t; +#elif defined(_WIN32) && defined(__GNUC__) +#include +#elif defined(_WIN32) +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +#else +/* Fallback if nothing above works */ +#include +#endif +#endif + +#ifndef GL_EXT_timer_query +typedef int64_t GLint64EXT; +typedef uint64_t GLuint64EXT; +#endif + +#ifndef GL_ARB_sync +typedef int64_t GLint64; +typedef uint64_t GLuint64; +typedef struct __GLsync *GLsync; +#endif + +#ifndef GL_ARB_cl_event +/* These incomplete types let us declare types compatible with OpenCL's cl_context and cl_event */ +struct _cl_context; +struct _cl_event; +#endif + +#ifndef GL_ARB_debug_output +typedef void (APIENTRY *GLDEBUGPROCARB)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + +#ifndef GL_AMD_debug_output +typedef void (APIENTRY *GLDEBUGPROCAMD)(GLuint id,GLenum category,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + +#ifndef GL_KHR_debug +typedef void (APIENTRY *GLDEBUGPROC)(GLenum source,GLenum type,GLuint id,GLenum severity,GLsizei length,const GLchar *message,GLvoid *userParam); +#endif + +#ifndef GL_NV_vdpau_interop +typedef GLintptr GLvdpauSurfaceNV; +#endif + +#ifndef GL_VERSION_1_0 +#define GL_VERSION_1_0 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glCullFace (GLenum mode); +GLAPI void APIENTRY glFrontFace (GLenum mode); +GLAPI void APIENTRY glHint (GLenum target, GLenum mode); +GLAPI void APIENTRY glLineWidth (GLfloat width); +GLAPI void APIENTRY glPointSize (GLfloat size); +GLAPI void APIENTRY glPolygonMode (GLenum face, GLenum mode); +GLAPI void APIENTRY glScissor (GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param); +GLAPI void APIENTRY glTexParameterfv (GLenum target, GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glTexParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glTexParameteriv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexImage1D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glDrawBuffer (GLenum mode); +GLAPI void APIENTRY glClear (GLbitfield mask); +GLAPI void APIENTRY glClearColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glClearStencil (GLint s); +GLAPI void APIENTRY glClearDepth (GLdouble depth); +GLAPI void APIENTRY glStencilMask (GLuint mask); +GLAPI void APIENTRY glColorMask (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +GLAPI void APIENTRY glDepthMask (GLboolean flag); +GLAPI void APIENTRY glDisable (GLenum cap); +GLAPI void APIENTRY glEnable (GLenum cap); +GLAPI void APIENTRY glFinish (void); +GLAPI void APIENTRY glFlush (void); +GLAPI void APIENTRY glBlendFunc (GLenum sfactor, GLenum dfactor); +GLAPI void APIENTRY glLogicOp (GLenum opcode); +GLAPI void APIENTRY glStencilFunc (GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilOp (GLenum fail, GLenum zfail, GLenum zpass); +GLAPI void APIENTRY glDepthFunc (GLenum func); +GLAPI void APIENTRY glPixelStoref (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPixelStorei (GLenum pname, GLint param); +GLAPI void APIENTRY glReadBuffer (GLenum mode); +GLAPI void APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +GLAPI void APIENTRY glGetBooleanv (GLenum pname, GLboolean *params); +GLAPI void APIENTRY glGetDoublev (GLenum pname, GLdouble *params); +GLAPI GLenum APIENTRY glGetError (void); +GLAPI void APIENTRY glGetFloatv (GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetIntegerv (GLenum pname, GLint *params); +GLAPI const GLubyte * APIENTRY glGetString (GLenum name); +GLAPI void APIENTRY glGetTexImage (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +GLAPI void APIENTRY glGetTexParameterfv (GLenum target, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexLevelParameterfv (GLenum target, GLint level, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetTexLevelParameteriv (GLenum target, GLint level, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsEnabled (GLenum cap); +GLAPI void APIENTRY glDepthRange (GLdouble near, GLdouble far); +GLAPI void APIENTRY glViewport (GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCULLFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLFRONTFACEPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLHINTPROC) (GLenum target, GLenum mode); +typedef void (APIENTRYP PFNGLLINEWIDTHPROC) (GLfloat width); +typedef void (APIENTRYP PFNGLPOINTSIZEPROC) (GLfloat size); +typedef void (APIENTRYP PFNGLPOLYGONMODEPROC) (GLenum face, GLenum mode); +typedef void (APIENTRYP PFNGLSCISSORPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLTEXPARAMETERFVPROC) (GLenum target, GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXIMAGE1DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXIMAGE2DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLDRAWBUFFERPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLCLEARPROC) (GLbitfield mask); +typedef void (APIENTRYP PFNGLCLEARCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLCLEARSTENCILPROC) (GLint s); +typedef void (APIENTRYP PFNGLCLEARDEPTHPROC) (GLdouble depth); +typedef void (APIENTRYP PFNGLSTENCILMASKPROC) (GLuint mask); +typedef void (APIENTRYP PFNGLCOLORMASKPROC) (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha); +typedef void (APIENTRYP PFNGLDEPTHMASKPROC) (GLboolean flag); +typedef void (APIENTRYP PFNGLDISABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLENABLEPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLFINISHPROC) (void); +typedef void (APIENTRYP PFNGLFLUSHPROC) (void); +typedef void (APIENTRYP PFNGLBLENDFUNCPROC) (GLenum sfactor, GLenum dfactor); +typedef void (APIENTRYP PFNGLLOGICOPPROC) (GLenum opcode); +typedef void (APIENTRYP PFNGLSTENCILFUNCPROC) (GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILOPPROC) (GLenum fail, GLenum zfail, GLenum zpass); +typedef void (APIENTRYP PFNGLDEPTHFUNCPROC) (GLenum func); +typedef void (APIENTRYP PFNGLPIXELSTOREFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPIXELSTOREIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLREADBUFFERPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLREADPIXELSPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid *pixels); +typedef void (APIENTRYP PFNGLGETBOOLEANVPROC) (GLenum pname, GLboolean *params); +typedef void (APIENTRYP PFNGLGETDOUBLEVPROC) (GLenum pname, GLdouble *params); +typedef GLenum (APIENTRYP PFNGLGETERRORPROC) (void); +typedef void (APIENTRYP PFNGLGETFLOATVPROC) (GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETINTEGERVPROC) (GLenum pname, GLint *params); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGPROC) (GLenum name); +typedef void (APIENTRYP PFNGLGETTEXIMAGEPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERFVPROC) (GLenum target, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERFVPROC) (GLenum target, GLint level, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETTEXLEVELPARAMETERIVPROC) (GLenum target, GLint level, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISENABLEDPROC) (GLenum cap); +typedef void (APIENTRYP PFNGLDEPTHRANGEPROC) (GLdouble near, GLdouble far); +typedef void (APIENTRYP PFNGLVIEWPORTPROC) (GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_VERSION_1_1 +#define GL_VERSION_1_1 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count); +GLAPI void APIENTRY glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +GLAPI void APIENTRY glGetPointerv (GLenum pname, GLvoid* *params); +GLAPI void APIENTRY glPolygonOffset (GLfloat factor, GLfloat units); +GLAPI void APIENTRY glCopyTexImage1D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +GLAPI void APIENTRY glCopyTexImage2D (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +GLAPI void APIENTRY glCopyTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +GLAPI void APIENTRY glCopyTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glBindTexture (GLenum target, GLuint texture); +GLAPI void APIENTRY glDeleteTextures (GLsizei n, const GLuint *textures); +GLAPI void APIENTRY glGenTextures (GLsizei n, GLuint *textures); +GLAPI GLboolean APIENTRY glIsTexture (GLuint texture); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSPROC) (GLenum mode, GLint first, GLsizei count); +typedef void (APIENTRYP PFNGLDRAWELEMENTSPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (APIENTRYP PFNGLGETPOINTERVPROC) (GLenum pname, GLvoid* *params); +typedef void (APIENTRYP PFNGLPOLYGONOFFSETPROC) (GLfloat factor, GLfloat units); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLint x, GLint y, GLsizei width, GLsizei height, GLint border); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLint x, GLint y, GLsizei width); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLBINDTEXTUREPROC) (GLenum target, GLuint texture); +typedef void (APIENTRYP PFNGLDELETETEXTURESPROC) (GLsizei n, const GLuint *textures); +typedef void (APIENTRYP PFNGLGENTEXTURESPROC) (GLsizei n, GLuint *textures); +typedef GLboolean (APIENTRYP PFNGLISTEXTUREPROC) (GLuint texture); +#endif + +#ifndef GL_VERSION_1_2 +#define GL_VERSION_1_2 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBlendColor (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +GLAPI void APIENTRY glBlendEquation (GLenum mode); +GLAPI void APIENTRY glDrawRangeElements (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +GLAPI void APIENTRY glTexImage3D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +GLAPI void APIENTRY glCopyTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDCOLORPROC) (GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha); +typedef void (APIENTRYP PFNGLBLENDEQUATIONPROC) (GLenum mode); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); +typedef void (APIENTRYP PFNGLTEXIMAGE3DPROC) (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLenum type, const GLvoid *pixels); +typedef void (APIENTRYP PFNGLCOPYTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_VERSION_1_3 +#define GL_VERSION_1_3 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glActiveTexture (GLenum texture); +GLAPI void APIENTRY glSampleCoverage (GLfloat value, GLboolean invert); +GLAPI void APIENTRY glCompressedTexImage3D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage2D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexImage1D (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage3D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage2D (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glCompressedTexSubImage1D (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +GLAPI void APIENTRY glGetCompressedTexImage (GLenum target, GLint level, GLvoid *img); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef void (APIENTRYP PFNGLSAMPLECOVERAGEPROC) (GLfloat value, GLboolean invert); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE3DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE1DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) (GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, GLsizei imageSize, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img); +#endif + +#ifndef GL_VERSION_1_4 +#define GL_VERSION_1_4 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBlendFuncSeparate (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +GLAPI void APIENTRY glMultiDrawArrays (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +GLAPI void APIENTRY glMultiDrawElements (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount); +GLAPI void APIENTRY glPointParameterf (GLenum pname, GLfloat param); +GLAPI void APIENTRY glPointParameterfv (GLenum pname, const GLfloat *params); +GLAPI void APIENTRY glPointParameteri (GLenum pname, GLint param); +GLAPI void APIENTRY glPointParameteriv (GLenum pname, const GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEPROC) (GLenum sfactorRGB, GLenum dfactorRGB, GLenum sfactorAlpha, GLenum dfactorAlpha); +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSPROC) (GLenum mode, const GLint *first, const GLsizei *count, GLsizei drawcount); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFPROC) (GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERFVPROC) (GLenum pname, const GLfloat *params); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIPROC) (GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLPOINTPARAMETERIVPROC) (GLenum pname, const GLint *params); +#endif + +#ifndef GL_VERSION_1_5 +#define GL_VERSION_1_5 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGenQueries (GLsizei n, GLuint *ids); +GLAPI void APIENTRY glDeleteQueries (GLsizei n, const GLuint *ids); +GLAPI GLboolean APIENTRY glIsQuery (GLuint id); +GLAPI void APIENTRY glBeginQuery (GLenum target, GLuint id); +GLAPI void APIENTRY glEndQuery (GLenum target); +GLAPI void APIENTRY glGetQueryiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectiv (GLuint id, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetQueryObjectuiv (GLuint id, GLenum pname, GLuint *params); +GLAPI void APIENTRY glBindBuffer (GLenum target, GLuint buffer); +GLAPI void APIENTRY glDeleteBuffers (GLsizei n, const GLuint *buffers); +GLAPI void APIENTRY glGenBuffers (GLsizei n, GLuint *buffers); +GLAPI GLboolean APIENTRY glIsBuffer (GLuint buffer); +GLAPI void APIENTRY glBufferData (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +GLAPI void APIENTRY glBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +GLAPI void APIENTRY glGetBufferSubData (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +GLAPI GLvoid* APIENTRY glMapBuffer (GLenum target, GLenum access); +GLAPI GLboolean APIENTRY glUnmapBuffer (GLenum target); +GLAPI void APIENTRY glGetBufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetBufferPointerv (GLenum target, GLenum pname, GLvoid* *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENQUERIESPROC) (GLsizei n, GLuint *ids); +typedef void (APIENTRYP PFNGLDELETEQUERIESPROC) (GLsizei n, const GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISQUERYPROC) (GLuint id); +typedef void (APIENTRYP PFNGLBEGINQUERYPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum target, GLuint buffer); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers); +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef GLboolean (APIENTRYP PFNGLISBUFFERPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data); +typedef void (APIENTRYP PFNGLGETBUFFERSUBDATAPROC) (GLenum target, GLintptr offset, GLsizeiptr size, GLvoid *data); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum target, GLenum access); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum target); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETBUFFERPOINTERVPROC) (GLenum target, GLenum pname, GLvoid* *params); +#endif + +#ifndef GL_VERSION_2_0 +#define GL_VERSION_2_0 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBlendEquationSeparate (GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glDrawBuffers (GLsizei n, const GLenum *bufs); +GLAPI void APIENTRY glStencilOpSeparate (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +GLAPI void APIENTRY glStencilFuncSeparate (GLenum face, GLenum func, GLint ref, GLuint mask); +GLAPI void APIENTRY glStencilMaskSeparate (GLenum face, GLuint mask); +GLAPI void APIENTRY glAttachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glBindAttribLocation (GLuint program, GLuint index, const GLchar *name); +GLAPI void APIENTRY glCompileShader (GLuint shader); +GLAPI GLuint APIENTRY glCreateProgram (void); +GLAPI GLuint APIENTRY glCreateShader (GLenum type); +GLAPI void APIENTRY glDeleteProgram (GLuint program); +GLAPI void APIENTRY glDeleteShader (GLuint shader); +GLAPI void APIENTRY glDetachShader (GLuint program, GLuint shader); +GLAPI void APIENTRY glDisableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glEnableVertexAttribArray (GLuint index); +GLAPI void APIENTRY glGetActiveAttrib (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetActiveUniform (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glGetAttachedShaders (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj); +GLAPI GLint APIENTRY glGetAttribLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetProgramInfoLog (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetShaderInfoLog (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +GLAPI void APIENTRY glGetShaderSource (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +GLAPI GLint APIENTRY glGetUniformLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glGetUniformfv (GLuint program, GLint location, GLfloat *params); +GLAPI void APIENTRY glGetUniformiv (GLuint program, GLint location, GLint *params); +GLAPI void APIENTRY glGetVertexAttribdv (GLuint index, GLenum pname, GLdouble *params); +GLAPI void APIENTRY glGetVertexAttribfv (GLuint index, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetVertexAttribiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribPointerv (GLuint index, GLenum pname, GLvoid* *pointer); +GLAPI GLboolean APIENTRY glIsProgram (GLuint program); +GLAPI GLboolean APIENTRY glIsShader (GLuint shader); +GLAPI void APIENTRY glLinkProgram (GLuint program); +GLAPI void APIENTRY glShaderSource (GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length); +GLAPI void APIENTRY glUseProgram (GLuint program); +GLAPI void APIENTRY glUniform1f (GLint location, GLfloat v0); +GLAPI void APIENTRY glUniform2f (GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glUniform3f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glUniform4f (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glUniform1i (GLint location, GLint v0); +GLAPI void APIENTRY glUniform2i (GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glUniform3i (GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glUniform4i (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glUniform1fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform2fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform3fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform4fv (GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glUniform1iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform2iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform3iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniform4iv (GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glUniformMatrix2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glValidateProgram (GLuint program); +GLAPI void APIENTRY glVertexAttrib1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttrib1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib1f (GLuint index, GLfloat x); +GLAPI void APIENTRY glVertexAttrib1fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib1s (GLuint index, GLshort x); +GLAPI void APIENTRY glVertexAttrib1sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttrib2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib2f (GLuint index, GLfloat x, GLfloat y); +GLAPI void APIENTRY glVertexAttrib2fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib2s (GLuint index, GLshort x, GLshort y); +GLAPI void APIENTRY glVertexAttrib2sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttrib3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib3f (GLuint index, GLfloat x, GLfloat y, GLfloat z); +GLAPI void APIENTRY glVertexAttrib3fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib3s (GLuint index, GLshort x, GLshort y, GLshort z); +GLAPI void APIENTRY glVertexAttrib3sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nbv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4Niv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4Nsv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4Nub (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +GLAPI void APIENTRY glVertexAttrib4Nubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4Nuiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4Nusv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttrib4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttrib4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttrib4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttrib4f (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +GLAPI void APIENTRY glVertexAttrib4fv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glVertexAttrib4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttrib4s (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +GLAPI void APIENTRY glVertexAttrib4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttrib4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttrib4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttrib4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glVertexAttribPointer (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEPROC) (GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLDRAWBUFFERSPROC) (GLsizei n, const GLenum *bufs); +typedef void (APIENTRYP PFNGLSTENCILOPSEPARATEPROC) (GLenum face, GLenum sfail, GLenum dpfail, GLenum dppass); +typedef void (APIENTRYP PFNGLSTENCILFUNCSEPARATEPROC) (GLenum face, GLenum func, GLint ref, GLuint mask); +typedef void (APIENTRYP PFNGLSTENCILMASKSEPARATEPROC) (GLenum face, GLuint mask); +typedef void (APIENTRYP PFNGLATTACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLBINDATTRIBLOCATIONPROC) (GLuint program, GLuint index, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERPROC) (GLuint shader); +typedef GLuint (APIENTRYP PFNGLCREATEPROGRAMPROC) (void); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROC) (GLenum type); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLDELETESHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLDETACHSHADERPROC) (GLuint program, GLuint shader); +typedef void (APIENTRYP PFNGLDISABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLENABLEVERTEXATTRIBARRAYPROC) (GLuint index); +typedef void (APIENTRYP PFNGLGETACTIVEATTRIBPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLint *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLGETATTACHEDSHADERSPROC) (GLuint program, GLsizei maxCount, GLsizei *count, GLuint *obj); +typedef GLint (APIENTRYP PFNGLGETATTRIBLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMIVPROC) (GLuint program, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERIVPROC) (GLuint shader, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSHADERINFOLOGPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +typedef void (APIENTRYP PFNGLGETSHADERSOURCEPROC) (GLuint shader, GLsizei bufSize, GLsizei *length, GLchar *source); +typedef GLint (APIENTRYP PFNGLGETUNIFORMLOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLGETUNIFORMFVPROC) (GLuint program, GLint location, GLfloat *params); +typedef void (APIENTRYP PFNGLGETUNIFORMIVPROC) (GLuint program, GLint location, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBDVPROC) (GLuint index, GLenum pname, GLdouble *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBFVPROC) (GLuint index, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBPOINTERVPROC) (GLuint index, GLenum pname, GLvoid* *pointer); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPROC) (GLuint program); +typedef GLboolean (APIENTRYP PFNGLISSHADERPROC) (GLuint shader); +typedef void (APIENTRYP PFNGLLINKPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLSHADERSOURCEPROC) (GLuint shader, GLsizei count, const GLchar* const *string, const GLint *length); +typedef void (APIENTRYP PFNGLUSEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLUNIFORM1FPROC) (GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLUNIFORM2FPROC) (GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLUNIFORM3FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLUNIFORM4FPROC) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLUNIFORM1IPROC) (GLint location, GLint v0); +typedef void (APIENTRYP PFNGLUNIFORM2IPROC) (GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLUNIFORM3IPROC) (GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLUNIFORM4IPROC) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLUNIFORM1FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM2FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM3FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM4FVPROC) (GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORM1IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM2IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM3IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORM4IVPROC) (GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPROC) (GLuint program); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FPROC) (GLuint index, GLfloat x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SPROC) (GLuint index, GLshort x); +typedef void (APIENTRYP PFNGLVERTEXATTRIB1SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FPROC) (GLuint index, GLfloat x, GLfloat y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SPROC) (GLuint index, GLshort x, GLshort y); +typedef void (APIENTRYP PFNGLVERTEXATTRIB2SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SPROC) (GLuint index, GLshort x, GLshort y, GLshort z); +typedef void (APIENTRYP PFNGLVERTEXATTRIB3SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NBVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NIVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NSVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBPROC) (GLuint index, GLubyte x, GLubyte y, GLubyte z, GLubyte w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4NUSVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4FVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SPROC) (GLuint index, GLshort x, GLshort y, GLshort z, GLshort w); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIB4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBPOINTERPROC) (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer); +#endif + +#ifndef GL_VERSION_2_1 +#define GL_VERSION_2_1 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glUniformMatrix2x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix2x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x2fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix3x4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glUniformMatrix4x3fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3FVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +#endif + +#ifndef GL_VERSION_3_0 +#define GL_VERSION_3_0 1 +/* OpenGL 3.0 also reuses entry points from these extensions: */ +/* ARB_framebuffer_object */ +/* ARB_map_buffer_range */ +/* ARB_vertex_array_object */ +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glColorMaski (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +GLAPI void APIENTRY glGetBooleani_v (GLenum target, GLuint index, GLboolean *data); +GLAPI void APIENTRY glGetIntegeri_v (GLenum target, GLuint index, GLint *data); +GLAPI void APIENTRY glEnablei (GLenum target, GLuint index); +GLAPI void APIENTRY glDisablei (GLenum target, GLuint index); +GLAPI GLboolean APIENTRY glIsEnabledi (GLenum target, GLuint index); +GLAPI void APIENTRY glBeginTransformFeedback (GLenum primitiveMode); +GLAPI void APIENTRY glEndTransformFeedback (void); +GLAPI void APIENTRY glBindBufferRange (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glBindBufferBase (GLenum target, GLuint index, GLuint buffer); +GLAPI void APIENTRY glTransformFeedbackVaryings (GLuint program, GLsizei count, const GLchar* const *varyings, GLenum bufferMode); +GLAPI void APIENTRY glGetTransformFeedbackVarying (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +GLAPI void APIENTRY glClampColor (GLenum target, GLenum clamp); +GLAPI void APIENTRY glBeginConditionalRender (GLuint id, GLenum mode); +GLAPI void APIENTRY glEndConditionalRender (void); +GLAPI void APIENTRY glVertexAttribIPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribIiv (GLuint index, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetVertexAttribIuiv (GLuint index, GLenum pname, GLuint *params); +GLAPI void APIENTRY glVertexAttribI1i (GLuint index, GLint x); +GLAPI void APIENTRY glVertexAttribI2i (GLuint index, GLint x, GLint y); +GLAPI void APIENTRY glVertexAttribI3i (GLuint index, GLint x, GLint y, GLint z); +GLAPI void APIENTRY glVertexAttribI4i (GLuint index, GLint x, GLint y, GLint z, GLint w); +GLAPI void APIENTRY glVertexAttribI1ui (GLuint index, GLuint x); +GLAPI void APIENTRY glVertexAttribI2ui (GLuint index, GLuint x, GLuint y); +GLAPI void APIENTRY glVertexAttribI3ui (GLuint index, GLuint x, GLuint y, GLuint z); +GLAPI void APIENTRY glVertexAttribI4ui (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +GLAPI void APIENTRY glVertexAttribI1iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI2iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI3iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI4iv (GLuint index, const GLint *v); +GLAPI void APIENTRY glVertexAttribI1uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI2uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI3uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4uiv (GLuint index, const GLuint *v); +GLAPI void APIENTRY glVertexAttribI4bv (GLuint index, const GLbyte *v); +GLAPI void APIENTRY glVertexAttribI4sv (GLuint index, const GLshort *v); +GLAPI void APIENTRY glVertexAttribI4ubv (GLuint index, const GLubyte *v); +GLAPI void APIENTRY glVertexAttribI4usv (GLuint index, const GLushort *v); +GLAPI void APIENTRY glGetUniformuiv (GLuint program, GLint location, GLuint *params); +GLAPI void APIENTRY glBindFragDataLocation (GLuint program, GLuint color, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataLocation (GLuint program, const GLchar *name); +GLAPI void APIENTRY glUniform1ui (GLint location, GLuint v0); +GLAPI void APIENTRY glUniform2ui (GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glUniform3ui (GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glUniform4ui (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glUniform1uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform2uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform3uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glUniform4uiv (GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glTexParameterIiv (GLenum target, GLenum pname, const GLint *params); +GLAPI void APIENTRY glTexParameterIuiv (GLenum target, GLenum pname, const GLuint *params); +GLAPI void APIENTRY glGetTexParameterIiv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetTexParameterIuiv (GLenum target, GLenum pname, GLuint *params); +GLAPI void APIENTRY glClearBufferiv (GLenum buffer, GLint drawbuffer, const GLint *value); +GLAPI void APIENTRY glClearBufferuiv (GLenum buffer, GLint drawbuffer, const GLuint *value); +GLAPI void APIENTRY glClearBufferfv (GLenum buffer, GLint drawbuffer, const GLfloat *value); +GLAPI void APIENTRY glClearBufferfi (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +GLAPI const GLubyte * APIENTRY glGetStringi (GLenum name, GLuint index); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOLORMASKIPROC) (GLuint index, GLboolean r, GLboolean g, GLboolean b, GLboolean a); +typedef void (APIENTRYP PFNGLGETBOOLEANI_VPROC) (GLenum target, GLuint index, GLboolean *data); +typedef void (APIENTRYP PFNGLGETINTEGERI_VPROC) (GLenum target, GLuint index, GLint *data); +typedef void (APIENTRYP PFNGLENABLEIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLDISABLEIPROC) (GLenum target, GLuint index); +typedef GLboolean (APIENTRYP PFNGLISENABLEDIPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLBEGINTRANSFORMFEEDBACKPROC) (GLenum primitiveMode); +typedef void (APIENTRYP PFNGLENDTRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLBINDBUFFERRANGEPROC) (GLenum target, GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLBINDBUFFERBASEPROC) (GLenum target, GLuint index, GLuint buffer); +typedef void (APIENTRYP PFNGLTRANSFORMFEEDBACKVARYINGSPROC) (GLuint program, GLsizei count, const GLchar* const *varyings, GLenum bufferMode); +typedef void (APIENTRYP PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) (GLuint program, GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name); +typedef void (APIENTRYP PFNGLCLAMPCOLORPROC) (GLenum target, GLenum clamp); +typedef void (APIENTRYP PFNGLBEGINCONDITIONALRENDERPROC) (GLuint id, GLenum mode); +typedef void (APIENTRYP PFNGLENDCONDITIONALRENDERPROC) (void); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIIVPROC) (GLuint index, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBIUIVPROC) (GLuint index, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IPROC) (GLuint index, GLint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IPROC) (GLuint index, GLint x, GLint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IPROC) (GLuint index, GLint x, GLint y, GLint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IPROC) (GLuint index, GLint x, GLint y, GLint z, GLint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIPROC) (GLuint index, GLuint x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIPROC) (GLuint index, GLuint x, GLuint y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIPROC) (GLuint index, GLuint x, GLuint y, GLuint z, GLuint w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4IVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI1UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI2UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI3UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UIVPROC) (GLuint index, const GLuint *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4BVPROC) (GLuint index, const GLbyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4SVPROC) (GLuint index, const GLshort *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4UBVPROC) (GLuint index, const GLubyte *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBI4USVPROC) (GLuint index, const GLushort *v); +typedef void (APIENTRYP PFNGLGETUNIFORMUIVPROC) (GLuint program, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONPROC) (GLuint program, GLuint color, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATALOCATIONPROC) (GLuint program, const GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORM1UIPROC) (GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLUNIFORM2UIPROC) (GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLUNIFORM3UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLUNIFORM4UIPROC) (GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLUNIFORM1UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM2UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM3UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLUNIFORM4UIVPROC) (GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, const GLint *params); +typedef void (APIENTRYP PFNGLTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, const GLuint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETTEXPARAMETERIUIVPROC) (GLenum target, GLenum pname, GLuint *params); +typedef void (APIENTRYP PFNGLCLEARBUFFERIVPROC) (GLenum buffer, GLint drawbuffer, const GLint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERUIVPROC) (GLenum buffer, GLint drawbuffer, const GLuint *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFVPROC) (GLenum buffer, GLint drawbuffer, const GLfloat *value); +typedef void (APIENTRYP PFNGLCLEARBUFFERFIPROC) (GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil); +typedef const GLubyte * (APIENTRYP PFNGLGETSTRINGIPROC) (GLenum name, GLuint index); +#endif + +#ifndef GL_VERSION_3_1 +#define GL_VERSION_3_1 1 +/* OpenGL 3.1 also reuses entry points from these extensions: */ +/* ARB_copy_buffer */ +/* ARB_uniform_buffer_object */ +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstanced (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +GLAPI void APIENTRY glDrawElementsInstanced (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount); +GLAPI void APIENTRY glTexBuffer (GLenum target, GLenum internalformat, GLuint buffer); +GLAPI void APIENTRY glPrimitiveRestartIndex (GLuint index); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount); +typedef void (APIENTRYP PFNGLTEXBUFFERPROC) (GLenum target, GLenum internalformat, GLuint buffer); +typedef void (APIENTRYP PFNGLPRIMITIVERESTARTINDEXPROC) (GLuint index); +#endif + +#ifndef GL_VERSION_3_2 +#define GL_VERSION_3_2 1 +/* OpenGL 3.2 also reuses entry points from these extensions: */ +/* ARB_draw_elements_base_vertex */ +/* ARB_provoking_vertex */ +/* ARB_sync */ +/* ARB_texture_multisample */ +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetInteger64i_v (GLenum target, GLuint index, GLint64 *data); +GLAPI void APIENTRY glGetBufferParameteri64v (GLenum target, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glFramebufferTexture (GLenum target, GLenum attachment, GLuint texture, GLint level); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETINTEGER64I_VPROC) (GLenum target, GLuint index, GLint64 *data); +typedef void (APIENTRYP PFNGLGETBUFFERPARAMETERI64VPROC) (GLenum target, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTUREPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level); +#endif + +#ifndef GL_VERSION_3_3 +#define GL_VERSION_3_3 1 +/* OpenGL 3.3 also reuses entry points from these extensions: */ +/* ARB_blend_func_extended */ +/* ARB_sampler_objects */ +/* ARB_explicit_attrib_location, but it has none */ +/* ARB_occlusion_query2 (no entry points) */ +/* ARB_shader_bit_encoding (no entry points) */ +/* ARB_texture_rgb10_a2ui (no entry points) */ +/* ARB_texture_swizzle (no entry points) */ +/* ARB_timer_query */ +/* ARB_vertex_type_2_10_10_10_rev */ +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glVertexAttribDivisor (GLuint index, GLuint divisor); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC) (GLuint index, GLuint divisor); +#endif + +#ifndef GL_VERSION_4_0 +#define GL_VERSION_4_0 1 +/* OpenGL 4.0 also reuses entry points from these extensions: */ +/* ARB_texture_query_lod (no entry points) */ +/* ARB_draw_indirect */ +/* ARB_gpu_shader5 (no entry points) */ +/* ARB_gpu_shader_fp64 */ +/* ARB_shader_subroutine */ +/* ARB_tessellation_shader */ +/* ARB_texture_buffer_object_rgb32 (no entry points) */ +/* ARB_texture_cube_map_array (no entry points) */ +/* ARB_texture_gather (no entry points) */ +/* ARB_transform_feedback2 */ +/* ARB_transform_feedback3 */ +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glMinSampleShading (GLfloat value); +GLAPI void APIENTRY glBlendEquationi (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparatei (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunci (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparatei (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGPROC) (GLfloat value); +typedef void (APIENTRYP PFNGLBLENDEQUATIONIPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +#ifndef GL_VERSION_4_1 +#define GL_VERSION_4_1 1 +/* OpenGL 4.1 reuses entry points from these extensions: */ +/* ARB_ES2_compatibility */ +/* ARB_get_program_binary */ +/* ARB_separate_shader_objects */ +/* ARB_shader_precision (no entry points) */ +/* ARB_vertex_attrib_64bit */ +/* ARB_viewport_array */ +#endif + +#ifndef GL_VERSION_4_2 +#define GL_VERSION_4_2 1 +/* OpenGL 4.2 reuses entry points from these extensions: */ +/* ARB_base_instance */ +/* ARB_shading_language_420pack (no entry points) */ +/* ARB_transform_feedback_instanced */ +/* ARB_compressed_texture_pixel_storage (no entry points) */ +/* ARB_conservative_depth (no entry points) */ +/* ARB_internalformat_query */ +/* ARB_map_buffer_alignment (no entry points) */ +/* ARB_shader_atomic_counters */ +/* ARB_shader_image_load_store */ +/* ARB_shading_language_packing (no entry points) */ +/* ARB_texture_storage */ +#endif + +#ifndef GL_VERSION_4_3 +#define GL_VERSION_4_3 1 +/* OpenGL 4.3 reuses entry points from these extensions: */ +/* ARB_arrays_of_arrays (no entry points, GLSL only) */ +/* ARB_fragment_layer_viewport (no entry points, GLSL only) */ +/* ARB_shader_image_size (no entry points, GLSL only) */ +/* ARB_ES3_compatibility (no entry points) */ +/* ARB_clear_buffer_object */ +/* ARB_compute_shader */ +/* ARB_copy_image */ +/* KHR_debug (includes ARB_debug_output commands promoted to KHR without suffixes) */ +/* ARB_explicit_uniform_location (no entry points) */ +/* ARB_framebuffer_no_attachments */ +/* ARB_internalformat_query2 */ +/* ARB_invalidate_subdata */ +/* ARB_multi_draw_indirect */ +/* ARB_program_interface_query */ +/* ARB_robust_buffer_access_behavior (no entry points) */ +/* ARB_shader_storage_buffer_object */ +/* ARB_stencil_texturing (no entry points) */ +/* ARB_texture_buffer_range */ +/* ARB_texture_query_levels (no entry points) */ +/* ARB_texture_storage_multisample */ +/* ARB_texture_view */ +/* ARB_vertex_attrib_binding */ +#endif + +#ifndef GL_ARB_depth_buffer_float +#define GL_ARB_depth_buffer_float 1 +#endif + +#ifndef GL_ARB_framebuffer_object +#define GL_ARB_framebuffer_object 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLboolean APIENTRY glIsRenderbuffer (GLuint renderbuffer); +GLAPI void APIENTRY glBindRenderbuffer (GLenum target, GLuint renderbuffer); +GLAPI void APIENTRY glDeleteRenderbuffers (GLsizei n, const GLuint *renderbuffers); +GLAPI void APIENTRY glGenRenderbuffers (GLsizei n, GLuint *renderbuffers); +GLAPI void APIENTRY glRenderbufferStorage (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glGetRenderbufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI GLboolean APIENTRY glIsFramebuffer (GLuint framebuffer); +GLAPI void APIENTRY glBindFramebuffer (GLenum target, GLuint framebuffer); +GLAPI void APIENTRY glDeleteFramebuffers (GLsizei n, const GLuint *framebuffers); +GLAPI void APIENTRY glGenFramebuffers (GLsizei n, GLuint *framebuffers); +GLAPI GLenum APIENTRY glCheckFramebufferStatus (GLenum target); +GLAPI void APIENTRY glFramebufferTexture1D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture2D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +GLAPI void APIENTRY glFramebufferTexture3D (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +GLAPI void APIENTRY glFramebufferRenderbuffer (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +GLAPI void APIENTRY glGetFramebufferAttachmentParameteriv (GLenum target, GLenum attachment, GLenum pname, GLint *params); +GLAPI void APIENTRY glGenerateMipmap (GLenum target); +GLAPI void APIENTRY glBlitFramebuffer (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +GLAPI void APIENTRY glRenderbufferStorageMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glFramebufferTextureLayer (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLboolean (APIENTRYP PFNGLISRENDERBUFFERPROC) (GLuint renderbuffer); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFERPROC) (GLenum target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSPROC) (GLsizei n, const GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEPROC) (GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLGETRENDERBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFERPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFERPROC) (GLenum target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSPROC) (GLenum target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE1DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE3DPROC) (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLint zoffset); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFERPROC) (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) (GLenum target, GLenum attachment, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGENERATEMIPMAPPROC) (GLenum target); +typedef void (APIENTRYP PFNGLBLITFRAMEBUFFERPROC) (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURELAYERPROC) (GLenum target, GLenum attachment, GLuint texture, GLint level, GLint layer); +#endif + +#ifndef GL_ARB_framebuffer_sRGB +#define GL_ARB_framebuffer_sRGB 1 +#endif + +#ifndef GL_ARB_half_float_vertex +#define GL_ARB_half_float_vertex 1 +#endif + +#ifndef GL_ARB_map_buffer_range +#define GL_ARB_map_buffer_range 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLvoid* APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +GLAPI void APIENTRY glFlushMappedBufferRange (GLenum target, GLintptr offset, GLsizeiptr length); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (APIENTRYP PFNGLFLUSHMAPPEDBUFFERRANGEPROC) (GLenum target, GLintptr offset, GLsizeiptr length); +#endif + +#ifndef GL_ARB_texture_compression_rgtc +#define GL_ARB_texture_compression_rgtc 1 +#endif + +#ifndef GL_ARB_texture_rg +#define GL_ARB_texture_rg 1 +#endif + +#ifndef GL_ARB_vertex_array_object +#define GL_ARB_vertex_array_object 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBindVertexArray (GLuint array); +GLAPI void APIENTRY glDeleteVertexArrays (GLsizei n, const GLuint *arrays); +GLAPI void APIENTRY glGenVertexArrays (GLsizei n, GLuint *arrays); +GLAPI GLboolean APIENTRY glIsVertexArray (GLuint array); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXARRAYPROC) (GLuint array); +typedef void (APIENTRYP PFNGLDELETEVERTEXARRAYSPROC) (GLsizei n, const GLuint *arrays); +typedef void (APIENTRYP PFNGLGENVERTEXARRAYSPROC) (GLsizei n, GLuint *arrays); +typedef GLboolean (APIENTRYP PFNGLISVERTEXARRAYPROC) (GLuint array); +#endif + +#ifndef GL_ARB_uniform_buffer_object +#define GL_ARB_uniform_buffer_object 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetUniformIndices (GLuint program, GLsizei uniformCount, const GLchar* const *uniformNames, GLuint *uniformIndices); +GLAPI void APIENTRY glGetActiveUniformsiv (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformName (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +GLAPI GLuint APIENTRY glGetUniformBlockIndex (GLuint program, const GLchar *uniformBlockName); +GLAPI void APIENTRY glGetActiveUniformBlockiv (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetActiveUniformBlockName (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +GLAPI void APIENTRY glUniformBlockBinding (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETUNIFORMINDICESPROC) (GLuint program, GLsizei uniformCount, const GLchar* const *uniformNames, GLuint *uniformIndices); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMSIVPROC) (GLuint program, GLsizei uniformCount, const GLuint *uniformIndices, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMNAMEPROC) (GLuint program, GLuint uniformIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformName); +typedef GLuint (APIENTRYP PFNGLGETUNIFORMBLOCKINDEXPROC) (GLuint program, const GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKIVPROC) (GLuint program, GLuint uniformBlockIndex, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) (GLuint program, GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName); +typedef void (APIENTRYP PFNGLUNIFORMBLOCKBINDINGPROC) (GLuint program, GLuint uniformBlockIndex, GLuint uniformBlockBinding); +#endif + +#ifndef GL_ARB_copy_buffer +#define GL_ARB_copy_buffer 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glCopyBufferSubData (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYBUFFERSUBDATAPROC) (GLenum readTarget, GLenum writeTarget, GLintptr readOffset, GLintptr writeOffset, GLsizeiptr size); +#endif + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp 1 +#endif + +#ifndef GL_ARB_draw_elements_base_vertex +#define GL_ARB_draw_elements_base_vertex 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawElementsBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +GLAPI void APIENTRY glDrawRangeElementsBaseVertex (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertex (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount, GLint basevertex); +GLAPI void APIENTRY glMultiDrawElementsBaseVertex (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount, const GLint *basevertex); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices, GLint basevertex); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) (GLenum mode, GLsizei count, GLenum type, const GLvoid *indices, GLsizei instancecount, GLint basevertex); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid* const *indices, GLsizei drawcount, const GLint *basevertex); +#endif + +#ifndef GL_ARB_fragment_coord_conventions +#define GL_ARB_fragment_coord_conventions 1 +#endif + +#ifndef GL_ARB_provoking_vertex +#define GL_ARB_provoking_vertex 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glProvokingVertex (GLenum mode); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPROVOKINGVERTEXPROC) (GLenum mode); +#endif + +#ifndef GL_ARB_seamless_cube_map +#define GL_ARB_seamless_cube_map 1 +#endif + +#ifndef GL_ARB_sync +#define GL_ARB_sync 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLsync APIENTRY glFenceSync (GLenum condition, GLbitfield flags); +GLAPI GLboolean APIENTRY glIsSync (GLsync sync); +GLAPI void APIENTRY glDeleteSync (GLsync sync); +GLAPI GLenum APIENTRY glClientWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glWaitSync (GLsync sync, GLbitfield flags, GLuint64 timeout); +GLAPI void APIENTRY glGetInteger64v (GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetSynciv (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLsync (APIENTRYP PFNGLFENCESYNCPROC) (GLenum condition, GLbitfield flags); +typedef GLboolean (APIENTRYP PFNGLISSYNCPROC) (GLsync sync); +typedef void (APIENTRYP PFNGLDELETESYNCPROC) (GLsync sync); +typedef GLenum (APIENTRYP PFNGLCLIENTWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLWAITSYNCPROC) (GLsync sync, GLbitfield flags, GLuint64 timeout); +typedef void (APIENTRYP PFNGLGETINTEGER64VPROC) (GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETSYNCIVPROC) (GLsync sync, GLenum pname, GLsizei bufSize, GLsizei *length, GLint *values); +#endif + +#ifndef GL_ARB_texture_multisample +#define GL_ARB_texture_multisample 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glTexImage2DMultisample (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexImage3DMultisample (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glGetMultisamplefv (GLenum pname, GLuint index, GLfloat *val); +GLAPI void APIENTRY glSampleMaski (GLuint index, GLbitfield mask); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXIMAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXIMAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLint internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLGETMULTISAMPLEFVPROC) (GLenum pname, GLuint index, GLfloat *val); +typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC) (GLuint index, GLbitfield mask); +#endif + +#ifndef GL_ARB_vertex_array_bgra +#define GL_ARB_vertex_array_bgra 1 +#endif + +#ifndef GL_ARB_draw_buffers_blend +#define GL_ARB_draw_buffers_blend 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBlendEquationiARB (GLuint buf, GLenum mode); +GLAPI void APIENTRY glBlendEquationSeparateiARB (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +GLAPI void APIENTRY glBlendFunciARB (GLuint buf, GLenum src, GLenum dst); +GLAPI void APIENTRY glBlendFuncSeparateiARB (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBLENDEQUATIONIARBPROC) (GLuint buf, GLenum mode); +typedef void (APIENTRYP PFNGLBLENDEQUATIONSEPARATEIARBPROC) (GLuint buf, GLenum modeRGB, GLenum modeAlpha); +typedef void (APIENTRYP PFNGLBLENDFUNCIARBPROC) (GLuint buf, GLenum src, GLenum dst); +typedef void (APIENTRYP PFNGLBLENDFUNCSEPARATEIARBPROC) (GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha); +#endif + +#ifndef GL_ARB_sample_shading +#define GL_ARB_sample_shading 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glMinSampleShadingARB (GLfloat value); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMINSAMPLESHADINGARBPROC) (GLfloat value); +#endif + +#ifndef GL_ARB_texture_cube_map_array +#define GL_ARB_texture_cube_map_array 1 +#endif + +#ifndef GL_ARB_texture_gather +#define GL_ARB_texture_gather 1 +#endif + +#ifndef GL_ARB_texture_query_lod +#define GL_ARB_texture_query_lod 1 +#endif + +#ifndef GL_ARB_shading_language_include +#define GL_ARB_shading_language_include 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glNamedStringARB (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +GLAPI void APIENTRY glDeleteNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glCompileShaderIncludeARB (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length); +GLAPI GLboolean APIENTRY glIsNamedStringARB (GLint namelen, const GLchar *name); +GLAPI void APIENTRY glGetNamedStringARB (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +GLAPI void APIENTRY glGetNamedStringivARB (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLNAMEDSTRINGARBPROC) (GLenum type, GLint namelen, const GLchar *name, GLint stringlen, const GLchar *string); +typedef void (APIENTRYP PFNGLDELETENAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLCOMPILESHADERINCLUDEARBPROC) (GLuint shader, GLsizei count, const GLchar* *path, const GLint *length); +typedef GLboolean (APIENTRYP PFNGLISNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGARBPROC) (GLint namelen, const GLchar *name, GLsizei bufSize, GLint *stringlen, GLchar *string); +typedef void (APIENTRYP PFNGLGETNAMEDSTRINGIVARBPROC) (GLint namelen, const GLchar *name, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc 1 +#endif + +#ifndef GL_ARB_blend_func_extended +#define GL_ARB_blend_func_extended 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBindFragDataLocationIndexed (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +GLAPI GLint APIENTRY glGetFragDataIndex (GLuint program, const GLchar *name); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) (GLuint program, GLuint colorNumber, GLuint index, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC) (GLuint program, const GLchar *name); +#endif + +#ifndef GL_ARB_explicit_attrib_location +#define GL_ARB_explicit_attrib_location 1 +#endif + +#ifndef GL_ARB_occlusion_query2 +#define GL_ARB_occlusion_query2 1 +#endif + +#ifndef GL_ARB_sampler_objects +#define GL_ARB_sampler_objects 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGenSamplers (GLsizei count, GLuint *samplers); +GLAPI void APIENTRY glDeleteSamplers (GLsizei count, const GLuint *samplers); +GLAPI GLboolean APIENTRY glIsSampler (GLuint sampler); +GLAPI void APIENTRY glBindSampler (GLuint unit, GLuint sampler); +GLAPI void APIENTRY glSamplerParameteri (GLuint sampler, GLenum pname, GLint param); +GLAPI void APIENTRY glSamplerParameteriv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterf (GLuint sampler, GLenum pname, GLfloat param); +GLAPI void APIENTRY glSamplerParameterfv (GLuint sampler, GLenum pname, const GLfloat *param); +GLAPI void APIENTRY glSamplerParameterIiv (GLuint sampler, GLenum pname, const GLint *param); +GLAPI void APIENTRY glSamplerParameterIuiv (GLuint sampler, GLenum pname, const GLuint *param); +GLAPI void APIENTRY glGetSamplerParameteriv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterIiv (GLuint sampler, GLenum pname, GLint *params); +GLAPI void APIENTRY glGetSamplerParameterfv (GLuint sampler, GLenum pname, GLfloat *params); +GLAPI void APIENTRY glGetSamplerParameterIuiv (GLuint sampler, GLenum pname, GLuint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGENSAMPLERSPROC) (GLsizei count, GLuint *samplers); +typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC) (GLsizei count, const GLuint *samplers); +typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC) (GLuint sampler); +typedef void (APIENTRYP PFNGLBINDSAMPLERPROC) (GLuint unit, GLuint sampler); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC) (GLuint sampler, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC) (GLuint sampler, GLenum pname, GLfloat param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, const GLfloat *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, const GLint *param); +typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, const GLuint *param); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC) (GLuint sampler, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC) (GLuint sampler, GLenum pname, GLfloat *params); +typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC) (GLuint sampler, GLenum pname, GLuint *params); +#endif + +#ifndef GL_ARB_shader_bit_encoding +#define GL_ARB_shader_bit_encoding 1 +#endif + +#ifndef GL_ARB_texture_rgb10_a2ui +#define GL_ARB_texture_rgb10_a2ui 1 +#endif + +#ifndef GL_ARB_texture_swizzle +#define GL_ARB_texture_swizzle 1 +#endif + +#ifndef GL_ARB_timer_query +#define GL_ARB_timer_query 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glQueryCounter (GLuint id, GLenum target); +GLAPI void APIENTRY glGetQueryObjecti64v (GLuint id, GLenum pname, GLint64 *params); +GLAPI void APIENTRY glGetQueryObjectui64v (GLuint id, GLenum pname, GLuint64 *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64 *params); +typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64 *params); +#endif + +#ifndef GL_ARB_vertex_type_2_10_10_10_rev +#define GL_ARB_vertex_type_2_10_10_10_rev 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glVertexP2ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP2uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP3ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP3uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glVertexP4ui (GLenum type, GLuint value); +GLAPI void APIENTRY glVertexP4uiv (GLenum type, const GLuint *value); +GLAPI void APIENTRY glTexCoordP1ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP1uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP2ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP2uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glTexCoordP4ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glTexCoordP4uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP1ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP1uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP2ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP2uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP3ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP3uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glMultiTexCoordP4ui (GLenum texture, GLenum type, GLuint coords); +GLAPI void APIENTRY glMultiTexCoordP4uiv (GLenum texture, GLenum type, const GLuint *coords); +GLAPI void APIENTRY glNormalP3ui (GLenum type, GLuint coords); +GLAPI void APIENTRY glNormalP3uiv (GLenum type, const GLuint *coords); +GLAPI void APIENTRY glColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glColorP4ui (GLenum type, GLuint color); +GLAPI void APIENTRY glColorP4uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glSecondaryColorP3ui (GLenum type, GLuint color); +GLAPI void APIENTRY glSecondaryColorP3uiv (GLenum type, const GLuint *color); +GLAPI void APIENTRY glVertexAttribP1ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP1uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP2ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP2uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP3ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP3uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +GLAPI void APIENTRY glVertexAttribP4ui (GLuint index, GLenum type, GLboolean normalized, GLuint value); +GLAPI void APIENTRY glVertexAttribP4uiv (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXP2UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP3UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXP4UIPROC) (GLenum type, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC) (GLenum type, const GLuint *value); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC) (GLenum texture, GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC) (GLenum texture, GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLNORMALP3UIPROC) (GLenum type, GLuint coords); +typedef void (APIENTRYP PFNGLNORMALP3UIVPROC) (GLenum type, const GLuint *coords); +typedef void (APIENTRYP PFNGLCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLCOLORP4UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLCOLORP4UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC) (GLenum type, GLuint color); +typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC) (GLenum type, const GLuint *color); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC) (GLuint index, GLenum type, GLboolean normalized, GLuint value); +typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC) (GLuint index, GLenum type, GLboolean normalized, const GLuint *value); +#endif + +#ifndef GL_ARB_draw_indirect +#define GL_ARB_draw_indirect 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawArraysIndirect (GLenum mode, const GLvoid *indirect); +GLAPI void APIENTRY glDrawElementsIndirect (GLenum mode, GLenum type, const GLvoid *indirect); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINDIRECTPROC) (GLenum mode, const GLvoid *indirect); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const GLvoid *indirect); +#endif + +#ifndef GL_ARB_gpu_shader5 +#define GL_ARB_gpu_shader5 1 +#endif + +#ifndef GL_ARB_gpu_shader_fp64 +#define GL_ARB_gpu_shader_fp64 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glUniform1d (GLint location, GLdouble x); +GLAPI void APIENTRY glUniform2d (GLint location, GLdouble x, GLdouble y); +GLAPI void APIENTRY glUniform3d (GLint location, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glUniform4d (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glUniform1dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform2dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform3dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniform4dv (GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix2x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix3x4dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x2dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glUniformMatrix4x3dv (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glGetUniformdv (GLuint program, GLint location, GLdouble *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUNIFORM1DPROC) (GLint location, GLdouble x); +typedef void (APIENTRYP PFNGLUNIFORM2DPROC) (GLint location, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLUNIFORM3DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLUNIFORM4DPROC) (GLint location, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLUNIFORM1DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM2DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM3DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORM4DVPROC) (GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX2X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX3X4DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X2DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLUNIFORMMATRIX4X3DVPROC) (GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLGETUNIFORMDVPROC) (GLuint program, GLint location, GLdouble *params); +#endif + +#ifndef GL_ARB_shader_subroutine +#define GL_ARB_shader_subroutine 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLint APIENTRY glGetSubroutineUniformLocation (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI GLuint APIENTRY glGetSubroutineIndex (GLuint program, GLenum shadertype, const GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineUniformiv (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +GLAPI void APIENTRY glGetActiveSubroutineUniformName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetActiveSubroutineName (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glUniformSubroutinesuiv (GLenum shadertype, GLsizei count, const GLuint *indices); +GLAPI void APIENTRY glGetUniformSubroutineuiv (GLenum shadertype, GLint location, GLuint *params); +GLAPI void APIENTRY glGetProgramStageiv (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLint (APIENTRYP PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef GLuint (APIENTRYP PFNGLGETSUBROUTINEINDEXPROC) (GLuint program, GLenum shadertype, const GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) (GLuint program, GLenum shadertype, GLuint index, GLenum pname, GLint *values); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETACTIVESUBROUTINENAMEPROC) (GLuint program, GLenum shadertype, GLuint index, GLsizei bufsize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLUNIFORMSUBROUTINESUIVPROC) (GLenum shadertype, GLsizei count, const GLuint *indices); +typedef void (APIENTRYP PFNGLGETUNIFORMSUBROUTINEUIVPROC) (GLenum shadertype, GLint location, GLuint *params); +typedef void (APIENTRYP PFNGLGETPROGRAMSTAGEIVPROC) (GLuint program, GLenum shadertype, GLenum pname, GLint *values); +#endif + +#ifndef GL_ARB_tessellation_shader +#define GL_ARB_tessellation_shader 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glPatchParameteri (GLenum pname, GLint value); +GLAPI void APIENTRY glPatchParameterfv (GLenum pname, const GLfloat *values); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLPATCHPARAMETERIPROC) (GLenum pname, GLint value); +typedef void (APIENTRYP PFNGLPATCHPARAMETERFVPROC) (GLenum pname, const GLfloat *values); +#endif + +#ifndef GL_ARB_texture_buffer_object_rgb32 +#define GL_ARB_texture_buffer_object_rgb32 1 +#endif + +#ifndef GL_ARB_transform_feedback2 +#define GL_ARB_transform_feedback2 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBindTransformFeedback (GLenum target, GLuint id); +GLAPI void APIENTRY glDeleteTransformFeedbacks (GLsizei n, const GLuint *ids); +GLAPI void APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint *ids); +GLAPI GLboolean APIENTRY glIsTransformFeedback (GLuint id); +GLAPI void APIENTRY glPauseTransformFeedback (void); +GLAPI void APIENTRY glResumeTransformFeedback (void); +GLAPI void APIENTRY glDrawTransformFeedback (GLenum mode, GLuint id); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDTRANSFORMFEEDBACKPROC) (GLenum target, GLuint id); +typedef void (APIENTRYP PFNGLDELETETRANSFORMFEEDBACKSPROC) (GLsizei n, const GLuint *ids); +typedef void (APIENTRYP PFNGLGENTRANSFORMFEEDBACKSPROC) (GLsizei n, GLuint *ids); +typedef GLboolean (APIENTRYP PFNGLISTRANSFORMFEEDBACKPROC) (GLuint id); +typedef void (APIENTRYP PFNGLPAUSETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLRESUMETRANSFORMFEEDBACKPROC) (void); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKPROC) (GLenum mode, GLuint id); +#endif + +#ifndef GL_ARB_transform_feedback3 +#define GL_ARB_transform_feedback3 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawTransformFeedbackStream (GLenum mode, GLuint id, GLuint stream); +GLAPI void APIENTRY glBeginQueryIndexed (GLenum target, GLuint index, GLuint id); +GLAPI void APIENTRY glEndQueryIndexed (GLenum target, GLuint index); +GLAPI void APIENTRY glGetQueryIndexediv (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) (GLenum mode, GLuint id, GLuint stream); +typedef void (APIENTRYP PFNGLBEGINQUERYINDEXEDPROC) (GLenum target, GLuint index, GLuint id); +typedef void (APIENTRYP PFNGLENDQUERYINDEXEDPROC) (GLenum target, GLuint index); +typedef void (APIENTRYP PFNGLGETQUERYINDEXEDIVPROC) (GLenum target, GLuint index, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_ES2_compatibility +#define GL_ARB_ES2_compatibility 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glReleaseShaderCompiler (void); +GLAPI void APIENTRY glShaderBinary (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); +GLAPI void APIENTRY glGetShaderPrecisionFormat (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +GLAPI void APIENTRY glDepthRangef (GLfloat n, GLfloat f); +GLAPI void APIENTRY glClearDepthf (GLfloat d); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLRELEASESHADERCOMPILERPROC) (void); +typedef void (APIENTRYP PFNGLSHADERBINARYPROC) (GLsizei count, const GLuint *shaders, GLenum binaryformat, const GLvoid *binary, GLsizei length); +typedef void (APIENTRYP PFNGLGETSHADERPRECISIONFORMATPROC) (GLenum shadertype, GLenum precisiontype, GLint *range, GLint *precision); +typedef void (APIENTRYP PFNGLDEPTHRANGEFPROC) (GLfloat n, GLfloat f); +typedef void (APIENTRYP PFNGLCLEARDEPTHFPROC) (GLfloat d); +#endif + +#ifndef GL_ARB_get_program_binary +#define GL_ARB_get_program_binary 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetProgramBinary (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +GLAPI void APIENTRY glProgramBinary (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length); +GLAPI void APIENTRY glProgramParameteri (GLuint program, GLenum pname, GLint value); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETPROGRAMBINARYPROC) (GLuint program, GLsizei bufSize, GLsizei *length, GLenum *binaryFormat, GLvoid *binary); +typedef void (APIENTRYP PFNGLPROGRAMBINARYPROC) (GLuint program, GLenum binaryFormat, const GLvoid *binary, GLsizei length); +typedef void (APIENTRYP PFNGLPROGRAMPARAMETERIPROC) (GLuint program, GLenum pname, GLint value); +#endif + +#ifndef GL_ARB_separate_shader_objects +#define GL_ARB_separate_shader_objects 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glUseProgramStages (GLuint pipeline, GLbitfield stages, GLuint program); +GLAPI void APIENTRY glActiveShaderProgram (GLuint pipeline, GLuint program); +GLAPI GLuint APIENTRY glCreateShaderProgramv (GLenum type, GLsizei count, const GLchar* const *strings); +GLAPI void APIENTRY glBindProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glDeleteProgramPipelines (GLsizei n, const GLuint *pipelines); +GLAPI void APIENTRY glGenProgramPipelines (GLsizei n, GLuint *pipelines); +GLAPI GLboolean APIENTRY glIsProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineiv (GLuint pipeline, GLenum pname, GLint *params); +GLAPI void APIENTRY glProgramUniform1i (GLuint program, GLint location, GLint v0); +GLAPI void APIENTRY glProgramUniform1iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform1f (GLuint program, GLint location, GLfloat v0); +GLAPI void APIENTRY glProgramUniform1fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform1d (GLuint program, GLint location, GLdouble v0); +GLAPI void APIENTRY glProgramUniform1dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform1ui (GLuint program, GLint location, GLuint v0); +GLAPI void APIENTRY glProgramUniform1uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform2i (GLuint program, GLint location, GLint v0, GLint v1); +GLAPI void APIENTRY glProgramUniform2iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform2f (GLuint program, GLint location, GLfloat v0, GLfloat v1); +GLAPI void APIENTRY glProgramUniform2fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform2d (GLuint program, GLint location, GLdouble v0, GLdouble v1); +GLAPI void APIENTRY glProgramUniform2dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform2ui (GLuint program, GLint location, GLuint v0, GLuint v1); +GLAPI void APIENTRY glProgramUniform2uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform3i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +GLAPI void APIENTRY glProgramUniform3iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform3f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +GLAPI void APIENTRY glProgramUniform3fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform3d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +GLAPI void APIENTRY glProgramUniform3dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform3ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +GLAPI void APIENTRY glProgramUniform3uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniform4i (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +GLAPI void APIENTRY glProgramUniform4iv (GLuint program, GLint location, GLsizei count, const GLint *value); +GLAPI void APIENTRY glProgramUniform4f (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +GLAPI void APIENTRY glProgramUniform4fv (GLuint program, GLint location, GLsizei count, const GLfloat *value); +GLAPI void APIENTRY glProgramUniform4d (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +GLAPI void APIENTRY glProgramUniform4dv (GLuint program, GLint location, GLsizei count, const GLdouble *value); +GLAPI void APIENTRY glProgramUniform4ui (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +GLAPI void APIENTRY glProgramUniform4uiv (GLuint program, GLint location, GLsizei count, const GLuint *value); +GLAPI void APIENTRY glProgramUniformMatrix2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3fv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +GLAPI void APIENTRY glProgramUniformMatrix2x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix2x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x2dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix3x4dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glProgramUniformMatrix4x3dv (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +GLAPI void APIENTRY glValidateProgramPipeline (GLuint pipeline); +GLAPI void APIENTRY glGetProgramPipelineInfoLog (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLUSEPROGRAMSTAGESPROC) (GLuint pipeline, GLbitfield stages, GLuint program); +typedef void (APIENTRYP PFNGLACTIVESHADERPROGRAMPROC) (GLuint pipeline, GLuint program); +typedef GLuint (APIENTRYP PFNGLCREATESHADERPROGRAMVPROC) (GLenum type, GLsizei count, const GLchar* const *strings); +typedef void (APIENTRYP PFNGLBINDPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLDELETEPROGRAMPIPELINESPROC) (GLsizei n, const GLuint *pipelines); +typedef void (APIENTRYP PFNGLGENPROGRAMPIPELINESPROC) (GLsizei n, GLuint *pipelines); +typedef GLboolean (APIENTRYP PFNGLISPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEIVPROC) (GLuint pipeline, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IPROC) (GLuint program, GLint location, GLint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FPROC) (GLuint program, GLint location, GLfloat v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DPROC) (GLuint program, GLint location, GLdouble v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIPROC) (GLuint program, GLint location, GLuint v0); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM1UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IPROC) (GLuint program, GLint location, GLint v0, GLint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM2UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM3UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IPROC) (GLuint program, GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4IVPROC) (GLuint program, GLint location, GLsizei count, const GLint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FPROC) (GLuint program, GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4FVPROC) (GLuint program, GLint location, GLsizei count, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DPROC) (GLuint program, GLint location, GLdouble v0, GLdouble v1, GLdouble v2, GLdouble v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4DVPROC) (GLuint program, GLint location, GLsizei count, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIPROC) (GLuint program, GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORM4UIVPROC) (GLuint program, GLint location, GLsizei count, const GLuint *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) (GLuint program, GLint location, GLsizei count, GLboolean transpose, const GLdouble *value); +typedef void (APIENTRYP PFNGLVALIDATEPROGRAMPIPELINEPROC) (GLuint pipeline); +typedef void (APIENTRYP PFNGLGETPROGRAMPIPELINEINFOLOGPROC) (GLuint pipeline, GLsizei bufSize, GLsizei *length, GLchar *infoLog); +#endif + +#ifndef GL_ARB_vertex_attrib_64bit +#define GL_ARB_vertex_attrib_64bit 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glVertexAttribL1d (GLuint index, GLdouble x); +GLAPI void APIENTRY glVertexAttribL2d (GLuint index, GLdouble x, GLdouble y); +GLAPI void APIENTRY glVertexAttribL3d (GLuint index, GLdouble x, GLdouble y, GLdouble z); +GLAPI void APIENTRY glVertexAttribL4d (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +GLAPI void APIENTRY glVertexAttribL1dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL2dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL3dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribL4dv (GLuint index, const GLdouble *v); +GLAPI void APIENTRY glVertexAttribLPointer (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +GLAPI void APIENTRY glGetVertexAttribLdv (GLuint index, GLenum pname, GLdouble *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DPROC) (GLuint index, GLdouble x); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DPROC) (GLuint index, GLdouble x, GLdouble y); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DPROC) (GLuint index, GLdouble x, GLdouble y, GLdouble z, GLdouble w); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL1DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL2DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL3DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBL4DVPROC) (GLuint index, const GLdouble *v); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLPOINTERPROC) (GLuint index, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer); +typedef void (APIENTRYP PFNGLGETVERTEXATTRIBLDVPROC) (GLuint index, GLenum pname, GLdouble *params); +#endif + +#ifndef GL_ARB_viewport_array +#define GL_ARB_viewport_array 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glViewportArrayv (GLuint first, GLsizei count, const GLfloat *v); +GLAPI void APIENTRY glViewportIndexedf (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +GLAPI void APIENTRY glViewportIndexedfv (GLuint index, const GLfloat *v); +GLAPI void APIENTRY glScissorArrayv (GLuint first, GLsizei count, const GLint *v); +GLAPI void APIENTRY glScissorIndexed (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +GLAPI void APIENTRY glScissorIndexedv (GLuint index, const GLint *v); +GLAPI void APIENTRY glDepthRangeArrayv (GLuint first, GLsizei count, const GLdouble *v); +GLAPI void APIENTRY glDepthRangeIndexed (GLuint index, GLdouble n, GLdouble f); +GLAPI void APIENTRY glGetFloati_v (GLenum target, GLuint index, GLfloat *data); +GLAPI void APIENTRY glGetDoublei_v (GLenum target, GLuint index, GLdouble *data); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLVIEWPORTARRAYVPROC) (GLuint first, GLsizei count, const GLfloat *v); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFPROC) (GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h); +typedef void (APIENTRYP PFNGLVIEWPORTINDEXEDFVPROC) (GLuint index, const GLfloat *v); +typedef void (APIENTRYP PFNGLSCISSORARRAYVPROC) (GLuint first, GLsizei count, const GLint *v); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDPROC) (GLuint index, GLint left, GLint bottom, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLSCISSORINDEXEDVPROC) (GLuint index, const GLint *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEARRAYVPROC) (GLuint first, GLsizei count, const GLdouble *v); +typedef void (APIENTRYP PFNGLDEPTHRANGEINDEXEDPROC) (GLuint index, GLdouble n, GLdouble f); +typedef void (APIENTRYP PFNGLGETFLOATI_VPROC) (GLenum target, GLuint index, GLfloat *data); +typedef void (APIENTRYP PFNGLGETDOUBLEI_VPROC) (GLenum target, GLuint index, GLdouble *data); +#endif + +#ifndef GL_ARB_cl_event +#define GL_ARB_cl_event 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLsync APIENTRY glCreateSyncFromCLeventARB (struct _cl_context * context, struct _cl_event * event, GLbitfield flags); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLsync (APIENTRYP PFNGLCREATESYNCFROMCLEVENTARBPROC) (struct _cl_context * context, struct _cl_event * event, GLbitfield flags); +#endif + +#ifndef GL_ARB_debug_output +#define GL_ARB_debug_output 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControlARB (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsertARB (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallbackARB (GLDEBUGPROCARB callback, const GLvoid *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLogARB (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLARBPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTARBPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKARBPROC) (GLDEBUGPROCARB callback, const GLvoid *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGARBPROC) (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +#endif + +#ifndef GL_ARB_robustness +#define GL_ARB_robustness 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI GLenum APIENTRY glGetGraphicsResetStatusARB (void); +GLAPI void APIENTRY glGetnTexImageARB (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid *img); +GLAPI void APIENTRY glReadnPixelsARB (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data); +GLAPI void APIENTRY glGetnCompressedTexImageARB (GLenum target, GLint lod, GLsizei bufSize, GLvoid *img); +GLAPI void APIENTRY glGetnUniformfvARB (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +GLAPI void APIENTRY glGetnUniformivARB (GLuint program, GLint location, GLsizei bufSize, GLint *params); +GLAPI void APIENTRY glGetnUniformuivARB (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +GLAPI void APIENTRY glGetnUniformdvARB (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef GLenum (APIENTRYP PFNGLGETGRAPHICSRESETSTATUSARBPROC) (void); +typedef void (APIENTRYP PFNGLGETNTEXIMAGEARBPROC) (GLenum target, GLint level, GLenum format, GLenum type, GLsizei bufSize, GLvoid *img); +typedef void (APIENTRYP PFNGLREADNPIXELSARBPROC) (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLsizei bufSize, GLvoid *data); +typedef void (APIENTRYP PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) (GLenum target, GLint lod, GLsizei bufSize, GLvoid *img); +typedef void (APIENTRYP PFNGLGETNUNIFORMFVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLfloat *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMUIVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLuint *params); +typedef void (APIENTRYP PFNGLGETNUNIFORMDVARBPROC) (GLuint program, GLint location, GLsizei bufSize, GLdouble *params); +#endif + +#ifndef GL_ARB_shader_stencil_export +#define GL_ARB_shader_stencil_export 1 +#endif + +#ifndef GL_ARB_base_instance +#define GL_ARB_base_instance 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawArraysInstancedBaseInstance (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +GLAPI void APIENTRY glDrawElementsInstancedBaseVertexBaseInstance (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLint first, GLsizei count, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLuint baseinstance); +typedef void (APIENTRYP PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) (GLenum mode, GLsizei count, GLenum type, const void *indices, GLsizei instancecount, GLint basevertex, GLuint baseinstance); +#endif + +#ifndef GL_ARB_shading_language_420pack +#define GL_ARB_shading_language_420pack 1 +#endif + +#ifndef GL_ARB_transform_feedback_instanced +#define GL_ARB_transform_feedback_instanced 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDrawTransformFeedbackInstanced (GLenum mode, GLuint id, GLsizei instancecount); +GLAPI void APIENTRY glDrawTransformFeedbackStreamInstanced (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) (GLenum mode, GLuint id, GLsizei instancecount); +typedef void (APIENTRYP PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) (GLenum mode, GLuint id, GLuint stream, GLsizei instancecount); +#endif + +#ifndef GL_ARB_compressed_texture_pixel_storage +#define GL_ARB_compressed_texture_pixel_storage 1 +#endif + +#ifndef GL_ARB_conservative_depth +#define GL_ARB_conservative_depth 1 +#endif + +#ifndef GL_ARB_internalformat_query +#define GL_ARB_internalformat_query 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETINTERNALFORMATIVPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint *params); +#endif + +#ifndef GL_ARB_map_buffer_alignment +#define GL_ARB_map_buffer_alignment 1 +#endif + +#ifndef GL_ARB_shader_atomic_counters +#define GL_ARB_shader_atomic_counters 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetActiveAtomicCounterBufferiv (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) (GLuint program, GLuint bufferIndex, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_shader_image_load_store +#define GL_ARB_shader_image_load_store 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBindImageTexture (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +GLAPI void APIENTRY glMemoryBarrier (GLbitfield barriers); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDIMAGETEXTUREPROC) (GLuint unit, GLuint texture, GLint level, GLboolean layered, GLint layer, GLenum access, GLenum format); +typedef void (APIENTRYP PFNGLMEMORYBARRIERPROC) (GLbitfield barriers); +#endif + +#ifndef GL_ARB_shading_language_packing +#define GL_ARB_shading_language_packing 1 +#endif + +#ifndef GL_ARB_texture_storage +#define GL_ARB_texture_storage 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glTexStorage1D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTexStorage2D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTexStorage3D (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glTextureStorage1DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +GLAPI void APIENTRY glTextureStorage2DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +GLAPI void APIENTRY glTextureStorage3DEXT (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSTORAGE1DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXSTORAGE2DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DPROC) (GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE1DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DEXTPROC) (GLuint texture, GLenum target, GLsizei levels, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth); +#endif + +#ifndef GL_KHR_texture_compression_astc_ldr +#define GL_KHR_texture_compression_astc_ldr 1 +#endif + +#ifndef GL_KHR_debug +#define GL_KHR_debug 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDebugMessageControl (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +GLAPI void APIENTRY glDebugMessageInsert (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +GLAPI void APIENTRY glDebugMessageCallback (GLDEBUGPROC callback, const void *userParam); +GLAPI GLuint APIENTRY glGetDebugMessageLog (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +GLAPI void APIENTRY glPushDebugGroup (GLenum source, GLuint id, GLsizei length, const GLchar *message); +GLAPI void APIENTRY glPopDebugGroup (void); +GLAPI void APIENTRY glObjectLabel (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectLabel (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +GLAPI void APIENTRY glObjectPtrLabel (const void *ptr, GLsizei length, const GLchar *label); +GLAPI void APIENTRY glGetObjectPtrLabel (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDEBUGMESSAGECONTROLPROC) (GLenum source, GLenum type, GLenum severity, GLsizei count, const GLuint *ids, GLboolean enabled); +typedef void (APIENTRYP PFNGLDEBUGMESSAGEINSERTPROC) (GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *buf); +typedef void (APIENTRYP PFNGLDEBUGMESSAGECALLBACKPROC) (GLDEBUGPROC callback, const void *userParam); +typedef GLuint (APIENTRYP PFNGLGETDEBUGMESSAGELOGPROC) (GLuint count, GLsizei bufsize, GLenum *sources, GLenum *types, GLuint *ids, GLenum *severities, GLsizei *lengths, GLchar *messageLog); +typedef void (APIENTRYP PFNGLPUSHDEBUGGROUPPROC) (GLenum source, GLuint id, GLsizei length, const GLchar *message); +typedef void (APIENTRYP PFNGLPOPDEBUGGROUPPROC) (void); +typedef void (APIENTRYP PFNGLOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTLABELPROC) (GLenum identifier, GLuint name, GLsizei bufSize, GLsizei *length, GLchar *label); +typedef void (APIENTRYP PFNGLOBJECTPTRLABELPROC) (const void *ptr, GLsizei length, const GLchar *label); +typedef void (APIENTRYP PFNGLGETOBJECTPTRLABELPROC) (const void *ptr, GLsizei bufSize, GLsizei *length, GLchar *label); +#endif + +#ifndef GL_ARB_arrays_of_arrays +#define GL_ARB_arrays_of_arrays 1 +#endif + +#ifndef GL_ARB_clear_buffer_object +#define GL_ARB_clear_buffer_object 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glClearBufferData (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearBufferSubData (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +GLAPI void APIENTRY glClearNamedBufferSubDataEXT (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, GLsizeiptr offset, GLsizeiptr size, const void *data); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCLEARBUFFERDATAPROC) (GLenum target, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARBUFFERSUBDATAPROC) (GLenum target, GLenum internalformat, GLintptr offset, GLsizeiptr size, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, const void *data); +typedef void (APIENTRYP PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) (GLuint buffer, GLenum internalformat, GLenum format, GLenum type, GLsizeiptr offset, GLsizeiptr size, const void *data); +#endif + +#ifndef GL_ARB_compute_shader +#define GL_ARB_compute_shader 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glDispatchCompute (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +GLAPI void APIENTRY glDispatchComputeIndirect (GLintptr indirect); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEPROC) (GLuint num_groups_x, GLuint num_groups_y, GLuint num_groups_z); +typedef void (APIENTRYP PFNGLDISPATCHCOMPUTEINDIRECTPROC) (GLintptr indirect); +#endif + +#ifndef GL_ARB_copy_image +#define GL_ARB_copy_image 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glCopyImageSubData (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLCOPYIMAGESUBDATAPROC) (GLuint srcName, GLenum srcTarget, GLint srcLevel, GLint srcX, GLint srcY, GLint srcZ, GLuint dstName, GLenum dstTarget, GLint dstLevel, GLint dstX, GLint dstY, GLint dstZ, GLsizei srcWidth, GLsizei srcHeight, GLsizei srcDepth); +#endif + +#ifndef GL_ARB_texture_view +#define GL_ARB_texture_view 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glTextureView (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXTUREVIEWPROC) (GLuint texture, GLenum target, GLuint origtexture, GLenum internalformat, GLuint minlevel, GLuint numlevels, GLuint minlayer, GLuint numlayers); +#endif + +#ifndef GL_ARB_vertex_attrib_binding +#define GL_ARB_vertex_attrib_binding 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glBindVertexBuffer (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexAttribFormat (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribIFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribLFormat (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexAttribBinding (GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexBindingDivisor (GLuint bindingindex, GLuint divisor); +GLAPI void APIENTRY glVertexArrayBindVertexBufferEXT (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +GLAPI void APIENTRY glVertexArrayVertexAttribFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribIFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribLFormatEXT (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +GLAPI void APIENTRY glVertexArrayVertexAttribBindingEXT (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +GLAPI void APIENTRY glVertexArrayVertexBindingDivisorEXT (GLuint vaobj, GLuint bindingindex, GLuint divisor); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLBINDVERTEXBUFFERPROC) (GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXATTRIBFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBIFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBLFORMATPROC) (GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXATTRIBBINDINGPROC) (GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXBINDINGDIVISORPROC) (GLuint bindingindex, GLuint divisor); +typedef void (APIENTRYP PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint buffer, GLintptr offset, GLsizei stride); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLboolean normalized, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) (GLuint vaobj, GLuint attribindex, GLint size, GLenum type, GLuint relativeoffset); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) (GLuint vaobj, GLuint attribindex, GLuint bindingindex); +typedef void (APIENTRYP PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) (GLuint vaobj, GLuint bindingindex, GLuint divisor); +#endif + +#ifndef GL_ARB_robustness_isolation +#define GL_ARB_robustness_isolation 1 +#endif + +#ifndef GL_ARB_ES3_compatibility +#define GL_ARB_ES3_compatibility 1 +#endif + +#ifndef GL_ARB_explicit_uniform_location +#define GL_ARB_explicit_uniform_location 1 +#endif + +#ifndef GL_ARB_fragment_layer_viewport +#define GL_ARB_fragment_layer_viewport 1 +#endif + +#ifndef GL_ARB_framebuffer_no_attachments +#define GL_ARB_framebuffer_no_attachments 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glFramebufferParameteri (GLenum target, GLenum pname, GLint param); +GLAPI void APIENTRY glGetFramebufferParameteriv (GLenum target, GLenum pname, GLint *params); +GLAPI void APIENTRY glNamedFramebufferParameteriEXT (GLuint framebuffer, GLenum pname, GLint param); +GLAPI void APIENTRY glGetNamedFramebufferParameterivEXT (GLuint framebuffer, GLenum pname, GLint *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLFRAMEBUFFERPARAMETERIPROC) (GLenum target, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETFRAMEBUFFERPARAMETERIVPROC) (GLenum target, GLenum pname, GLint *params); +typedef void (APIENTRYP PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) (GLuint framebuffer, GLenum pname, GLint param); +typedef void (APIENTRYP PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) (GLuint framebuffer, GLenum pname, GLint *params); +#endif + +#ifndef GL_ARB_internalformat_query2 +#define GL_ARB_internalformat_query2 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetInternalformati64v (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETINTERNALFORMATI64VPROC) (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint64 *params); +#endif + +#ifndef GL_ARB_invalidate_subdata +#define GL_ARB_invalidate_subdata 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glInvalidateTexSubImage (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +GLAPI void APIENTRY glInvalidateTexImage (GLuint texture, GLint level); +GLAPI void APIENTRY glInvalidateBufferSubData (GLuint buffer, GLintptr offset, GLsizeiptr length); +GLAPI void APIENTRY glInvalidateBufferData (GLuint buffer); +GLAPI void APIENTRY glInvalidateFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments); +GLAPI void APIENTRY glInvalidateSubFramebuffer (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLINVALIDATETEXSUBIMAGEPROC) (GLuint texture, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth); +typedef void (APIENTRYP PFNGLINVALIDATETEXIMAGEPROC) (GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERSUBDATAPROC) (GLuint buffer, GLintptr offset, GLsizeiptr length); +typedef void (APIENTRYP PFNGLINVALIDATEBUFFERDATAPROC) (GLuint buffer); +typedef void (APIENTRYP PFNGLINVALIDATEFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments); +typedef void (APIENTRYP PFNGLINVALIDATESUBFRAMEBUFFERPROC) (GLenum target, GLsizei numAttachments, const GLenum *attachments, GLint x, GLint y, GLsizei width, GLsizei height); +#endif + +#ifndef GL_ARB_multi_draw_indirect +#define GL_ARB_multi_draw_indirect 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glMultiDrawArraysIndirect (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +GLAPI void APIENTRY glMultiDrawElementsIndirect (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLMULTIDRAWARRAYSINDIRECTPROC) (GLenum mode, const void *indirect, GLsizei drawcount, GLsizei stride); +typedef void (APIENTRYP PFNGLMULTIDRAWELEMENTSINDIRECTPROC) (GLenum mode, GLenum type, const void *indirect, GLsizei drawcount, GLsizei stride); +#endif + +#ifndef GL_ARB_program_interface_query +#define GL_ARB_program_interface_query 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glGetProgramInterfaceiv (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +GLAPI GLuint APIENTRY glGetProgramResourceIndex (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI void APIENTRY glGetProgramResourceName (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +GLAPI void APIENTRY glGetProgramResourceiv (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +GLAPI GLint APIENTRY glGetProgramResourceLocation (GLuint program, GLenum programInterface, const GLchar *name); +GLAPI GLint APIENTRY glGetProgramResourceLocationIndex (GLuint program, GLenum programInterface, const GLchar *name); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLGETPROGRAMINTERFACEIVPROC) (GLuint program, GLenum programInterface, GLenum pname, GLint *params); +typedef GLuint (APIENTRYP PFNGLGETPROGRAMRESOURCEINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCENAMEPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name); +typedef void (APIENTRYP PFNGLGETPROGRAMRESOURCEIVPROC) (GLuint program, GLenum programInterface, GLuint index, GLsizei propCount, const GLenum *props, GLsizei bufSize, GLsizei *length, GLint *params); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONPROC) (GLuint program, GLenum programInterface, const GLchar *name); +typedef GLint (APIENTRYP PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) (GLuint program, GLenum programInterface, const GLchar *name); +#endif + +#ifndef GL_ARB_robust_buffer_access_behavior +#define GL_ARB_robust_buffer_access_behavior 1 +#endif + +#ifndef GL_ARB_shader_image_size +#define GL_ARB_shader_image_size 1 +#endif + +#ifndef GL_ARB_shader_storage_buffer_object +#define GL_ARB_shader_storage_buffer_object 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glShaderStorageBlockBinding (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLSHADERSTORAGEBLOCKBINDINGPROC) (GLuint program, GLuint storageBlockIndex, GLuint storageBlockBinding); +#endif + +#ifndef GL_ARB_stencil_texturing +#define GL_ARB_stencil_texturing 1 +#endif + +#ifndef GL_ARB_texture_buffer_range +#define GL_ARB_texture_buffer_range 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glTexBufferRange (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +GLAPI void APIENTRY glTextureBufferRangeEXT (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXBUFFERRANGEPROC) (GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +typedef void (APIENTRYP PFNGLTEXTUREBUFFERRANGEEXTPROC) (GLuint texture, GLenum target, GLenum internalformat, GLuint buffer, GLintptr offset, GLsizeiptr size); +#endif + +#ifndef GL_ARB_texture_query_levels +#define GL_ARB_texture_query_levels 1 +#endif + +#ifndef GL_ARB_texture_storage_multisample +#define GL_ARB_texture_storage_multisample 1 +#ifdef GLCOREARB_PROTOTYPES +GLAPI void APIENTRY glTexStorage2DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTexStorage3DMultisample (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage2DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +GLAPI void APIENTRY glTextureStorage3DMultisampleEXT (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +#endif /* GLCOREARB_PROTOTYPES */ +typedef void (APIENTRYP PFNGLTEXSTORAGE2DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXSTORAGE3DMULTISAMPLEPROC) (GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLboolean fixedsamplelocations); +typedef void (APIENTRYP PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) (GLuint texture, GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, GLboolean fixedsamplelocations); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/apps/exampleViewer/common/gl3w/gl3w.c b/apps/exampleViewer/common/gl3w/gl3w.c new file mode 100644 index 0000000000..464e017788 --- /dev/null +++ b/apps/exampleViewer/common/gl3w/gl3w.c @@ -0,0 +1,1344 @@ +#include + +#ifdef _MSC_VER +#pragma warning (disable: 4055) // warning C4055: 'type cast' : from data pointer 'void *' to function pointer +#pragma warning (disable: 4152) // warning C4152: nonstandard extension, function/data pointer conversion in expression +#endif + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN 1 +#include + +static HMODULE libgl; + +static void open_libgl(void) +{ + libgl = LoadLibraryA("opengl32.dll"); +} + +static void close_libgl(void) +{ + FreeLibrary(libgl); +} + +static void *get_proc(const char *proc) +{ + void *res; + + res = wglGetProcAddress(proc); + if (!res) + res = GetProcAddress(libgl, proc); + return res; +} +#elif defined(__APPLE__) || defined(__APPLE_CC__) +#include + +CFBundleRef bundle; +CFURLRef bundleURL; + +static void open_libgl(void) +{ + bundleURL = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, + CFSTR("/System/Library/Frameworks/OpenGL.framework"), + kCFURLPOSIXPathStyle, true); + + bundle = CFBundleCreate(kCFAllocatorDefault, bundleURL); + assert(bundle != NULL); +} + +static void close_libgl(void) +{ + CFRelease(bundle); + CFRelease(bundleURL); +} + +static void *get_proc(const char *proc) +{ + void *res; + + CFStringRef procname = CFStringCreateWithCString(kCFAllocatorDefault, proc, + kCFStringEncodingASCII); + res = CFBundleGetFunctionPointerForName(bundle, procname); + CFRelease(procname); + return res; +} +#else +#include +#include + +static void *libgl; + +static void open_libgl(void) +{ + libgl = dlopen("libGL.so.1", RTLD_LAZY | RTLD_GLOBAL); +} + +static void close_libgl(void) +{ + dlclose(libgl); +} + +static void *get_proc(const char *proc) +{ + void *res; + + res = (void*)glXGetProcAddress((const GLubyte *) proc); + if (!res) + res = dlsym(libgl, proc); + return res; +} +#endif + +static struct { + int major, minor; +} version; + +static int parse_version(void) +{ + if (!glGetIntegerv) + return -1; + + glGetIntegerv(GL_MAJOR_VERSION, &version.major); + glGetIntegerv(GL_MINOR_VERSION, &version.minor); + + if (version.major < 3) + return -1; + return 0; +} + +static void load_procs(void); + +int gl3wInit(void) +{ + open_libgl(); + load_procs(); + close_libgl(); + return parse_version(); +} + +int gl3wIsSupported(int major, int minor) +{ + if (major < 3) + return 0; + if (version.major == major) + return version.minor >= minor; + return version.major >= major; +} + +void *gl3wGetProcAddress(const char *proc) +{ + return get_proc(proc); +} + +PFNGLCULLFACEPROC gl3wCullFace; +PFNGLFRONTFACEPROC gl3wFrontFace; +PFNGLHINTPROC gl3wHint; +PFNGLLINEWIDTHPROC gl3wLineWidth; +PFNGLPOINTSIZEPROC gl3wPointSize; +PFNGLPOLYGONMODEPROC gl3wPolygonMode; +PFNGLSCISSORPROC gl3wScissor; +PFNGLTEXPARAMETERFPROC gl3wTexParameterf; +PFNGLTEXPARAMETERFVPROC gl3wTexParameterfv; +PFNGLTEXPARAMETERIPROC gl3wTexParameteri; +PFNGLTEXPARAMETERIVPROC gl3wTexParameteriv; +PFNGLTEXIMAGE1DPROC gl3wTexImage1D; +PFNGLTEXIMAGE2DPROC gl3wTexImage2D; +PFNGLDRAWBUFFERPROC gl3wDrawBuffer; +PFNGLCLEARPROC gl3wClear; +PFNGLCLEARCOLORPROC gl3wClearColor; +PFNGLCLEARSTENCILPROC gl3wClearStencil; +PFNGLCLEARDEPTHPROC gl3wClearDepth; +PFNGLSTENCILMASKPROC gl3wStencilMask; +PFNGLCOLORMASKPROC gl3wColorMask; +PFNGLDEPTHMASKPROC gl3wDepthMask; +PFNGLDISABLEPROC gl3wDisable; +PFNGLENABLEPROC gl3wEnable; +PFNGLFINISHPROC gl3wFinish; +PFNGLFLUSHPROC gl3wFlush; +PFNGLBLENDFUNCPROC gl3wBlendFunc; +PFNGLLOGICOPPROC gl3wLogicOp; +PFNGLSTENCILFUNCPROC gl3wStencilFunc; +PFNGLSTENCILOPPROC gl3wStencilOp; +PFNGLDEPTHFUNCPROC gl3wDepthFunc; +PFNGLPIXELSTOREFPROC gl3wPixelStoref; +PFNGLPIXELSTOREIPROC gl3wPixelStorei; +PFNGLREADBUFFERPROC gl3wReadBuffer; +PFNGLREADPIXELSPROC gl3wReadPixels; +PFNGLGETBOOLEANVPROC gl3wGetBooleanv; +PFNGLGETDOUBLEVPROC gl3wGetDoublev; +PFNGLGETERRORPROC gl3wGetError; +PFNGLGETFLOATVPROC gl3wGetFloatv; +PFNGLGETINTEGERVPROC gl3wGetIntegerv; +PFNGLGETSTRINGPROC gl3wGetString; +PFNGLGETTEXIMAGEPROC gl3wGetTexImage; +PFNGLGETTEXPARAMETERFVPROC gl3wGetTexParameterfv; +PFNGLGETTEXPARAMETERIVPROC gl3wGetTexParameteriv; +PFNGLGETTEXLEVELPARAMETERFVPROC gl3wGetTexLevelParameterfv; +PFNGLGETTEXLEVELPARAMETERIVPROC gl3wGetTexLevelParameteriv; +PFNGLISENABLEDPROC gl3wIsEnabled; +PFNGLDEPTHRANGEPROC gl3wDepthRange; +PFNGLVIEWPORTPROC gl3wViewport; +PFNGLDRAWARRAYSPROC gl3wDrawArrays; +PFNGLDRAWELEMENTSPROC gl3wDrawElements; +PFNGLGETPOINTERVPROC gl3wGetPointerv; +PFNGLPOLYGONOFFSETPROC gl3wPolygonOffset; +PFNGLCOPYTEXIMAGE1DPROC gl3wCopyTexImage1D; +PFNGLCOPYTEXIMAGE2DPROC gl3wCopyTexImage2D; +PFNGLCOPYTEXSUBIMAGE1DPROC gl3wCopyTexSubImage1D; +PFNGLCOPYTEXSUBIMAGE2DPROC gl3wCopyTexSubImage2D; +PFNGLTEXSUBIMAGE1DPROC gl3wTexSubImage1D; +PFNGLTEXSUBIMAGE2DPROC gl3wTexSubImage2D; +PFNGLBINDTEXTUREPROC gl3wBindTexture; +PFNGLDELETETEXTURESPROC gl3wDeleteTextures; +PFNGLGENTEXTURESPROC gl3wGenTextures; +PFNGLISTEXTUREPROC gl3wIsTexture; +PFNGLBLENDCOLORPROC gl3wBlendColor; +PFNGLBLENDEQUATIONPROC gl3wBlendEquation; +PFNGLDRAWRANGEELEMENTSPROC gl3wDrawRangeElements; +PFNGLTEXIMAGE3DPROC gl3wTexImage3D; +PFNGLTEXSUBIMAGE3DPROC gl3wTexSubImage3D; +PFNGLCOPYTEXSUBIMAGE3DPROC gl3wCopyTexSubImage3D; +PFNGLACTIVETEXTUREPROC gl3wActiveTexture; +PFNGLSAMPLECOVERAGEPROC gl3wSampleCoverage; +PFNGLCOMPRESSEDTEXIMAGE3DPROC gl3wCompressedTexImage3D; +PFNGLCOMPRESSEDTEXIMAGE2DPROC gl3wCompressedTexImage2D; +PFNGLCOMPRESSEDTEXIMAGE1DPROC gl3wCompressedTexImage1D; +PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC gl3wCompressedTexSubImage3D; +PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC gl3wCompressedTexSubImage2D; +PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC gl3wCompressedTexSubImage1D; +PFNGLGETCOMPRESSEDTEXIMAGEPROC gl3wGetCompressedTexImage; +PFNGLBLENDFUNCSEPARATEPROC gl3wBlendFuncSeparate; +PFNGLMULTIDRAWARRAYSPROC gl3wMultiDrawArrays; +PFNGLMULTIDRAWELEMENTSPROC gl3wMultiDrawElements; +PFNGLPOINTPARAMETERFPROC gl3wPointParameterf; +PFNGLPOINTPARAMETERFVPROC gl3wPointParameterfv; +PFNGLPOINTPARAMETERIPROC gl3wPointParameteri; +PFNGLPOINTPARAMETERIVPROC gl3wPointParameteriv; +PFNGLGENQUERIESPROC gl3wGenQueries; +PFNGLDELETEQUERIESPROC gl3wDeleteQueries; +PFNGLISQUERYPROC gl3wIsQuery; +PFNGLBEGINQUERYPROC gl3wBeginQuery; +PFNGLENDQUERYPROC gl3wEndQuery; +PFNGLGETQUERYIVPROC gl3wGetQueryiv; +PFNGLGETQUERYOBJECTIVPROC gl3wGetQueryObjectiv; +PFNGLGETQUERYOBJECTUIVPROC gl3wGetQueryObjectuiv; +PFNGLBINDBUFFERPROC gl3wBindBuffer; +PFNGLDELETEBUFFERSPROC gl3wDeleteBuffers; +PFNGLGENBUFFERSPROC gl3wGenBuffers; +PFNGLISBUFFERPROC gl3wIsBuffer; +PFNGLBUFFERDATAPROC gl3wBufferData; +PFNGLBUFFERSUBDATAPROC gl3wBufferSubData; +PFNGLGETBUFFERSUBDATAPROC gl3wGetBufferSubData; +PFNGLMAPBUFFERPROC gl3wMapBuffer; +PFNGLUNMAPBUFFERPROC gl3wUnmapBuffer; +PFNGLGETBUFFERPARAMETERIVPROC gl3wGetBufferParameteriv; +PFNGLGETBUFFERPOINTERVPROC gl3wGetBufferPointerv; +PFNGLBLENDEQUATIONSEPARATEPROC gl3wBlendEquationSeparate; +PFNGLDRAWBUFFERSPROC gl3wDrawBuffers; +PFNGLSTENCILOPSEPARATEPROC gl3wStencilOpSeparate; +PFNGLSTENCILFUNCSEPARATEPROC gl3wStencilFuncSeparate; +PFNGLSTENCILMASKSEPARATEPROC gl3wStencilMaskSeparate; +PFNGLATTACHSHADERPROC gl3wAttachShader; +PFNGLBINDATTRIBLOCATIONPROC gl3wBindAttribLocation; +PFNGLCOMPILESHADERPROC gl3wCompileShader; +PFNGLCREATEPROGRAMPROC gl3wCreateProgram; +PFNGLCREATESHADERPROC gl3wCreateShader; +PFNGLDELETEPROGRAMPROC gl3wDeleteProgram; +PFNGLDELETESHADERPROC gl3wDeleteShader; +PFNGLDETACHSHADERPROC gl3wDetachShader; +PFNGLDISABLEVERTEXATTRIBARRAYPROC gl3wDisableVertexAttribArray; +PFNGLENABLEVERTEXATTRIBARRAYPROC gl3wEnableVertexAttribArray; +PFNGLGETACTIVEATTRIBPROC gl3wGetActiveAttrib; +PFNGLGETACTIVEUNIFORMPROC gl3wGetActiveUniform; +PFNGLGETATTACHEDSHADERSPROC gl3wGetAttachedShaders; +PFNGLGETATTRIBLOCATIONPROC gl3wGetAttribLocation; +PFNGLGETPROGRAMIVPROC gl3wGetProgramiv; +PFNGLGETPROGRAMINFOLOGPROC gl3wGetProgramInfoLog; +PFNGLGETSHADERIVPROC gl3wGetShaderiv; +PFNGLGETSHADERINFOLOGPROC gl3wGetShaderInfoLog; +PFNGLGETSHADERSOURCEPROC gl3wGetShaderSource; +PFNGLGETUNIFORMLOCATIONPROC gl3wGetUniformLocation; +PFNGLGETUNIFORMFVPROC gl3wGetUniformfv; +PFNGLGETUNIFORMIVPROC gl3wGetUniformiv; +PFNGLGETVERTEXATTRIBDVPROC gl3wGetVertexAttribdv; +PFNGLGETVERTEXATTRIBFVPROC gl3wGetVertexAttribfv; +PFNGLGETVERTEXATTRIBIVPROC gl3wGetVertexAttribiv; +PFNGLGETVERTEXATTRIBPOINTERVPROC gl3wGetVertexAttribPointerv; +PFNGLISPROGRAMPROC gl3wIsProgram; +PFNGLISSHADERPROC gl3wIsShader; +PFNGLLINKPROGRAMPROC gl3wLinkProgram; +PFNGLSHADERSOURCEPROC gl3wShaderSource; +PFNGLUSEPROGRAMPROC gl3wUseProgram; +PFNGLUNIFORM1FPROC gl3wUniform1f; +PFNGLUNIFORM2FPROC gl3wUniform2f; +PFNGLUNIFORM3FPROC gl3wUniform3f; +PFNGLUNIFORM4FPROC gl3wUniform4f; +PFNGLUNIFORM1IPROC gl3wUniform1i; +PFNGLUNIFORM2IPROC gl3wUniform2i; +PFNGLUNIFORM3IPROC gl3wUniform3i; +PFNGLUNIFORM4IPROC gl3wUniform4i; +PFNGLUNIFORM1FVPROC gl3wUniform1fv; +PFNGLUNIFORM2FVPROC gl3wUniform2fv; +PFNGLUNIFORM3FVPROC gl3wUniform3fv; +PFNGLUNIFORM4FVPROC gl3wUniform4fv; +PFNGLUNIFORM1IVPROC gl3wUniform1iv; +PFNGLUNIFORM2IVPROC gl3wUniform2iv; +PFNGLUNIFORM3IVPROC gl3wUniform3iv; +PFNGLUNIFORM4IVPROC gl3wUniform4iv; +PFNGLUNIFORMMATRIX2FVPROC gl3wUniformMatrix2fv; +PFNGLUNIFORMMATRIX3FVPROC gl3wUniformMatrix3fv; +PFNGLUNIFORMMATRIX4FVPROC gl3wUniformMatrix4fv; +PFNGLVALIDATEPROGRAMPROC gl3wValidateProgram; +PFNGLVERTEXATTRIB1DPROC gl3wVertexAttrib1d; +PFNGLVERTEXATTRIB1DVPROC gl3wVertexAttrib1dv; +PFNGLVERTEXATTRIB1FPROC gl3wVertexAttrib1f; +PFNGLVERTEXATTRIB1FVPROC gl3wVertexAttrib1fv; +PFNGLVERTEXATTRIB1SPROC gl3wVertexAttrib1s; +PFNGLVERTEXATTRIB1SVPROC gl3wVertexAttrib1sv; +PFNGLVERTEXATTRIB2DPROC gl3wVertexAttrib2d; +PFNGLVERTEXATTRIB2DVPROC gl3wVertexAttrib2dv; +PFNGLVERTEXATTRIB2FPROC gl3wVertexAttrib2f; +PFNGLVERTEXATTRIB2FVPROC gl3wVertexAttrib2fv; +PFNGLVERTEXATTRIB2SPROC gl3wVertexAttrib2s; +PFNGLVERTEXATTRIB2SVPROC gl3wVertexAttrib2sv; +PFNGLVERTEXATTRIB3DPROC gl3wVertexAttrib3d; +PFNGLVERTEXATTRIB3DVPROC gl3wVertexAttrib3dv; +PFNGLVERTEXATTRIB3FPROC gl3wVertexAttrib3f; +PFNGLVERTEXATTRIB3FVPROC gl3wVertexAttrib3fv; +PFNGLVERTEXATTRIB3SPROC gl3wVertexAttrib3s; +PFNGLVERTEXATTRIB3SVPROC gl3wVertexAttrib3sv; +PFNGLVERTEXATTRIB4NBVPROC gl3wVertexAttrib4Nbv; +PFNGLVERTEXATTRIB4NIVPROC gl3wVertexAttrib4Niv; +PFNGLVERTEXATTRIB4NSVPROC gl3wVertexAttrib4Nsv; +PFNGLVERTEXATTRIB4NUBPROC gl3wVertexAttrib4Nub; +PFNGLVERTEXATTRIB4NUBVPROC gl3wVertexAttrib4Nubv; +PFNGLVERTEXATTRIB4NUIVPROC gl3wVertexAttrib4Nuiv; +PFNGLVERTEXATTRIB4NUSVPROC gl3wVertexAttrib4Nusv; +PFNGLVERTEXATTRIB4BVPROC gl3wVertexAttrib4bv; +PFNGLVERTEXATTRIB4DPROC gl3wVertexAttrib4d; +PFNGLVERTEXATTRIB4DVPROC gl3wVertexAttrib4dv; +PFNGLVERTEXATTRIB4FPROC gl3wVertexAttrib4f; +PFNGLVERTEXATTRIB4FVPROC gl3wVertexAttrib4fv; +PFNGLVERTEXATTRIB4IVPROC gl3wVertexAttrib4iv; +PFNGLVERTEXATTRIB4SPROC gl3wVertexAttrib4s; +PFNGLVERTEXATTRIB4SVPROC gl3wVertexAttrib4sv; +PFNGLVERTEXATTRIB4UBVPROC gl3wVertexAttrib4ubv; +PFNGLVERTEXATTRIB4UIVPROC gl3wVertexAttrib4uiv; +PFNGLVERTEXATTRIB4USVPROC gl3wVertexAttrib4usv; +PFNGLVERTEXATTRIBPOINTERPROC gl3wVertexAttribPointer; +PFNGLUNIFORMMATRIX2X3FVPROC gl3wUniformMatrix2x3fv; +PFNGLUNIFORMMATRIX3X2FVPROC gl3wUniformMatrix3x2fv; +PFNGLUNIFORMMATRIX2X4FVPROC gl3wUniformMatrix2x4fv; +PFNGLUNIFORMMATRIX4X2FVPROC gl3wUniformMatrix4x2fv; +PFNGLUNIFORMMATRIX3X4FVPROC gl3wUniformMatrix3x4fv; +PFNGLUNIFORMMATRIX4X3FVPROC gl3wUniformMatrix4x3fv; +PFNGLCOLORMASKIPROC gl3wColorMaski; +PFNGLGETBOOLEANI_VPROC gl3wGetBooleani_v; +PFNGLGETINTEGERI_VPROC gl3wGetIntegeri_v; +PFNGLENABLEIPROC gl3wEnablei; +PFNGLDISABLEIPROC gl3wDisablei; +PFNGLISENABLEDIPROC gl3wIsEnabledi; +PFNGLBEGINTRANSFORMFEEDBACKPROC gl3wBeginTransformFeedback; +PFNGLENDTRANSFORMFEEDBACKPROC gl3wEndTransformFeedback; +PFNGLBINDBUFFERRANGEPROC gl3wBindBufferRange; +PFNGLBINDBUFFERBASEPROC gl3wBindBufferBase; +PFNGLTRANSFORMFEEDBACKVARYINGSPROC gl3wTransformFeedbackVaryings; +PFNGLGETTRANSFORMFEEDBACKVARYINGPROC gl3wGetTransformFeedbackVarying; +PFNGLCLAMPCOLORPROC gl3wClampColor; +PFNGLBEGINCONDITIONALRENDERPROC gl3wBeginConditionalRender; +PFNGLENDCONDITIONALRENDERPROC gl3wEndConditionalRender; +PFNGLVERTEXATTRIBIPOINTERPROC gl3wVertexAttribIPointer; +PFNGLGETVERTEXATTRIBIIVPROC gl3wGetVertexAttribIiv; +PFNGLGETVERTEXATTRIBIUIVPROC gl3wGetVertexAttribIuiv; +PFNGLVERTEXATTRIBI1IPROC gl3wVertexAttribI1i; +PFNGLVERTEXATTRIBI2IPROC gl3wVertexAttribI2i; +PFNGLVERTEXATTRIBI3IPROC gl3wVertexAttribI3i; +PFNGLVERTEXATTRIBI4IPROC gl3wVertexAttribI4i; +PFNGLVERTEXATTRIBI1UIPROC gl3wVertexAttribI1ui; +PFNGLVERTEXATTRIBI2UIPROC gl3wVertexAttribI2ui; +PFNGLVERTEXATTRIBI3UIPROC gl3wVertexAttribI3ui; +PFNGLVERTEXATTRIBI4UIPROC gl3wVertexAttribI4ui; +PFNGLVERTEXATTRIBI1IVPROC gl3wVertexAttribI1iv; +PFNGLVERTEXATTRIBI2IVPROC gl3wVertexAttribI2iv; +PFNGLVERTEXATTRIBI3IVPROC gl3wVertexAttribI3iv; +PFNGLVERTEXATTRIBI4IVPROC gl3wVertexAttribI4iv; +PFNGLVERTEXATTRIBI1UIVPROC gl3wVertexAttribI1uiv; +PFNGLVERTEXATTRIBI2UIVPROC gl3wVertexAttribI2uiv; +PFNGLVERTEXATTRIBI3UIVPROC gl3wVertexAttribI3uiv; +PFNGLVERTEXATTRIBI4UIVPROC gl3wVertexAttribI4uiv; +PFNGLVERTEXATTRIBI4BVPROC gl3wVertexAttribI4bv; +PFNGLVERTEXATTRIBI4SVPROC gl3wVertexAttribI4sv; +PFNGLVERTEXATTRIBI4UBVPROC gl3wVertexAttribI4ubv; +PFNGLVERTEXATTRIBI4USVPROC gl3wVertexAttribI4usv; +PFNGLGETUNIFORMUIVPROC gl3wGetUniformuiv; +PFNGLBINDFRAGDATALOCATIONPROC gl3wBindFragDataLocation; +PFNGLGETFRAGDATALOCATIONPROC gl3wGetFragDataLocation; +PFNGLUNIFORM1UIPROC gl3wUniform1ui; +PFNGLUNIFORM2UIPROC gl3wUniform2ui; +PFNGLUNIFORM3UIPROC gl3wUniform3ui; +PFNGLUNIFORM4UIPROC gl3wUniform4ui; +PFNGLUNIFORM1UIVPROC gl3wUniform1uiv; +PFNGLUNIFORM2UIVPROC gl3wUniform2uiv; +PFNGLUNIFORM3UIVPROC gl3wUniform3uiv; +PFNGLUNIFORM4UIVPROC gl3wUniform4uiv; +PFNGLTEXPARAMETERIIVPROC gl3wTexParameterIiv; +PFNGLTEXPARAMETERIUIVPROC gl3wTexParameterIuiv; +PFNGLGETTEXPARAMETERIIVPROC gl3wGetTexParameterIiv; +PFNGLGETTEXPARAMETERIUIVPROC gl3wGetTexParameterIuiv; +PFNGLCLEARBUFFERIVPROC gl3wClearBufferiv; +PFNGLCLEARBUFFERUIVPROC gl3wClearBufferuiv; +PFNGLCLEARBUFFERFVPROC gl3wClearBufferfv; +PFNGLCLEARBUFFERFIPROC gl3wClearBufferfi; +PFNGLGETSTRINGIPROC gl3wGetStringi; +PFNGLDRAWARRAYSINSTANCEDPROC gl3wDrawArraysInstanced; +PFNGLDRAWELEMENTSINSTANCEDPROC gl3wDrawElementsInstanced; +PFNGLTEXBUFFERPROC gl3wTexBuffer; +PFNGLPRIMITIVERESTARTINDEXPROC gl3wPrimitiveRestartIndex; +PFNGLGETINTEGER64I_VPROC gl3wGetInteger64i_v; +PFNGLGETBUFFERPARAMETERI64VPROC gl3wGetBufferParameteri64v; +PFNGLFRAMEBUFFERTEXTUREPROC gl3wFramebufferTexture; +PFNGLVERTEXATTRIBDIVISORPROC gl3wVertexAttribDivisor; +PFNGLMINSAMPLESHADINGPROC gl3wMinSampleShading; +PFNGLBLENDEQUATIONIPROC gl3wBlendEquationi; +PFNGLBLENDEQUATIONSEPARATEIPROC gl3wBlendEquationSeparatei; +PFNGLBLENDFUNCIPROC gl3wBlendFunci; +PFNGLBLENDFUNCSEPARATEIPROC gl3wBlendFuncSeparatei; +PFNGLISRENDERBUFFERPROC gl3wIsRenderbuffer; +PFNGLBINDRENDERBUFFERPROC gl3wBindRenderbuffer; +PFNGLDELETERENDERBUFFERSPROC gl3wDeleteRenderbuffers; +PFNGLGENRENDERBUFFERSPROC gl3wGenRenderbuffers; +PFNGLRENDERBUFFERSTORAGEPROC gl3wRenderbufferStorage; +PFNGLGETRENDERBUFFERPARAMETERIVPROC gl3wGetRenderbufferParameteriv; +PFNGLISFRAMEBUFFERPROC gl3wIsFramebuffer; +PFNGLBINDFRAMEBUFFERPROC gl3wBindFramebuffer; +PFNGLDELETEFRAMEBUFFERSPROC gl3wDeleteFramebuffers; +PFNGLGENFRAMEBUFFERSPROC gl3wGenFramebuffers; +PFNGLCHECKFRAMEBUFFERSTATUSPROC gl3wCheckFramebufferStatus; +PFNGLFRAMEBUFFERTEXTURE1DPROC gl3wFramebufferTexture1D; +PFNGLFRAMEBUFFERTEXTURE2DPROC gl3wFramebufferTexture2D; +PFNGLFRAMEBUFFERTEXTURE3DPROC gl3wFramebufferTexture3D; +PFNGLFRAMEBUFFERRENDERBUFFERPROC gl3wFramebufferRenderbuffer; +PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC gl3wGetFramebufferAttachmentParameteriv; +PFNGLGENERATEMIPMAPPROC gl3wGenerateMipmap; +PFNGLBLITFRAMEBUFFERPROC gl3wBlitFramebuffer; +PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC gl3wRenderbufferStorageMultisample; +PFNGLFRAMEBUFFERTEXTURELAYERPROC gl3wFramebufferTextureLayer; +PFNGLMAPBUFFERRANGEPROC gl3wMapBufferRange; +PFNGLFLUSHMAPPEDBUFFERRANGEPROC gl3wFlushMappedBufferRange; +PFNGLBINDVERTEXARRAYPROC gl3wBindVertexArray; +PFNGLDELETEVERTEXARRAYSPROC gl3wDeleteVertexArrays; +PFNGLGENVERTEXARRAYSPROC gl3wGenVertexArrays; +PFNGLISVERTEXARRAYPROC gl3wIsVertexArray; +PFNGLGETUNIFORMINDICESPROC gl3wGetUniformIndices; +PFNGLGETACTIVEUNIFORMSIVPROC gl3wGetActiveUniformsiv; +PFNGLGETACTIVEUNIFORMNAMEPROC gl3wGetActiveUniformName; +PFNGLGETUNIFORMBLOCKINDEXPROC gl3wGetUniformBlockIndex; +PFNGLGETACTIVEUNIFORMBLOCKIVPROC gl3wGetActiveUniformBlockiv; +PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC gl3wGetActiveUniformBlockName; +PFNGLUNIFORMBLOCKBINDINGPROC gl3wUniformBlockBinding; +PFNGLCOPYBUFFERSUBDATAPROC gl3wCopyBufferSubData; +PFNGLDRAWELEMENTSBASEVERTEXPROC gl3wDrawElementsBaseVertex; +PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC gl3wDrawRangeElementsBaseVertex; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC gl3wDrawElementsInstancedBaseVertex; +PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC gl3wMultiDrawElementsBaseVertex; +PFNGLPROVOKINGVERTEXPROC gl3wProvokingVertex; +PFNGLFENCESYNCPROC gl3wFenceSync; +PFNGLISSYNCPROC gl3wIsSync; +PFNGLDELETESYNCPROC gl3wDeleteSync; +PFNGLCLIENTWAITSYNCPROC gl3wClientWaitSync; +PFNGLWAITSYNCPROC gl3wWaitSync; +PFNGLGETINTEGER64VPROC gl3wGetInteger64v; +PFNGLGETSYNCIVPROC gl3wGetSynciv; +PFNGLTEXIMAGE2DMULTISAMPLEPROC gl3wTexImage2DMultisample; +PFNGLTEXIMAGE3DMULTISAMPLEPROC gl3wTexImage3DMultisample; +PFNGLGETMULTISAMPLEFVPROC gl3wGetMultisamplefv; +PFNGLSAMPLEMASKIPROC gl3wSampleMaski; +PFNGLBLENDEQUATIONIARBPROC gl3wBlendEquationiARB; +PFNGLBLENDEQUATIONSEPARATEIARBPROC gl3wBlendEquationSeparateiARB; +PFNGLBLENDFUNCIARBPROC gl3wBlendFunciARB; +PFNGLBLENDFUNCSEPARATEIARBPROC gl3wBlendFuncSeparateiARB; +PFNGLMINSAMPLESHADINGARBPROC gl3wMinSampleShadingARB; +PFNGLNAMEDSTRINGARBPROC gl3wNamedStringARB; +PFNGLDELETENAMEDSTRINGARBPROC gl3wDeleteNamedStringARB; +PFNGLCOMPILESHADERINCLUDEARBPROC gl3wCompileShaderIncludeARB; +PFNGLISNAMEDSTRINGARBPROC gl3wIsNamedStringARB; +PFNGLGETNAMEDSTRINGARBPROC gl3wGetNamedStringARB; +PFNGLGETNAMEDSTRINGIVARBPROC gl3wGetNamedStringivARB; +PFNGLBINDFRAGDATALOCATIONINDEXEDPROC gl3wBindFragDataLocationIndexed; +PFNGLGETFRAGDATAINDEXPROC gl3wGetFragDataIndex; +PFNGLGENSAMPLERSPROC gl3wGenSamplers; +PFNGLDELETESAMPLERSPROC gl3wDeleteSamplers; +PFNGLISSAMPLERPROC gl3wIsSampler; +PFNGLBINDSAMPLERPROC gl3wBindSampler; +PFNGLSAMPLERPARAMETERIPROC gl3wSamplerParameteri; +PFNGLSAMPLERPARAMETERIVPROC gl3wSamplerParameteriv; +PFNGLSAMPLERPARAMETERFPROC gl3wSamplerParameterf; +PFNGLSAMPLERPARAMETERFVPROC gl3wSamplerParameterfv; +PFNGLSAMPLERPARAMETERIIVPROC gl3wSamplerParameterIiv; +PFNGLSAMPLERPARAMETERIUIVPROC gl3wSamplerParameterIuiv; +PFNGLGETSAMPLERPARAMETERIVPROC gl3wGetSamplerParameteriv; +PFNGLGETSAMPLERPARAMETERIIVPROC gl3wGetSamplerParameterIiv; +PFNGLGETSAMPLERPARAMETERFVPROC gl3wGetSamplerParameterfv; +PFNGLGETSAMPLERPARAMETERIUIVPROC gl3wGetSamplerParameterIuiv; +PFNGLQUERYCOUNTERPROC gl3wQueryCounter; +PFNGLGETQUERYOBJECTI64VPROC gl3wGetQueryObjecti64v; +PFNGLGETQUERYOBJECTUI64VPROC gl3wGetQueryObjectui64v; +PFNGLVERTEXP2UIPROC gl3wVertexP2ui; +PFNGLVERTEXP2UIVPROC gl3wVertexP2uiv; +PFNGLVERTEXP3UIPROC gl3wVertexP3ui; +PFNGLVERTEXP3UIVPROC gl3wVertexP3uiv; +PFNGLVERTEXP4UIPROC gl3wVertexP4ui; +PFNGLVERTEXP4UIVPROC gl3wVertexP4uiv; +PFNGLTEXCOORDP1UIPROC gl3wTexCoordP1ui; +PFNGLTEXCOORDP1UIVPROC gl3wTexCoordP1uiv; +PFNGLTEXCOORDP2UIPROC gl3wTexCoordP2ui; +PFNGLTEXCOORDP2UIVPROC gl3wTexCoordP2uiv; +PFNGLTEXCOORDP3UIPROC gl3wTexCoordP3ui; +PFNGLTEXCOORDP3UIVPROC gl3wTexCoordP3uiv; +PFNGLTEXCOORDP4UIPROC gl3wTexCoordP4ui; +PFNGLTEXCOORDP4UIVPROC gl3wTexCoordP4uiv; +PFNGLMULTITEXCOORDP1UIPROC gl3wMultiTexCoordP1ui; +PFNGLMULTITEXCOORDP1UIVPROC gl3wMultiTexCoordP1uiv; +PFNGLMULTITEXCOORDP2UIPROC gl3wMultiTexCoordP2ui; +PFNGLMULTITEXCOORDP2UIVPROC gl3wMultiTexCoordP2uiv; +PFNGLMULTITEXCOORDP3UIPROC gl3wMultiTexCoordP3ui; +PFNGLMULTITEXCOORDP3UIVPROC gl3wMultiTexCoordP3uiv; +PFNGLMULTITEXCOORDP4UIPROC gl3wMultiTexCoordP4ui; +PFNGLMULTITEXCOORDP4UIVPROC gl3wMultiTexCoordP4uiv; +PFNGLNORMALP3UIPROC gl3wNormalP3ui; +PFNGLNORMALP3UIVPROC gl3wNormalP3uiv; +PFNGLCOLORP3UIPROC gl3wColorP3ui; +PFNGLCOLORP3UIVPROC gl3wColorP3uiv; +PFNGLCOLORP4UIPROC gl3wColorP4ui; +PFNGLCOLORP4UIVPROC gl3wColorP4uiv; +PFNGLSECONDARYCOLORP3UIPROC gl3wSecondaryColorP3ui; +PFNGLSECONDARYCOLORP3UIVPROC gl3wSecondaryColorP3uiv; +PFNGLVERTEXATTRIBP1UIPROC gl3wVertexAttribP1ui; +PFNGLVERTEXATTRIBP1UIVPROC gl3wVertexAttribP1uiv; +PFNGLVERTEXATTRIBP2UIPROC gl3wVertexAttribP2ui; +PFNGLVERTEXATTRIBP2UIVPROC gl3wVertexAttribP2uiv; +PFNGLVERTEXATTRIBP3UIPROC gl3wVertexAttribP3ui; +PFNGLVERTEXATTRIBP3UIVPROC gl3wVertexAttribP3uiv; +PFNGLVERTEXATTRIBP4UIPROC gl3wVertexAttribP4ui; +PFNGLVERTEXATTRIBP4UIVPROC gl3wVertexAttribP4uiv; +PFNGLDRAWARRAYSINDIRECTPROC gl3wDrawArraysIndirect; +PFNGLDRAWELEMENTSINDIRECTPROC gl3wDrawElementsIndirect; +PFNGLUNIFORM1DPROC gl3wUniform1d; +PFNGLUNIFORM2DPROC gl3wUniform2d; +PFNGLUNIFORM3DPROC gl3wUniform3d; +PFNGLUNIFORM4DPROC gl3wUniform4d; +PFNGLUNIFORM1DVPROC gl3wUniform1dv; +PFNGLUNIFORM2DVPROC gl3wUniform2dv; +PFNGLUNIFORM3DVPROC gl3wUniform3dv; +PFNGLUNIFORM4DVPROC gl3wUniform4dv; +PFNGLUNIFORMMATRIX2DVPROC gl3wUniformMatrix2dv; +PFNGLUNIFORMMATRIX3DVPROC gl3wUniformMatrix3dv; +PFNGLUNIFORMMATRIX4DVPROC gl3wUniformMatrix4dv; +PFNGLUNIFORMMATRIX2X3DVPROC gl3wUniformMatrix2x3dv; +PFNGLUNIFORMMATRIX2X4DVPROC gl3wUniformMatrix2x4dv; +PFNGLUNIFORMMATRIX3X2DVPROC gl3wUniformMatrix3x2dv; +PFNGLUNIFORMMATRIX3X4DVPROC gl3wUniformMatrix3x4dv; +PFNGLUNIFORMMATRIX4X2DVPROC gl3wUniformMatrix4x2dv; +PFNGLUNIFORMMATRIX4X3DVPROC gl3wUniformMatrix4x3dv; +PFNGLGETUNIFORMDVPROC gl3wGetUniformdv; +PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC gl3wGetSubroutineUniformLocation; +PFNGLGETSUBROUTINEINDEXPROC gl3wGetSubroutineIndex; +PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC gl3wGetActiveSubroutineUniformiv; +PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC gl3wGetActiveSubroutineUniformName; +PFNGLGETACTIVESUBROUTINENAMEPROC gl3wGetActiveSubroutineName; +PFNGLUNIFORMSUBROUTINESUIVPROC gl3wUniformSubroutinesuiv; +PFNGLGETUNIFORMSUBROUTINEUIVPROC gl3wGetUniformSubroutineuiv; +PFNGLGETPROGRAMSTAGEIVPROC gl3wGetProgramStageiv; +PFNGLPATCHPARAMETERIPROC gl3wPatchParameteri; +PFNGLPATCHPARAMETERFVPROC gl3wPatchParameterfv; +PFNGLBINDTRANSFORMFEEDBACKPROC gl3wBindTransformFeedback; +PFNGLDELETETRANSFORMFEEDBACKSPROC gl3wDeleteTransformFeedbacks; +PFNGLGENTRANSFORMFEEDBACKSPROC gl3wGenTransformFeedbacks; +PFNGLISTRANSFORMFEEDBACKPROC gl3wIsTransformFeedback; +PFNGLPAUSETRANSFORMFEEDBACKPROC gl3wPauseTransformFeedback; +PFNGLRESUMETRANSFORMFEEDBACKPROC gl3wResumeTransformFeedback; +PFNGLDRAWTRANSFORMFEEDBACKPROC gl3wDrawTransformFeedback; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC gl3wDrawTransformFeedbackStream; +PFNGLBEGINQUERYINDEXEDPROC gl3wBeginQueryIndexed; +PFNGLENDQUERYINDEXEDPROC gl3wEndQueryIndexed; +PFNGLGETQUERYINDEXEDIVPROC gl3wGetQueryIndexediv; +PFNGLRELEASESHADERCOMPILERPROC gl3wReleaseShaderCompiler; +PFNGLSHADERBINARYPROC gl3wShaderBinary; +PFNGLGETSHADERPRECISIONFORMATPROC gl3wGetShaderPrecisionFormat; +PFNGLDEPTHRANGEFPROC gl3wDepthRangef; +PFNGLCLEARDEPTHFPROC gl3wClearDepthf; +PFNGLGETPROGRAMBINARYPROC gl3wGetProgramBinary; +PFNGLPROGRAMBINARYPROC gl3wProgramBinary; +PFNGLPROGRAMPARAMETERIPROC gl3wProgramParameteri; +PFNGLUSEPROGRAMSTAGESPROC gl3wUseProgramStages; +PFNGLACTIVESHADERPROGRAMPROC gl3wActiveShaderProgram; +PFNGLCREATESHADERPROGRAMVPROC gl3wCreateShaderProgramv; +PFNGLBINDPROGRAMPIPELINEPROC gl3wBindProgramPipeline; +PFNGLDELETEPROGRAMPIPELINESPROC gl3wDeleteProgramPipelines; +PFNGLGENPROGRAMPIPELINESPROC gl3wGenProgramPipelines; +PFNGLISPROGRAMPIPELINEPROC gl3wIsProgramPipeline; +PFNGLGETPROGRAMPIPELINEIVPROC gl3wGetProgramPipelineiv; +PFNGLPROGRAMUNIFORM1IPROC gl3wProgramUniform1i; +PFNGLPROGRAMUNIFORM1IVPROC gl3wProgramUniform1iv; +PFNGLPROGRAMUNIFORM1FPROC gl3wProgramUniform1f; +PFNGLPROGRAMUNIFORM1FVPROC gl3wProgramUniform1fv; +PFNGLPROGRAMUNIFORM1DPROC gl3wProgramUniform1d; +PFNGLPROGRAMUNIFORM1DVPROC gl3wProgramUniform1dv; +PFNGLPROGRAMUNIFORM1UIPROC gl3wProgramUniform1ui; +PFNGLPROGRAMUNIFORM1UIVPROC gl3wProgramUniform1uiv; +PFNGLPROGRAMUNIFORM2IPROC gl3wProgramUniform2i; +PFNGLPROGRAMUNIFORM2IVPROC gl3wProgramUniform2iv; +PFNGLPROGRAMUNIFORM2FPROC gl3wProgramUniform2f; +PFNGLPROGRAMUNIFORM2FVPROC gl3wProgramUniform2fv; +PFNGLPROGRAMUNIFORM2DPROC gl3wProgramUniform2d; +PFNGLPROGRAMUNIFORM2DVPROC gl3wProgramUniform2dv; +PFNGLPROGRAMUNIFORM2UIPROC gl3wProgramUniform2ui; +PFNGLPROGRAMUNIFORM2UIVPROC gl3wProgramUniform2uiv; +PFNGLPROGRAMUNIFORM3IPROC gl3wProgramUniform3i; +PFNGLPROGRAMUNIFORM3IVPROC gl3wProgramUniform3iv; +PFNGLPROGRAMUNIFORM3FPROC gl3wProgramUniform3f; +PFNGLPROGRAMUNIFORM3FVPROC gl3wProgramUniform3fv; +PFNGLPROGRAMUNIFORM3DPROC gl3wProgramUniform3d; +PFNGLPROGRAMUNIFORM3DVPROC gl3wProgramUniform3dv; +PFNGLPROGRAMUNIFORM3UIPROC gl3wProgramUniform3ui; +PFNGLPROGRAMUNIFORM3UIVPROC gl3wProgramUniform3uiv; +PFNGLPROGRAMUNIFORM4IPROC gl3wProgramUniform4i; +PFNGLPROGRAMUNIFORM4IVPROC gl3wProgramUniform4iv; +PFNGLPROGRAMUNIFORM4FPROC gl3wProgramUniform4f; +PFNGLPROGRAMUNIFORM4FVPROC gl3wProgramUniform4fv; +PFNGLPROGRAMUNIFORM4DPROC gl3wProgramUniform4d; +PFNGLPROGRAMUNIFORM4DVPROC gl3wProgramUniform4dv; +PFNGLPROGRAMUNIFORM4UIPROC gl3wProgramUniform4ui; +PFNGLPROGRAMUNIFORM4UIVPROC gl3wProgramUniform4uiv; +PFNGLPROGRAMUNIFORMMATRIX2FVPROC gl3wProgramUniformMatrix2fv; +PFNGLPROGRAMUNIFORMMATRIX3FVPROC gl3wProgramUniformMatrix3fv; +PFNGLPROGRAMUNIFORMMATRIX4FVPROC gl3wProgramUniformMatrix4fv; +PFNGLPROGRAMUNIFORMMATRIX2DVPROC gl3wProgramUniformMatrix2dv; +PFNGLPROGRAMUNIFORMMATRIX3DVPROC gl3wProgramUniformMatrix3dv; +PFNGLPROGRAMUNIFORMMATRIX4DVPROC gl3wProgramUniformMatrix4dv; +PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC gl3wProgramUniformMatrix2x3fv; +PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC gl3wProgramUniformMatrix3x2fv; +PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC gl3wProgramUniformMatrix2x4fv; +PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC gl3wProgramUniformMatrix4x2fv; +PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC gl3wProgramUniformMatrix3x4fv; +PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC gl3wProgramUniformMatrix4x3fv; +PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC gl3wProgramUniformMatrix2x3dv; +PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC gl3wProgramUniformMatrix3x2dv; +PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC gl3wProgramUniformMatrix2x4dv; +PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC gl3wProgramUniformMatrix4x2dv; +PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC gl3wProgramUniformMatrix3x4dv; +PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC gl3wProgramUniformMatrix4x3dv; +PFNGLVALIDATEPROGRAMPIPELINEPROC gl3wValidateProgramPipeline; +PFNGLGETPROGRAMPIPELINEINFOLOGPROC gl3wGetProgramPipelineInfoLog; +PFNGLVERTEXATTRIBL1DPROC gl3wVertexAttribL1d; +PFNGLVERTEXATTRIBL2DPROC gl3wVertexAttribL2d; +PFNGLVERTEXATTRIBL3DPROC gl3wVertexAttribL3d; +PFNGLVERTEXATTRIBL4DPROC gl3wVertexAttribL4d; +PFNGLVERTEXATTRIBL1DVPROC gl3wVertexAttribL1dv; +PFNGLVERTEXATTRIBL2DVPROC gl3wVertexAttribL2dv; +PFNGLVERTEXATTRIBL3DVPROC gl3wVertexAttribL3dv; +PFNGLVERTEXATTRIBL4DVPROC gl3wVertexAttribL4dv; +PFNGLVERTEXATTRIBLPOINTERPROC gl3wVertexAttribLPointer; +PFNGLGETVERTEXATTRIBLDVPROC gl3wGetVertexAttribLdv; +PFNGLVIEWPORTARRAYVPROC gl3wViewportArrayv; +PFNGLVIEWPORTINDEXEDFPROC gl3wViewportIndexedf; +PFNGLVIEWPORTINDEXEDFVPROC gl3wViewportIndexedfv; +PFNGLSCISSORARRAYVPROC gl3wScissorArrayv; +PFNGLSCISSORINDEXEDPROC gl3wScissorIndexed; +PFNGLSCISSORINDEXEDVPROC gl3wScissorIndexedv; +PFNGLDEPTHRANGEARRAYVPROC gl3wDepthRangeArrayv; +PFNGLDEPTHRANGEINDEXEDPROC gl3wDepthRangeIndexed; +PFNGLGETFLOATI_VPROC gl3wGetFloati_v; +PFNGLGETDOUBLEI_VPROC gl3wGetDoublei_v; +PFNGLCREATESYNCFROMCLEVENTARBPROC gl3wCreateSyncFromCLeventARB; +PFNGLDEBUGMESSAGECONTROLARBPROC gl3wDebugMessageControlARB; +PFNGLDEBUGMESSAGEINSERTARBPROC gl3wDebugMessageInsertARB; +PFNGLDEBUGMESSAGECALLBACKARBPROC gl3wDebugMessageCallbackARB; +PFNGLGETDEBUGMESSAGELOGARBPROC gl3wGetDebugMessageLogARB; +PFNGLGETGRAPHICSRESETSTATUSARBPROC gl3wGetGraphicsResetStatusARB; +PFNGLGETNTEXIMAGEARBPROC gl3wGetnTexImageARB; +PFNGLREADNPIXELSARBPROC gl3wReadnPixelsARB; +PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC gl3wGetnCompressedTexImageARB; +PFNGLGETNUNIFORMFVARBPROC gl3wGetnUniformfvARB; +PFNGLGETNUNIFORMIVARBPROC gl3wGetnUniformivARB; +PFNGLGETNUNIFORMUIVARBPROC gl3wGetnUniformuivARB; +PFNGLGETNUNIFORMDVARBPROC gl3wGetnUniformdvARB; +PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC gl3wDrawArraysInstancedBaseInstance; +PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC gl3wDrawElementsInstancedBaseInstance; +PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC gl3wDrawElementsInstancedBaseVertexBaseInstance; +PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC gl3wDrawTransformFeedbackInstanced; +PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC gl3wDrawTransformFeedbackStreamInstanced; +PFNGLGETINTERNALFORMATIVPROC gl3wGetInternalformativ; +PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC gl3wGetActiveAtomicCounterBufferiv; +PFNGLBINDIMAGETEXTUREPROC gl3wBindImageTexture; +PFNGLMEMORYBARRIERPROC gl3wMemoryBarrier; +PFNGLTEXSTORAGE1DPROC gl3wTexStorage1D; +PFNGLTEXSTORAGE2DPROC gl3wTexStorage2D; +PFNGLTEXSTORAGE3DPROC gl3wTexStorage3D; +PFNGLTEXTURESTORAGE1DEXTPROC gl3wTextureStorage1DEXT; +PFNGLTEXTURESTORAGE2DEXTPROC gl3wTextureStorage2DEXT; +PFNGLTEXTURESTORAGE3DEXTPROC gl3wTextureStorage3DEXT; +PFNGLDEBUGMESSAGECONTROLPROC gl3wDebugMessageControl; +PFNGLDEBUGMESSAGEINSERTPROC gl3wDebugMessageInsert; +PFNGLDEBUGMESSAGECALLBACKPROC gl3wDebugMessageCallback; +PFNGLGETDEBUGMESSAGELOGPROC gl3wGetDebugMessageLog; +PFNGLPUSHDEBUGGROUPPROC gl3wPushDebugGroup; +PFNGLPOPDEBUGGROUPPROC gl3wPopDebugGroup; +PFNGLOBJECTLABELPROC gl3wObjectLabel; +PFNGLGETOBJECTLABELPROC gl3wGetObjectLabel; +PFNGLOBJECTPTRLABELPROC gl3wObjectPtrLabel; +PFNGLGETOBJECTPTRLABELPROC gl3wGetObjectPtrLabel; +PFNGLCLEARBUFFERDATAPROC gl3wClearBufferData; +PFNGLCLEARBUFFERSUBDATAPROC gl3wClearBufferSubData; +PFNGLCLEARNAMEDBUFFERDATAEXTPROC gl3wClearNamedBufferDataEXT; +PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC gl3wClearNamedBufferSubDataEXT; +PFNGLDISPATCHCOMPUTEPROC gl3wDispatchCompute; +PFNGLDISPATCHCOMPUTEINDIRECTPROC gl3wDispatchComputeIndirect; +PFNGLCOPYIMAGESUBDATAPROC gl3wCopyImageSubData; +PFNGLTEXTUREVIEWPROC gl3wTextureView; +PFNGLBINDVERTEXBUFFERPROC gl3wBindVertexBuffer; +PFNGLVERTEXATTRIBFORMATPROC gl3wVertexAttribFormat; +PFNGLVERTEXATTRIBIFORMATPROC gl3wVertexAttribIFormat; +PFNGLVERTEXATTRIBLFORMATPROC gl3wVertexAttribLFormat; +PFNGLVERTEXATTRIBBINDINGPROC gl3wVertexAttribBinding; +PFNGLVERTEXBINDINGDIVISORPROC gl3wVertexBindingDivisor; +PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC gl3wVertexArrayBindVertexBufferEXT; +PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC gl3wVertexArrayVertexAttribFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC gl3wVertexArrayVertexAttribIFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC gl3wVertexArrayVertexAttribLFormatEXT; +PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC gl3wVertexArrayVertexAttribBindingEXT; +PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC gl3wVertexArrayVertexBindingDivisorEXT; +PFNGLFRAMEBUFFERPARAMETERIPROC gl3wFramebufferParameteri; +PFNGLGETFRAMEBUFFERPARAMETERIVPROC gl3wGetFramebufferParameteriv; +PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC gl3wNamedFramebufferParameteriEXT; +PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC gl3wGetNamedFramebufferParameterivEXT; +PFNGLGETINTERNALFORMATI64VPROC gl3wGetInternalformati64v; +PFNGLINVALIDATETEXSUBIMAGEPROC gl3wInvalidateTexSubImage; +PFNGLINVALIDATETEXIMAGEPROC gl3wInvalidateTexImage; +PFNGLINVALIDATEBUFFERSUBDATAPROC gl3wInvalidateBufferSubData; +PFNGLINVALIDATEBUFFERDATAPROC gl3wInvalidateBufferData; +PFNGLINVALIDATEFRAMEBUFFERPROC gl3wInvalidateFramebuffer; +PFNGLINVALIDATESUBFRAMEBUFFERPROC gl3wInvalidateSubFramebuffer; +PFNGLMULTIDRAWARRAYSINDIRECTPROC gl3wMultiDrawArraysIndirect; +PFNGLMULTIDRAWELEMENTSINDIRECTPROC gl3wMultiDrawElementsIndirect; +PFNGLGETPROGRAMINTERFACEIVPROC gl3wGetProgramInterfaceiv; +PFNGLGETPROGRAMRESOURCEINDEXPROC gl3wGetProgramResourceIndex; +PFNGLGETPROGRAMRESOURCENAMEPROC gl3wGetProgramResourceName; +PFNGLGETPROGRAMRESOURCEIVPROC gl3wGetProgramResourceiv; +PFNGLGETPROGRAMRESOURCELOCATIONPROC gl3wGetProgramResourceLocation; +PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC gl3wGetProgramResourceLocationIndex; +PFNGLSHADERSTORAGEBLOCKBINDINGPROC gl3wShaderStorageBlockBinding; +PFNGLTEXBUFFERRANGEPROC gl3wTexBufferRange; +PFNGLTEXTUREBUFFERRANGEEXTPROC gl3wTextureBufferRangeEXT; +PFNGLTEXSTORAGE2DMULTISAMPLEPROC gl3wTexStorage2DMultisample; +PFNGLTEXSTORAGE3DMULTISAMPLEPROC gl3wTexStorage3DMultisample; +PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC gl3wTextureStorage2DMultisampleEXT; +PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC gl3wTextureStorage3DMultisampleEXT; + +static void load_procs(void) +{ + gl3wCullFace = (PFNGLCULLFACEPROC) get_proc("glCullFace"); + gl3wFrontFace = (PFNGLFRONTFACEPROC) get_proc("glFrontFace"); + gl3wHint = (PFNGLHINTPROC) get_proc("glHint"); + gl3wLineWidth = (PFNGLLINEWIDTHPROC) get_proc("glLineWidth"); + gl3wPointSize = (PFNGLPOINTSIZEPROC) get_proc("glPointSize"); + gl3wPolygonMode = (PFNGLPOLYGONMODEPROC) get_proc("glPolygonMode"); + gl3wScissor = (PFNGLSCISSORPROC) get_proc("glScissor"); + gl3wTexParameterf = (PFNGLTEXPARAMETERFPROC) get_proc("glTexParameterf"); + gl3wTexParameterfv = (PFNGLTEXPARAMETERFVPROC) get_proc("glTexParameterfv"); + gl3wTexParameteri = (PFNGLTEXPARAMETERIPROC) get_proc("glTexParameteri"); + gl3wTexParameteriv = (PFNGLTEXPARAMETERIVPROC) get_proc("glTexParameteriv"); + gl3wTexImage1D = (PFNGLTEXIMAGE1DPROC) get_proc("glTexImage1D"); + gl3wTexImage2D = (PFNGLTEXIMAGE2DPROC) get_proc("glTexImage2D"); + gl3wDrawBuffer = (PFNGLDRAWBUFFERPROC) get_proc("glDrawBuffer"); + gl3wClear = (PFNGLCLEARPROC) get_proc("glClear"); + gl3wClearColor = (PFNGLCLEARCOLORPROC) get_proc("glClearColor"); + gl3wClearStencil = (PFNGLCLEARSTENCILPROC) get_proc("glClearStencil"); + gl3wClearDepth = (PFNGLCLEARDEPTHPROC) get_proc("glClearDepth"); + gl3wStencilMask = (PFNGLSTENCILMASKPROC) get_proc("glStencilMask"); + gl3wColorMask = (PFNGLCOLORMASKPROC) get_proc("glColorMask"); + gl3wDepthMask = (PFNGLDEPTHMASKPROC) get_proc("glDepthMask"); + gl3wDisable = (PFNGLDISABLEPROC) get_proc("glDisable"); + gl3wEnable = (PFNGLENABLEPROC) get_proc("glEnable"); + gl3wFinish = (PFNGLFINISHPROC) get_proc("glFinish"); + gl3wFlush = (PFNGLFLUSHPROC) get_proc("glFlush"); + gl3wBlendFunc = (PFNGLBLENDFUNCPROC) get_proc("glBlendFunc"); + gl3wLogicOp = (PFNGLLOGICOPPROC) get_proc("glLogicOp"); + gl3wStencilFunc = (PFNGLSTENCILFUNCPROC) get_proc("glStencilFunc"); + gl3wStencilOp = (PFNGLSTENCILOPPROC) get_proc("glStencilOp"); + gl3wDepthFunc = (PFNGLDEPTHFUNCPROC) get_proc("glDepthFunc"); + gl3wPixelStoref = (PFNGLPIXELSTOREFPROC) get_proc("glPixelStoref"); + gl3wPixelStorei = (PFNGLPIXELSTOREIPROC) get_proc("glPixelStorei"); + gl3wReadBuffer = (PFNGLREADBUFFERPROC) get_proc("glReadBuffer"); + gl3wReadPixels = (PFNGLREADPIXELSPROC) get_proc("glReadPixels"); + gl3wGetBooleanv = (PFNGLGETBOOLEANVPROC) get_proc("glGetBooleanv"); + gl3wGetDoublev = (PFNGLGETDOUBLEVPROC) get_proc("glGetDoublev"); + gl3wGetError = (PFNGLGETERRORPROC) get_proc("glGetError"); + gl3wGetFloatv = (PFNGLGETFLOATVPROC) get_proc("glGetFloatv"); + gl3wGetIntegerv = (PFNGLGETINTEGERVPROC) get_proc("glGetIntegerv"); + gl3wGetString = (PFNGLGETSTRINGPROC) get_proc("glGetString"); + gl3wGetTexImage = (PFNGLGETTEXIMAGEPROC) get_proc("glGetTexImage"); + gl3wGetTexParameterfv = (PFNGLGETTEXPARAMETERFVPROC) get_proc("glGetTexParameterfv"); + gl3wGetTexParameteriv = (PFNGLGETTEXPARAMETERIVPROC) get_proc("glGetTexParameteriv"); + gl3wGetTexLevelParameterfv = (PFNGLGETTEXLEVELPARAMETERFVPROC) get_proc("glGetTexLevelParameterfv"); + gl3wGetTexLevelParameteriv = (PFNGLGETTEXLEVELPARAMETERIVPROC) get_proc("glGetTexLevelParameteriv"); + gl3wIsEnabled = (PFNGLISENABLEDPROC) get_proc("glIsEnabled"); + gl3wDepthRange = (PFNGLDEPTHRANGEPROC) get_proc("glDepthRange"); + gl3wViewport = (PFNGLVIEWPORTPROC) get_proc("glViewport"); + gl3wDrawArrays = (PFNGLDRAWARRAYSPROC) get_proc("glDrawArrays"); + gl3wDrawElements = (PFNGLDRAWELEMENTSPROC) get_proc("glDrawElements"); + gl3wGetPointerv = (PFNGLGETPOINTERVPROC) get_proc("glGetPointerv"); + gl3wPolygonOffset = (PFNGLPOLYGONOFFSETPROC) get_proc("glPolygonOffset"); + gl3wCopyTexImage1D = (PFNGLCOPYTEXIMAGE1DPROC) get_proc("glCopyTexImage1D"); + gl3wCopyTexImage2D = (PFNGLCOPYTEXIMAGE2DPROC) get_proc("glCopyTexImage2D"); + gl3wCopyTexSubImage1D = (PFNGLCOPYTEXSUBIMAGE1DPROC) get_proc("glCopyTexSubImage1D"); + gl3wCopyTexSubImage2D = (PFNGLCOPYTEXSUBIMAGE2DPROC) get_proc("glCopyTexSubImage2D"); + gl3wTexSubImage1D = (PFNGLTEXSUBIMAGE1DPROC) get_proc("glTexSubImage1D"); + gl3wTexSubImage2D = (PFNGLTEXSUBIMAGE2DPROC) get_proc("glTexSubImage2D"); + gl3wBindTexture = (PFNGLBINDTEXTUREPROC) get_proc("glBindTexture"); + gl3wDeleteTextures = (PFNGLDELETETEXTURESPROC) get_proc("glDeleteTextures"); + gl3wGenTextures = (PFNGLGENTEXTURESPROC) get_proc("glGenTextures"); + gl3wIsTexture = (PFNGLISTEXTUREPROC) get_proc("glIsTexture"); + gl3wBlendColor = (PFNGLBLENDCOLORPROC) get_proc("glBlendColor"); + gl3wBlendEquation = (PFNGLBLENDEQUATIONPROC) get_proc("glBlendEquation"); + gl3wDrawRangeElements = (PFNGLDRAWRANGEELEMENTSPROC) get_proc("glDrawRangeElements"); + gl3wTexImage3D = (PFNGLTEXIMAGE3DPROC) get_proc("glTexImage3D"); + gl3wTexSubImage3D = (PFNGLTEXSUBIMAGE3DPROC) get_proc("glTexSubImage3D"); + gl3wCopyTexSubImage3D = (PFNGLCOPYTEXSUBIMAGE3DPROC) get_proc("glCopyTexSubImage3D"); + gl3wActiveTexture = (PFNGLACTIVETEXTUREPROC) get_proc("glActiveTexture"); + gl3wSampleCoverage = (PFNGLSAMPLECOVERAGEPROC) get_proc("glSampleCoverage"); + gl3wCompressedTexImage3D = (PFNGLCOMPRESSEDTEXIMAGE3DPROC) get_proc("glCompressedTexImage3D"); + gl3wCompressedTexImage2D = (PFNGLCOMPRESSEDTEXIMAGE2DPROC) get_proc("glCompressedTexImage2D"); + gl3wCompressedTexImage1D = (PFNGLCOMPRESSEDTEXIMAGE1DPROC) get_proc("glCompressedTexImage1D"); + gl3wCompressedTexSubImage3D = (PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) get_proc("glCompressedTexSubImage3D"); + gl3wCompressedTexSubImage2D = (PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC) get_proc("glCompressedTexSubImage2D"); + gl3wCompressedTexSubImage1D = (PFNGLCOMPRESSEDTEXSUBIMAGE1DPROC) get_proc("glCompressedTexSubImage1D"); + gl3wGetCompressedTexImage = (PFNGLGETCOMPRESSEDTEXIMAGEPROC) get_proc("glGetCompressedTexImage"); + gl3wBlendFuncSeparate = (PFNGLBLENDFUNCSEPARATEPROC) get_proc("glBlendFuncSeparate"); + gl3wMultiDrawArrays = (PFNGLMULTIDRAWARRAYSPROC) get_proc("glMultiDrawArrays"); + gl3wMultiDrawElements = (PFNGLMULTIDRAWELEMENTSPROC) get_proc("glMultiDrawElements"); + gl3wPointParameterf = (PFNGLPOINTPARAMETERFPROC) get_proc("glPointParameterf"); + gl3wPointParameterfv = (PFNGLPOINTPARAMETERFVPROC) get_proc("glPointParameterfv"); + gl3wPointParameteri = (PFNGLPOINTPARAMETERIPROC) get_proc("glPointParameteri"); + gl3wPointParameteriv = (PFNGLPOINTPARAMETERIVPROC) get_proc("glPointParameteriv"); + gl3wGenQueries = (PFNGLGENQUERIESPROC) get_proc("glGenQueries"); + gl3wDeleteQueries = (PFNGLDELETEQUERIESPROC) get_proc("glDeleteQueries"); + gl3wIsQuery = (PFNGLISQUERYPROC) get_proc("glIsQuery"); + gl3wBeginQuery = (PFNGLBEGINQUERYPROC) get_proc("glBeginQuery"); + gl3wEndQuery = (PFNGLENDQUERYPROC) get_proc("glEndQuery"); + gl3wGetQueryiv = (PFNGLGETQUERYIVPROC) get_proc("glGetQueryiv"); + gl3wGetQueryObjectiv = (PFNGLGETQUERYOBJECTIVPROC) get_proc("glGetQueryObjectiv"); + gl3wGetQueryObjectuiv = (PFNGLGETQUERYOBJECTUIVPROC) get_proc("glGetQueryObjectuiv"); + gl3wBindBuffer = (PFNGLBINDBUFFERPROC) get_proc("glBindBuffer"); + gl3wDeleteBuffers = (PFNGLDELETEBUFFERSPROC) get_proc("glDeleteBuffers"); + gl3wGenBuffers = (PFNGLGENBUFFERSPROC) get_proc("glGenBuffers"); + gl3wIsBuffer = (PFNGLISBUFFERPROC) get_proc("glIsBuffer"); + gl3wBufferData = (PFNGLBUFFERDATAPROC) get_proc("glBufferData"); + gl3wBufferSubData = (PFNGLBUFFERSUBDATAPROC) get_proc("glBufferSubData"); + gl3wGetBufferSubData = (PFNGLGETBUFFERSUBDATAPROC) get_proc("glGetBufferSubData"); + gl3wMapBuffer = (PFNGLMAPBUFFERPROC) get_proc("glMapBuffer"); + gl3wUnmapBuffer = (PFNGLUNMAPBUFFERPROC) get_proc("glUnmapBuffer"); + gl3wGetBufferParameteriv = (PFNGLGETBUFFERPARAMETERIVPROC) get_proc("glGetBufferParameteriv"); + gl3wGetBufferPointerv = (PFNGLGETBUFFERPOINTERVPROC) get_proc("glGetBufferPointerv"); + gl3wBlendEquationSeparate = (PFNGLBLENDEQUATIONSEPARATEPROC) get_proc("glBlendEquationSeparate"); + gl3wDrawBuffers = (PFNGLDRAWBUFFERSPROC) get_proc("glDrawBuffers"); + gl3wStencilOpSeparate = (PFNGLSTENCILOPSEPARATEPROC) get_proc("glStencilOpSeparate"); + gl3wStencilFuncSeparate = (PFNGLSTENCILFUNCSEPARATEPROC) get_proc("glStencilFuncSeparate"); + gl3wStencilMaskSeparate = (PFNGLSTENCILMASKSEPARATEPROC) get_proc("glStencilMaskSeparate"); + gl3wAttachShader = (PFNGLATTACHSHADERPROC) get_proc("glAttachShader"); + gl3wBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC) get_proc("glBindAttribLocation"); + gl3wCompileShader = (PFNGLCOMPILESHADERPROC) get_proc("glCompileShader"); + gl3wCreateProgram = (PFNGLCREATEPROGRAMPROC) get_proc("glCreateProgram"); + gl3wCreateShader = (PFNGLCREATESHADERPROC) get_proc("glCreateShader"); + gl3wDeleteProgram = (PFNGLDELETEPROGRAMPROC) get_proc("glDeleteProgram"); + gl3wDeleteShader = (PFNGLDELETESHADERPROC) get_proc("glDeleteShader"); + gl3wDetachShader = (PFNGLDETACHSHADERPROC) get_proc("glDetachShader"); + gl3wDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC) get_proc("glDisableVertexAttribArray"); + gl3wEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC) get_proc("glEnableVertexAttribArray"); + gl3wGetActiveAttrib = (PFNGLGETACTIVEATTRIBPROC) get_proc("glGetActiveAttrib"); + gl3wGetActiveUniform = (PFNGLGETACTIVEUNIFORMPROC) get_proc("glGetActiveUniform"); + gl3wGetAttachedShaders = (PFNGLGETATTACHEDSHADERSPROC) get_proc("glGetAttachedShaders"); + gl3wGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC) get_proc("glGetAttribLocation"); + gl3wGetProgramiv = (PFNGLGETPROGRAMIVPROC) get_proc("glGetProgramiv"); + gl3wGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC) get_proc("glGetProgramInfoLog"); + gl3wGetShaderiv = (PFNGLGETSHADERIVPROC) get_proc("glGetShaderiv"); + gl3wGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC) get_proc("glGetShaderInfoLog"); + gl3wGetShaderSource = (PFNGLGETSHADERSOURCEPROC) get_proc("glGetShaderSource"); + gl3wGetUniformLocation = (PFNGLGETUNIFORMLOCATIONPROC) get_proc("glGetUniformLocation"); + gl3wGetUniformfv = (PFNGLGETUNIFORMFVPROC) get_proc("glGetUniformfv"); + gl3wGetUniformiv = (PFNGLGETUNIFORMIVPROC) get_proc("glGetUniformiv"); + gl3wGetVertexAttribdv = (PFNGLGETVERTEXATTRIBDVPROC) get_proc("glGetVertexAttribdv"); + gl3wGetVertexAttribfv = (PFNGLGETVERTEXATTRIBFVPROC) get_proc("glGetVertexAttribfv"); + gl3wGetVertexAttribiv = (PFNGLGETVERTEXATTRIBIVPROC) get_proc("glGetVertexAttribiv"); + gl3wGetVertexAttribPointerv = (PFNGLGETVERTEXATTRIBPOINTERVPROC) get_proc("glGetVertexAttribPointerv"); + gl3wIsProgram = (PFNGLISPROGRAMPROC) get_proc("glIsProgram"); + gl3wIsShader = (PFNGLISSHADERPROC) get_proc("glIsShader"); + gl3wLinkProgram = (PFNGLLINKPROGRAMPROC) get_proc("glLinkProgram"); + gl3wShaderSource = (PFNGLSHADERSOURCEPROC) get_proc("glShaderSource"); + gl3wUseProgram = (PFNGLUSEPROGRAMPROC) get_proc("glUseProgram"); + gl3wUniform1f = (PFNGLUNIFORM1FPROC) get_proc("glUniform1f"); + gl3wUniform2f = (PFNGLUNIFORM2FPROC) get_proc("glUniform2f"); + gl3wUniform3f = (PFNGLUNIFORM3FPROC) get_proc("glUniform3f"); + gl3wUniform4f = (PFNGLUNIFORM4FPROC) get_proc("glUniform4f"); + gl3wUniform1i = (PFNGLUNIFORM1IPROC) get_proc("glUniform1i"); + gl3wUniform2i = (PFNGLUNIFORM2IPROC) get_proc("glUniform2i"); + gl3wUniform3i = (PFNGLUNIFORM3IPROC) get_proc("glUniform3i"); + gl3wUniform4i = (PFNGLUNIFORM4IPROC) get_proc("glUniform4i"); + gl3wUniform1fv = (PFNGLUNIFORM1FVPROC) get_proc("glUniform1fv"); + gl3wUniform2fv = (PFNGLUNIFORM2FVPROC) get_proc("glUniform2fv"); + gl3wUniform3fv = (PFNGLUNIFORM3FVPROC) get_proc("glUniform3fv"); + gl3wUniform4fv = (PFNGLUNIFORM4FVPROC) get_proc("glUniform4fv"); + gl3wUniform1iv = (PFNGLUNIFORM1IVPROC) get_proc("glUniform1iv"); + gl3wUniform2iv = (PFNGLUNIFORM2IVPROC) get_proc("glUniform2iv"); + gl3wUniform3iv = (PFNGLUNIFORM3IVPROC) get_proc("glUniform3iv"); + gl3wUniform4iv = (PFNGLUNIFORM4IVPROC) get_proc("glUniform4iv"); + gl3wUniformMatrix2fv = (PFNGLUNIFORMMATRIX2FVPROC) get_proc("glUniformMatrix2fv"); + gl3wUniformMatrix3fv = (PFNGLUNIFORMMATRIX3FVPROC) get_proc("glUniformMatrix3fv"); + gl3wUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC) get_proc("glUniformMatrix4fv"); + gl3wValidateProgram = (PFNGLVALIDATEPROGRAMPROC) get_proc("glValidateProgram"); + gl3wVertexAttrib1d = (PFNGLVERTEXATTRIB1DPROC) get_proc("glVertexAttrib1d"); + gl3wVertexAttrib1dv = (PFNGLVERTEXATTRIB1DVPROC) get_proc("glVertexAttrib1dv"); + gl3wVertexAttrib1f = (PFNGLVERTEXATTRIB1FPROC) get_proc("glVertexAttrib1f"); + gl3wVertexAttrib1fv = (PFNGLVERTEXATTRIB1FVPROC) get_proc("glVertexAttrib1fv"); + gl3wVertexAttrib1s = (PFNGLVERTEXATTRIB1SPROC) get_proc("glVertexAttrib1s"); + gl3wVertexAttrib1sv = (PFNGLVERTEXATTRIB1SVPROC) get_proc("glVertexAttrib1sv"); + gl3wVertexAttrib2d = (PFNGLVERTEXATTRIB2DPROC) get_proc("glVertexAttrib2d"); + gl3wVertexAttrib2dv = (PFNGLVERTEXATTRIB2DVPROC) get_proc("glVertexAttrib2dv"); + gl3wVertexAttrib2f = (PFNGLVERTEXATTRIB2FPROC) get_proc("glVertexAttrib2f"); + gl3wVertexAttrib2fv = (PFNGLVERTEXATTRIB2FVPROC) get_proc("glVertexAttrib2fv"); + gl3wVertexAttrib2s = (PFNGLVERTEXATTRIB2SPROC) get_proc("glVertexAttrib2s"); + gl3wVertexAttrib2sv = (PFNGLVERTEXATTRIB2SVPROC) get_proc("glVertexAttrib2sv"); + gl3wVertexAttrib3d = (PFNGLVERTEXATTRIB3DPROC) get_proc("glVertexAttrib3d"); + gl3wVertexAttrib3dv = (PFNGLVERTEXATTRIB3DVPROC) get_proc("glVertexAttrib3dv"); + gl3wVertexAttrib3f = (PFNGLVERTEXATTRIB3FPROC) get_proc("glVertexAttrib3f"); + gl3wVertexAttrib3fv = (PFNGLVERTEXATTRIB3FVPROC) get_proc("glVertexAttrib3fv"); + gl3wVertexAttrib3s = (PFNGLVERTEXATTRIB3SPROC) get_proc("glVertexAttrib3s"); + gl3wVertexAttrib3sv = (PFNGLVERTEXATTRIB3SVPROC) get_proc("glVertexAttrib3sv"); + gl3wVertexAttrib4Nbv = (PFNGLVERTEXATTRIB4NBVPROC) get_proc("glVertexAttrib4Nbv"); + gl3wVertexAttrib4Niv = (PFNGLVERTEXATTRIB4NIVPROC) get_proc("glVertexAttrib4Niv"); + gl3wVertexAttrib4Nsv = (PFNGLVERTEXATTRIB4NSVPROC) get_proc("glVertexAttrib4Nsv"); + gl3wVertexAttrib4Nub = (PFNGLVERTEXATTRIB4NUBPROC) get_proc("glVertexAttrib4Nub"); + gl3wVertexAttrib4Nubv = (PFNGLVERTEXATTRIB4NUBVPROC) get_proc("glVertexAttrib4Nubv"); + gl3wVertexAttrib4Nuiv = (PFNGLVERTEXATTRIB4NUIVPROC) get_proc("glVertexAttrib4Nuiv"); + gl3wVertexAttrib4Nusv = (PFNGLVERTEXATTRIB4NUSVPROC) get_proc("glVertexAttrib4Nusv"); + gl3wVertexAttrib4bv = (PFNGLVERTEXATTRIB4BVPROC) get_proc("glVertexAttrib4bv"); + gl3wVertexAttrib4d = (PFNGLVERTEXATTRIB4DPROC) get_proc("glVertexAttrib4d"); + gl3wVertexAttrib4dv = (PFNGLVERTEXATTRIB4DVPROC) get_proc("glVertexAttrib4dv"); + gl3wVertexAttrib4f = (PFNGLVERTEXATTRIB4FPROC) get_proc("glVertexAttrib4f"); + gl3wVertexAttrib4fv = (PFNGLVERTEXATTRIB4FVPROC) get_proc("glVertexAttrib4fv"); + gl3wVertexAttrib4iv = (PFNGLVERTEXATTRIB4IVPROC) get_proc("glVertexAttrib4iv"); + gl3wVertexAttrib4s = (PFNGLVERTEXATTRIB4SPROC) get_proc("glVertexAttrib4s"); + gl3wVertexAttrib4sv = (PFNGLVERTEXATTRIB4SVPROC) get_proc("glVertexAttrib4sv"); + gl3wVertexAttrib4ubv = (PFNGLVERTEXATTRIB4UBVPROC) get_proc("glVertexAttrib4ubv"); + gl3wVertexAttrib4uiv = (PFNGLVERTEXATTRIB4UIVPROC) get_proc("glVertexAttrib4uiv"); + gl3wVertexAttrib4usv = (PFNGLVERTEXATTRIB4USVPROC) get_proc("glVertexAttrib4usv"); + gl3wVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC) get_proc("glVertexAttribPointer"); + gl3wUniformMatrix2x3fv = (PFNGLUNIFORMMATRIX2X3FVPROC) get_proc("glUniformMatrix2x3fv"); + gl3wUniformMatrix3x2fv = (PFNGLUNIFORMMATRIX3X2FVPROC) get_proc("glUniformMatrix3x2fv"); + gl3wUniformMatrix2x4fv = (PFNGLUNIFORMMATRIX2X4FVPROC) get_proc("glUniformMatrix2x4fv"); + gl3wUniformMatrix4x2fv = (PFNGLUNIFORMMATRIX4X2FVPROC) get_proc("glUniformMatrix4x2fv"); + gl3wUniformMatrix3x4fv = (PFNGLUNIFORMMATRIX3X4FVPROC) get_proc("glUniformMatrix3x4fv"); + gl3wUniformMatrix4x3fv = (PFNGLUNIFORMMATRIX4X3FVPROC) get_proc("glUniformMatrix4x3fv"); + gl3wColorMaski = (PFNGLCOLORMASKIPROC) get_proc("glColorMaski"); + gl3wGetBooleani_v = (PFNGLGETBOOLEANI_VPROC) get_proc("glGetBooleani_v"); + gl3wGetIntegeri_v = (PFNGLGETINTEGERI_VPROC) get_proc("glGetIntegeri_v"); + gl3wEnablei = (PFNGLENABLEIPROC) get_proc("glEnablei"); + gl3wDisablei = (PFNGLDISABLEIPROC) get_proc("glDisablei"); + gl3wIsEnabledi = (PFNGLISENABLEDIPROC) get_proc("glIsEnabledi"); + gl3wBeginTransformFeedback = (PFNGLBEGINTRANSFORMFEEDBACKPROC) get_proc("glBeginTransformFeedback"); + gl3wEndTransformFeedback = (PFNGLENDTRANSFORMFEEDBACKPROC) get_proc("glEndTransformFeedback"); + gl3wBindBufferRange = (PFNGLBINDBUFFERRANGEPROC) get_proc("glBindBufferRange"); + gl3wBindBufferBase = (PFNGLBINDBUFFERBASEPROC) get_proc("glBindBufferBase"); + gl3wTransformFeedbackVaryings = (PFNGLTRANSFORMFEEDBACKVARYINGSPROC) get_proc("glTransformFeedbackVaryings"); + gl3wGetTransformFeedbackVarying = (PFNGLGETTRANSFORMFEEDBACKVARYINGPROC) get_proc("glGetTransformFeedbackVarying"); + gl3wClampColor = (PFNGLCLAMPCOLORPROC) get_proc("glClampColor"); + gl3wBeginConditionalRender = (PFNGLBEGINCONDITIONALRENDERPROC) get_proc("glBeginConditionalRender"); + gl3wEndConditionalRender = (PFNGLENDCONDITIONALRENDERPROC) get_proc("glEndConditionalRender"); + gl3wVertexAttribIPointer = (PFNGLVERTEXATTRIBIPOINTERPROC) get_proc("glVertexAttribIPointer"); + gl3wGetVertexAttribIiv = (PFNGLGETVERTEXATTRIBIIVPROC) get_proc("glGetVertexAttribIiv"); + gl3wGetVertexAttribIuiv = (PFNGLGETVERTEXATTRIBIUIVPROC) get_proc("glGetVertexAttribIuiv"); + gl3wVertexAttribI1i = (PFNGLVERTEXATTRIBI1IPROC) get_proc("glVertexAttribI1i"); + gl3wVertexAttribI2i = (PFNGLVERTEXATTRIBI2IPROC) get_proc("glVertexAttribI2i"); + gl3wVertexAttribI3i = (PFNGLVERTEXATTRIBI3IPROC) get_proc("glVertexAttribI3i"); + gl3wVertexAttribI4i = (PFNGLVERTEXATTRIBI4IPROC) get_proc("glVertexAttribI4i"); + gl3wVertexAttribI1ui = (PFNGLVERTEXATTRIBI1UIPROC) get_proc("glVertexAttribI1ui"); + gl3wVertexAttribI2ui = (PFNGLVERTEXATTRIBI2UIPROC) get_proc("glVertexAttribI2ui"); + gl3wVertexAttribI3ui = (PFNGLVERTEXATTRIBI3UIPROC) get_proc("glVertexAttribI3ui"); + gl3wVertexAttribI4ui = (PFNGLVERTEXATTRIBI4UIPROC) get_proc("glVertexAttribI4ui"); + gl3wVertexAttribI1iv = (PFNGLVERTEXATTRIBI1IVPROC) get_proc("glVertexAttribI1iv"); + gl3wVertexAttribI2iv = (PFNGLVERTEXATTRIBI2IVPROC) get_proc("glVertexAttribI2iv"); + gl3wVertexAttribI3iv = (PFNGLVERTEXATTRIBI3IVPROC) get_proc("glVertexAttribI3iv"); + gl3wVertexAttribI4iv = (PFNGLVERTEXATTRIBI4IVPROC) get_proc("glVertexAttribI4iv"); + gl3wVertexAttribI1uiv = (PFNGLVERTEXATTRIBI1UIVPROC) get_proc("glVertexAttribI1uiv"); + gl3wVertexAttribI2uiv = (PFNGLVERTEXATTRIBI2UIVPROC) get_proc("glVertexAttribI2uiv"); + gl3wVertexAttribI3uiv = (PFNGLVERTEXATTRIBI3UIVPROC) get_proc("glVertexAttribI3uiv"); + gl3wVertexAttribI4uiv = (PFNGLVERTEXATTRIBI4UIVPROC) get_proc("glVertexAttribI4uiv"); + gl3wVertexAttribI4bv = (PFNGLVERTEXATTRIBI4BVPROC) get_proc("glVertexAttribI4bv"); + gl3wVertexAttribI4sv = (PFNGLVERTEXATTRIBI4SVPROC) get_proc("glVertexAttribI4sv"); + gl3wVertexAttribI4ubv = (PFNGLVERTEXATTRIBI4UBVPROC) get_proc("glVertexAttribI4ubv"); + gl3wVertexAttribI4usv = (PFNGLVERTEXATTRIBI4USVPROC) get_proc("glVertexAttribI4usv"); + gl3wGetUniformuiv = (PFNGLGETUNIFORMUIVPROC) get_proc("glGetUniformuiv"); + gl3wBindFragDataLocation = (PFNGLBINDFRAGDATALOCATIONPROC) get_proc("glBindFragDataLocation"); + gl3wGetFragDataLocation = (PFNGLGETFRAGDATALOCATIONPROC) get_proc("glGetFragDataLocation"); + gl3wUniform1ui = (PFNGLUNIFORM1UIPROC) get_proc("glUniform1ui"); + gl3wUniform2ui = (PFNGLUNIFORM2UIPROC) get_proc("glUniform2ui"); + gl3wUniform3ui = (PFNGLUNIFORM3UIPROC) get_proc("glUniform3ui"); + gl3wUniform4ui = (PFNGLUNIFORM4UIPROC) get_proc("glUniform4ui"); + gl3wUniform1uiv = (PFNGLUNIFORM1UIVPROC) get_proc("glUniform1uiv"); + gl3wUniform2uiv = (PFNGLUNIFORM2UIVPROC) get_proc("glUniform2uiv"); + gl3wUniform3uiv = (PFNGLUNIFORM3UIVPROC) get_proc("glUniform3uiv"); + gl3wUniform4uiv = (PFNGLUNIFORM4UIVPROC) get_proc("glUniform4uiv"); + gl3wTexParameterIiv = (PFNGLTEXPARAMETERIIVPROC) get_proc("glTexParameterIiv"); + gl3wTexParameterIuiv = (PFNGLTEXPARAMETERIUIVPROC) get_proc("glTexParameterIuiv"); + gl3wGetTexParameterIiv = (PFNGLGETTEXPARAMETERIIVPROC) get_proc("glGetTexParameterIiv"); + gl3wGetTexParameterIuiv = (PFNGLGETTEXPARAMETERIUIVPROC) get_proc("glGetTexParameterIuiv"); + gl3wClearBufferiv = (PFNGLCLEARBUFFERIVPROC) get_proc("glClearBufferiv"); + gl3wClearBufferuiv = (PFNGLCLEARBUFFERUIVPROC) get_proc("glClearBufferuiv"); + gl3wClearBufferfv = (PFNGLCLEARBUFFERFVPROC) get_proc("glClearBufferfv"); + gl3wClearBufferfi = (PFNGLCLEARBUFFERFIPROC) get_proc("glClearBufferfi"); + gl3wGetStringi = (PFNGLGETSTRINGIPROC) get_proc("glGetStringi"); + gl3wDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC) get_proc("glDrawArraysInstanced"); + gl3wDrawElementsInstanced = (PFNGLDRAWELEMENTSINSTANCEDPROC) get_proc("glDrawElementsInstanced"); + gl3wTexBuffer = (PFNGLTEXBUFFERPROC) get_proc("glTexBuffer"); + gl3wPrimitiveRestartIndex = (PFNGLPRIMITIVERESTARTINDEXPROC) get_proc("glPrimitiveRestartIndex"); + gl3wGetInteger64i_v = (PFNGLGETINTEGER64I_VPROC) get_proc("glGetInteger64i_v"); + gl3wGetBufferParameteri64v = (PFNGLGETBUFFERPARAMETERI64VPROC) get_proc("glGetBufferParameteri64v"); + gl3wFramebufferTexture = (PFNGLFRAMEBUFFERTEXTUREPROC) get_proc("glFramebufferTexture"); + gl3wVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC) get_proc("glVertexAttribDivisor"); + gl3wMinSampleShading = (PFNGLMINSAMPLESHADINGPROC) get_proc("glMinSampleShading"); + gl3wBlendEquationi = (PFNGLBLENDEQUATIONIPROC) get_proc("glBlendEquationi"); + gl3wBlendEquationSeparatei = (PFNGLBLENDEQUATIONSEPARATEIPROC) get_proc("glBlendEquationSeparatei"); + gl3wBlendFunci = (PFNGLBLENDFUNCIPROC) get_proc("glBlendFunci"); + gl3wBlendFuncSeparatei = (PFNGLBLENDFUNCSEPARATEIPROC) get_proc("glBlendFuncSeparatei"); + gl3wIsRenderbuffer = (PFNGLISRENDERBUFFERPROC) get_proc("glIsRenderbuffer"); + gl3wBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC) get_proc("glBindRenderbuffer"); + gl3wDeleteRenderbuffers = (PFNGLDELETERENDERBUFFERSPROC) get_proc("glDeleteRenderbuffers"); + gl3wGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC) get_proc("glGenRenderbuffers"); + gl3wRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) get_proc("glRenderbufferStorage"); + gl3wGetRenderbufferParameteriv = (PFNGLGETRENDERBUFFERPARAMETERIVPROC) get_proc("glGetRenderbufferParameteriv"); + gl3wIsFramebuffer = (PFNGLISFRAMEBUFFERPROC) get_proc("glIsFramebuffer"); + gl3wBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC) get_proc("glBindFramebuffer"); + gl3wDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSPROC) get_proc("glDeleteFramebuffers"); + gl3wGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC) get_proc("glGenFramebuffers"); + gl3wCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC) get_proc("glCheckFramebufferStatus"); + gl3wFramebufferTexture1D = (PFNGLFRAMEBUFFERTEXTURE1DPROC) get_proc("glFramebufferTexture1D"); + gl3wFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DPROC) get_proc("glFramebufferTexture2D"); + gl3wFramebufferTexture3D = (PFNGLFRAMEBUFFERTEXTURE3DPROC) get_proc("glFramebufferTexture3D"); + gl3wFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC) get_proc("glFramebufferRenderbuffer"); + gl3wGetFramebufferAttachmentParameteriv = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC) get_proc("glGetFramebufferAttachmentParameteriv"); + gl3wGenerateMipmap = (PFNGLGENERATEMIPMAPPROC) get_proc("glGenerateMipmap"); + gl3wBlitFramebuffer = (PFNGLBLITFRAMEBUFFERPROC) get_proc("glBlitFramebuffer"); + gl3wRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) get_proc("glRenderbufferStorageMultisample"); + gl3wFramebufferTextureLayer = (PFNGLFRAMEBUFFERTEXTURELAYERPROC) get_proc("glFramebufferTextureLayer"); + gl3wMapBufferRange = (PFNGLMAPBUFFERRANGEPROC) get_proc("glMapBufferRange"); + gl3wFlushMappedBufferRange = (PFNGLFLUSHMAPPEDBUFFERRANGEPROC) get_proc("glFlushMappedBufferRange"); + gl3wBindVertexArray = (PFNGLBINDVERTEXARRAYPROC) get_proc("glBindVertexArray"); + gl3wDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC) get_proc("glDeleteVertexArrays"); + gl3wGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC) get_proc("glGenVertexArrays"); + gl3wIsVertexArray = (PFNGLISVERTEXARRAYPROC) get_proc("glIsVertexArray"); + gl3wGetUniformIndices = (PFNGLGETUNIFORMINDICESPROC) get_proc("glGetUniformIndices"); + gl3wGetActiveUniformsiv = (PFNGLGETACTIVEUNIFORMSIVPROC) get_proc("glGetActiveUniformsiv"); + gl3wGetActiveUniformName = (PFNGLGETACTIVEUNIFORMNAMEPROC) get_proc("glGetActiveUniformName"); + gl3wGetUniformBlockIndex = (PFNGLGETUNIFORMBLOCKINDEXPROC) get_proc("glGetUniformBlockIndex"); + gl3wGetActiveUniformBlockiv = (PFNGLGETACTIVEUNIFORMBLOCKIVPROC) get_proc("glGetActiveUniformBlockiv"); + gl3wGetActiveUniformBlockName = (PFNGLGETACTIVEUNIFORMBLOCKNAMEPROC) get_proc("glGetActiveUniformBlockName"); + gl3wUniformBlockBinding = (PFNGLUNIFORMBLOCKBINDINGPROC) get_proc("glUniformBlockBinding"); + gl3wCopyBufferSubData = (PFNGLCOPYBUFFERSUBDATAPROC) get_proc("glCopyBufferSubData"); + gl3wDrawElementsBaseVertex = (PFNGLDRAWELEMENTSBASEVERTEXPROC) get_proc("glDrawElementsBaseVertex"); + gl3wDrawRangeElementsBaseVertex = (PFNGLDRAWRANGEELEMENTSBASEVERTEXPROC) get_proc("glDrawRangeElementsBaseVertex"); + gl3wDrawElementsInstancedBaseVertex = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXPROC) get_proc("glDrawElementsInstancedBaseVertex"); + gl3wMultiDrawElementsBaseVertex = (PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC) get_proc("glMultiDrawElementsBaseVertex"); + gl3wProvokingVertex = (PFNGLPROVOKINGVERTEXPROC) get_proc("glProvokingVertex"); + gl3wFenceSync = (PFNGLFENCESYNCPROC) get_proc("glFenceSync"); + gl3wIsSync = (PFNGLISSYNCPROC) get_proc("glIsSync"); + gl3wDeleteSync = (PFNGLDELETESYNCPROC) get_proc("glDeleteSync"); + gl3wClientWaitSync = (PFNGLCLIENTWAITSYNCPROC) get_proc("glClientWaitSync"); + gl3wWaitSync = (PFNGLWAITSYNCPROC) get_proc("glWaitSync"); + gl3wGetInteger64v = (PFNGLGETINTEGER64VPROC) get_proc("glGetInteger64v"); + gl3wGetSynciv = (PFNGLGETSYNCIVPROC) get_proc("glGetSynciv"); + gl3wTexImage2DMultisample = (PFNGLTEXIMAGE2DMULTISAMPLEPROC) get_proc("glTexImage2DMultisample"); + gl3wTexImage3DMultisample = (PFNGLTEXIMAGE3DMULTISAMPLEPROC) get_proc("glTexImage3DMultisample"); + gl3wGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC) get_proc("glGetMultisamplefv"); + gl3wSampleMaski = (PFNGLSAMPLEMASKIPROC) get_proc("glSampleMaski"); + gl3wBlendEquationiARB = (PFNGLBLENDEQUATIONIARBPROC) get_proc("glBlendEquationiARB"); + gl3wBlendEquationSeparateiARB = (PFNGLBLENDEQUATIONSEPARATEIARBPROC) get_proc("glBlendEquationSeparateiARB"); + gl3wBlendFunciARB = (PFNGLBLENDFUNCIARBPROC) get_proc("glBlendFunciARB"); + gl3wBlendFuncSeparateiARB = (PFNGLBLENDFUNCSEPARATEIARBPROC) get_proc("glBlendFuncSeparateiARB"); + gl3wMinSampleShadingARB = (PFNGLMINSAMPLESHADINGARBPROC) get_proc("glMinSampleShadingARB"); + gl3wNamedStringARB = (PFNGLNAMEDSTRINGARBPROC) get_proc("glNamedStringARB"); + gl3wDeleteNamedStringARB = (PFNGLDELETENAMEDSTRINGARBPROC) get_proc("glDeleteNamedStringARB"); + gl3wCompileShaderIncludeARB = (PFNGLCOMPILESHADERINCLUDEARBPROC) get_proc("glCompileShaderIncludeARB"); + gl3wIsNamedStringARB = (PFNGLISNAMEDSTRINGARBPROC) get_proc("glIsNamedStringARB"); + gl3wGetNamedStringARB = (PFNGLGETNAMEDSTRINGARBPROC) get_proc("glGetNamedStringARB"); + gl3wGetNamedStringivARB = (PFNGLGETNAMEDSTRINGIVARBPROC) get_proc("glGetNamedStringivARB"); + gl3wBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC) get_proc("glBindFragDataLocationIndexed"); + gl3wGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC) get_proc("glGetFragDataIndex"); + gl3wGenSamplers = (PFNGLGENSAMPLERSPROC) get_proc("glGenSamplers"); + gl3wDeleteSamplers = (PFNGLDELETESAMPLERSPROC) get_proc("glDeleteSamplers"); + gl3wIsSampler = (PFNGLISSAMPLERPROC) get_proc("glIsSampler"); + gl3wBindSampler = (PFNGLBINDSAMPLERPROC) get_proc("glBindSampler"); + gl3wSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC) get_proc("glSamplerParameteri"); + gl3wSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC) get_proc("glSamplerParameteriv"); + gl3wSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC) get_proc("glSamplerParameterf"); + gl3wSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC) get_proc("glSamplerParameterfv"); + gl3wSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC) get_proc("glSamplerParameterIiv"); + gl3wSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC) get_proc("glSamplerParameterIuiv"); + gl3wGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC) get_proc("glGetSamplerParameteriv"); + gl3wGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC) get_proc("glGetSamplerParameterIiv"); + gl3wGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC) get_proc("glGetSamplerParameterfv"); + gl3wGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC) get_proc("glGetSamplerParameterIuiv"); + gl3wQueryCounter = (PFNGLQUERYCOUNTERPROC) get_proc("glQueryCounter"); + gl3wGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC) get_proc("glGetQueryObjecti64v"); + gl3wGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC) get_proc("glGetQueryObjectui64v"); + gl3wVertexP2ui = (PFNGLVERTEXP2UIPROC) get_proc("glVertexP2ui"); + gl3wVertexP2uiv = (PFNGLVERTEXP2UIVPROC) get_proc("glVertexP2uiv"); + gl3wVertexP3ui = (PFNGLVERTEXP3UIPROC) get_proc("glVertexP3ui"); + gl3wVertexP3uiv = (PFNGLVERTEXP3UIVPROC) get_proc("glVertexP3uiv"); + gl3wVertexP4ui = (PFNGLVERTEXP4UIPROC) get_proc("glVertexP4ui"); + gl3wVertexP4uiv = (PFNGLVERTEXP4UIVPROC) get_proc("glVertexP4uiv"); + gl3wTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC) get_proc("glTexCoordP1ui"); + gl3wTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC) get_proc("glTexCoordP1uiv"); + gl3wTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC) get_proc("glTexCoordP2ui"); + gl3wTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC) get_proc("glTexCoordP2uiv"); + gl3wTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC) get_proc("glTexCoordP3ui"); + gl3wTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC) get_proc("glTexCoordP3uiv"); + gl3wTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC) get_proc("glTexCoordP4ui"); + gl3wTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC) get_proc("glTexCoordP4uiv"); + gl3wMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC) get_proc("glMultiTexCoordP1ui"); + gl3wMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC) get_proc("glMultiTexCoordP1uiv"); + gl3wMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC) get_proc("glMultiTexCoordP2ui"); + gl3wMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC) get_proc("glMultiTexCoordP2uiv"); + gl3wMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC) get_proc("glMultiTexCoordP3ui"); + gl3wMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC) get_proc("glMultiTexCoordP3uiv"); + gl3wMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC) get_proc("glMultiTexCoordP4ui"); + gl3wMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC) get_proc("glMultiTexCoordP4uiv"); + gl3wNormalP3ui = (PFNGLNORMALP3UIPROC) get_proc("glNormalP3ui"); + gl3wNormalP3uiv = (PFNGLNORMALP3UIVPROC) get_proc("glNormalP3uiv"); + gl3wColorP3ui = (PFNGLCOLORP3UIPROC) get_proc("glColorP3ui"); + gl3wColorP3uiv = (PFNGLCOLORP3UIVPROC) get_proc("glColorP3uiv"); + gl3wColorP4ui = (PFNGLCOLORP4UIPROC) get_proc("glColorP4ui"); + gl3wColorP4uiv = (PFNGLCOLORP4UIVPROC) get_proc("glColorP4uiv"); + gl3wSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC) get_proc("glSecondaryColorP3ui"); + gl3wSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC) get_proc("glSecondaryColorP3uiv"); + gl3wVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC) get_proc("glVertexAttribP1ui"); + gl3wVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC) get_proc("glVertexAttribP1uiv"); + gl3wVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC) get_proc("glVertexAttribP2ui"); + gl3wVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC) get_proc("glVertexAttribP2uiv"); + gl3wVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC) get_proc("glVertexAttribP3ui"); + gl3wVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC) get_proc("glVertexAttribP3uiv"); + gl3wVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC) get_proc("glVertexAttribP4ui"); + gl3wVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC) get_proc("glVertexAttribP4uiv"); + gl3wDrawArraysIndirect = (PFNGLDRAWARRAYSINDIRECTPROC) get_proc("glDrawArraysIndirect"); + gl3wDrawElementsIndirect = (PFNGLDRAWELEMENTSINDIRECTPROC) get_proc("glDrawElementsIndirect"); + gl3wUniform1d = (PFNGLUNIFORM1DPROC) get_proc("glUniform1d"); + gl3wUniform2d = (PFNGLUNIFORM2DPROC) get_proc("glUniform2d"); + gl3wUniform3d = (PFNGLUNIFORM3DPROC) get_proc("glUniform3d"); + gl3wUniform4d = (PFNGLUNIFORM4DPROC) get_proc("glUniform4d"); + gl3wUniform1dv = (PFNGLUNIFORM1DVPROC) get_proc("glUniform1dv"); + gl3wUniform2dv = (PFNGLUNIFORM2DVPROC) get_proc("glUniform2dv"); + gl3wUniform3dv = (PFNGLUNIFORM3DVPROC) get_proc("glUniform3dv"); + gl3wUniform4dv = (PFNGLUNIFORM4DVPROC) get_proc("glUniform4dv"); + gl3wUniformMatrix2dv = (PFNGLUNIFORMMATRIX2DVPROC) get_proc("glUniformMatrix2dv"); + gl3wUniformMatrix3dv = (PFNGLUNIFORMMATRIX3DVPROC) get_proc("glUniformMatrix3dv"); + gl3wUniformMatrix4dv = (PFNGLUNIFORMMATRIX4DVPROC) get_proc("glUniformMatrix4dv"); + gl3wUniformMatrix2x3dv = (PFNGLUNIFORMMATRIX2X3DVPROC) get_proc("glUniformMatrix2x3dv"); + gl3wUniformMatrix2x4dv = (PFNGLUNIFORMMATRIX2X4DVPROC) get_proc("glUniformMatrix2x4dv"); + gl3wUniformMatrix3x2dv = (PFNGLUNIFORMMATRIX3X2DVPROC) get_proc("glUniformMatrix3x2dv"); + gl3wUniformMatrix3x4dv = (PFNGLUNIFORMMATRIX3X4DVPROC) get_proc("glUniformMatrix3x4dv"); + gl3wUniformMatrix4x2dv = (PFNGLUNIFORMMATRIX4X2DVPROC) get_proc("glUniformMatrix4x2dv"); + gl3wUniformMatrix4x3dv = (PFNGLUNIFORMMATRIX4X3DVPROC) get_proc("glUniformMatrix4x3dv"); + gl3wGetUniformdv = (PFNGLGETUNIFORMDVPROC) get_proc("glGetUniformdv"); + gl3wGetSubroutineUniformLocation = (PFNGLGETSUBROUTINEUNIFORMLOCATIONPROC) get_proc("glGetSubroutineUniformLocation"); + gl3wGetSubroutineIndex = (PFNGLGETSUBROUTINEINDEXPROC) get_proc("glGetSubroutineIndex"); + gl3wGetActiveSubroutineUniformiv = (PFNGLGETACTIVESUBROUTINEUNIFORMIVPROC) get_proc("glGetActiveSubroutineUniformiv"); + gl3wGetActiveSubroutineUniformName = (PFNGLGETACTIVESUBROUTINEUNIFORMNAMEPROC) get_proc("glGetActiveSubroutineUniformName"); + gl3wGetActiveSubroutineName = (PFNGLGETACTIVESUBROUTINENAMEPROC) get_proc("glGetActiveSubroutineName"); + gl3wUniformSubroutinesuiv = (PFNGLUNIFORMSUBROUTINESUIVPROC) get_proc("glUniformSubroutinesuiv"); + gl3wGetUniformSubroutineuiv = (PFNGLGETUNIFORMSUBROUTINEUIVPROC) get_proc("glGetUniformSubroutineuiv"); + gl3wGetProgramStageiv = (PFNGLGETPROGRAMSTAGEIVPROC) get_proc("glGetProgramStageiv"); + gl3wPatchParameteri = (PFNGLPATCHPARAMETERIPROC) get_proc("glPatchParameteri"); + gl3wPatchParameterfv = (PFNGLPATCHPARAMETERFVPROC) get_proc("glPatchParameterfv"); + gl3wBindTransformFeedback = (PFNGLBINDTRANSFORMFEEDBACKPROC) get_proc("glBindTransformFeedback"); + gl3wDeleteTransformFeedbacks = (PFNGLDELETETRANSFORMFEEDBACKSPROC) get_proc("glDeleteTransformFeedbacks"); + gl3wGenTransformFeedbacks = (PFNGLGENTRANSFORMFEEDBACKSPROC) get_proc("glGenTransformFeedbacks"); + gl3wIsTransformFeedback = (PFNGLISTRANSFORMFEEDBACKPROC) get_proc("glIsTransformFeedback"); + gl3wPauseTransformFeedback = (PFNGLPAUSETRANSFORMFEEDBACKPROC) get_proc("glPauseTransformFeedback"); + gl3wResumeTransformFeedback = (PFNGLRESUMETRANSFORMFEEDBACKPROC) get_proc("glResumeTransformFeedback"); + gl3wDrawTransformFeedback = (PFNGLDRAWTRANSFORMFEEDBACKPROC) get_proc("glDrawTransformFeedback"); + gl3wDrawTransformFeedbackStream = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMPROC) get_proc("glDrawTransformFeedbackStream"); + gl3wBeginQueryIndexed = (PFNGLBEGINQUERYINDEXEDPROC) get_proc("glBeginQueryIndexed"); + gl3wEndQueryIndexed = (PFNGLENDQUERYINDEXEDPROC) get_proc("glEndQueryIndexed"); + gl3wGetQueryIndexediv = (PFNGLGETQUERYINDEXEDIVPROC) get_proc("glGetQueryIndexediv"); + gl3wReleaseShaderCompiler = (PFNGLRELEASESHADERCOMPILERPROC) get_proc("glReleaseShaderCompiler"); + gl3wShaderBinary = (PFNGLSHADERBINARYPROC) get_proc("glShaderBinary"); + gl3wGetShaderPrecisionFormat = (PFNGLGETSHADERPRECISIONFORMATPROC) get_proc("glGetShaderPrecisionFormat"); + gl3wDepthRangef = (PFNGLDEPTHRANGEFPROC) get_proc("glDepthRangef"); + gl3wClearDepthf = (PFNGLCLEARDEPTHFPROC) get_proc("glClearDepthf"); + gl3wGetProgramBinary = (PFNGLGETPROGRAMBINARYPROC) get_proc("glGetProgramBinary"); + gl3wProgramBinary = (PFNGLPROGRAMBINARYPROC) get_proc("glProgramBinary"); + gl3wProgramParameteri = (PFNGLPROGRAMPARAMETERIPROC) get_proc("glProgramParameteri"); + gl3wUseProgramStages = (PFNGLUSEPROGRAMSTAGESPROC) get_proc("glUseProgramStages"); + gl3wActiveShaderProgram = (PFNGLACTIVESHADERPROGRAMPROC) get_proc("glActiveShaderProgram"); + gl3wCreateShaderProgramv = (PFNGLCREATESHADERPROGRAMVPROC) get_proc("glCreateShaderProgramv"); + gl3wBindProgramPipeline = (PFNGLBINDPROGRAMPIPELINEPROC) get_proc("glBindProgramPipeline"); + gl3wDeleteProgramPipelines = (PFNGLDELETEPROGRAMPIPELINESPROC) get_proc("glDeleteProgramPipelines"); + gl3wGenProgramPipelines = (PFNGLGENPROGRAMPIPELINESPROC) get_proc("glGenProgramPipelines"); + gl3wIsProgramPipeline = (PFNGLISPROGRAMPIPELINEPROC) get_proc("glIsProgramPipeline"); + gl3wGetProgramPipelineiv = (PFNGLGETPROGRAMPIPELINEIVPROC) get_proc("glGetProgramPipelineiv"); + gl3wProgramUniform1i = (PFNGLPROGRAMUNIFORM1IPROC) get_proc("glProgramUniform1i"); + gl3wProgramUniform1iv = (PFNGLPROGRAMUNIFORM1IVPROC) get_proc("glProgramUniform1iv"); + gl3wProgramUniform1f = (PFNGLPROGRAMUNIFORM1FPROC) get_proc("glProgramUniform1f"); + gl3wProgramUniform1fv = (PFNGLPROGRAMUNIFORM1FVPROC) get_proc("glProgramUniform1fv"); + gl3wProgramUniform1d = (PFNGLPROGRAMUNIFORM1DPROC) get_proc("glProgramUniform1d"); + gl3wProgramUniform1dv = (PFNGLPROGRAMUNIFORM1DVPROC) get_proc("glProgramUniform1dv"); + gl3wProgramUniform1ui = (PFNGLPROGRAMUNIFORM1UIPROC) get_proc("glProgramUniform1ui"); + gl3wProgramUniform1uiv = (PFNGLPROGRAMUNIFORM1UIVPROC) get_proc("glProgramUniform1uiv"); + gl3wProgramUniform2i = (PFNGLPROGRAMUNIFORM2IPROC) get_proc("glProgramUniform2i"); + gl3wProgramUniform2iv = (PFNGLPROGRAMUNIFORM2IVPROC) get_proc("glProgramUniform2iv"); + gl3wProgramUniform2f = (PFNGLPROGRAMUNIFORM2FPROC) get_proc("glProgramUniform2f"); + gl3wProgramUniform2fv = (PFNGLPROGRAMUNIFORM2FVPROC) get_proc("glProgramUniform2fv"); + gl3wProgramUniform2d = (PFNGLPROGRAMUNIFORM2DPROC) get_proc("glProgramUniform2d"); + gl3wProgramUniform2dv = (PFNGLPROGRAMUNIFORM2DVPROC) get_proc("glProgramUniform2dv"); + gl3wProgramUniform2ui = (PFNGLPROGRAMUNIFORM2UIPROC) get_proc("glProgramUniform2ui"); + gl3wProgramUniform2uiv = (PFNGLPROGRAMUNIFORM2UIVPROC) get_proc("glProgramUniform2uiv"); + gl3wProgramUniform3i = (PFNGLPROGRAMUNIFORM3IPROC) get_proc("glProgramUniform3i"); + gl3wProgramUniform3iv = (PFNGLPROGRAMUNIFORM3IVPROC) get_proc("glProgramUniform3iv"); + gl3wProgramUniform3f = (PFNGLPROGRAMUNIFORM3FPROC) get_proc("glProgramUniform3f"); + gl3wProgramUniform3fv = (PFNGLPROGRAMUNIFORM3FVPROC) get_proc("glProgramUniform3fv"); + gl3wProgramUniform3d = (PFNGLPROGRAMUNIFORM3DPROC) get_proc("glProgramUniform3d"); + gl3wProgramUniform3dv = (PFNGLPROGRAMUNIFORM3DVPROC) get_proc("glProgramUniform3dv"); + gl3wProgramUniform3ui = (PFNGLPROGRAMUNIFORM3UIPROC) get_proc("glProgramUniform3ui"); + gl3wProgramUniform3uiv = (PFNGLPROGRAMUNIFORM3UIVPROC) get_proc("glProgramUniform3uiv"); + gl3wProgramUniform4i = (PFNGLPROGRAMUNIFORM4IPROC) get_proc("glProgramUniform4i"); + gl3wProgramUniform4iv = (PFNGLPROGRAMUNIFORM4IVPROC) get_proc("glProgramUniform4iv"); + gl3wProgramUniform4f = (PFNGLPROGRAMUNIFORM4FPROC) get_proc("glProgramUniform4f"); + gl3wProgramUniform4fv = (PFNGLPROGRAMUNIFORM4FVPROC) get_proc("glProgramUniform4fv"); + gl3wProgramUniform4d = (PFNGLPROGRAMUNIFORM4DPROC) get_proc("glProgramUniform4d"); + gl3wProgramUniform4dv = (PFNGLPROGRAMUNIFORM4DVPROC) get_proc("glProgramUniform4dv"); + gl3wProgramUniform4ui = (PFNGLPROGRAMUNIFORM4UIPROC) get_proc("glProgramUniform4ui"); + gl3wProgramUniform4uiv = (PFNGLPROGRAMUNIFORM4UIVPROC) get_proc("glProgramUniform4uiv"); + gl3wProgramUniformMatrix2fv = (PFNGLPROGRAMUNIFORMMATRIX2FVPROC) get_proc("glProgramUniformMatrix2fv"); + gl3wProgramUniformMatrix3fv = (PFNGLPROGRAMUNIFORMMATRIX3FVPROC) get_proc("glProgramUniformMatrix3fv"); + gl3wProgramUniformMatrix4fv = (PFNGLPROGRAMUNIFORMMATRIX4FVPROC) get_proc("glProgramUniformMatrix4fv"); + gl3wProgramUniformMatrix2dv = (PFNGLPROGRAMUNIFORMMATRIX2DVPROC) get_proc("glProgramUniformMatrix2dv"); + gl3wProgramUniformMatrix3dv = (PFNGLPROGRAMUNIFORMMATRIX3DVPROC) get_proc("glProgramUniformMatrix3dv"); + gl3wProgramUniformMatrix4dv = (PFNGLPROGRAMUNIFORMMATRIX4DVPROC) get_proc("glProgramUniformMatrix4dv"); + gl3wProgramUniformMatrix2x3fv = (PFNGLPROGRAMUNIFORMMATRIX2X3FVPROC) get_proc("glProgramUniformMatrix2x3fv"); + gl3wProgramUniformMatrix3x2fv = (PFNGLPROGRAMUNIFORMMATRIX3X2FVPROC) get_proc("glProgramUniformMatrix3x2fv"); + gl3wProgramUniformMatrix2x4fv = (PFNGLPROGRAMUNIFORMMATRIX2X4FVPROC) get_proc("glProgramUniformMatrix2x4fv"); + gl3wProgramUniformMatrix4x2fv = (PFNGLPROGRAMUNIFORMMATRIX4X2FVPROC) get_proc("glProgramUniformMatrix4x2fv"); + gl3wProgramUniformMatrix3x4fv = (PFNGLPROGRAMUNIFORMMATRIX3X4FVPROC) get_proc("glProgramUniformMatrix3x4fv"); + gl3wProgramUniformMatrix4x3fv = (PFNGLPROGRAMUNIFORMMATRIX4X3FVPROC) get_proc("glProgramUniformMatrix4x3fv"); + gl3wProgramUniformMatrix2x3dv = (PFNGLPROGRAMUNIFORMMATRIX2X3DVPROC) get_proc("glProgramUniformMatrix2x3dv"); + gl3wProgramUniformMatrix3x2dv = (PFNGLPROGRAMUNIFORMMATRIX3X2DVPROC) get_proc("glProgramUniformMatrix3x2dv"); + gl3wProgramUniformMatrix2x4dv = (PFNGLPROGRAMUNIFORMMATRIX2X4DVPROC) get_proc("glProgramUniformMatrix2x4dv"); + gl3wProgramUniformMatrix4x2dv = (PFNGLPROGRAMUNIFORMMATRIX4X2DVPROC) get_proc("glProgramUniformMatrix4x2dv"); + gl3wProgramUniformMatrix3x4dv = (PFNGLPROGRAMUNIFORMMATRIX3X4DVPROC) get_proc("glProgramUniformMatrix3x4dv"); + gl3wProgramUniformMatrix4x3dv = (PFNGLPROGRAMUNIFORMMATRIX4X3DVPROC) get_proc("glProgramUniformMatrix4x3dv"); + gl3wValidateProgramPipeline = (PFNGLVALIDATEPROGRAMPIPELINEPROC) get_proc("glValidateProgramPipeline"); + gl3wGetProgramPipelineInfoLog = (PFNGLGETPROGRAMPIPELINEINFOLOGPROC) get_proc("glGetProgramPipelineInfoLog"); + gl3wVertexAttribL1d = (PFNGLVERTEXATTRIBL1DPROC) get_proc("glVertexAttribL1d"); + gl3wVertexAttribL2d = (PFNGLVERTEXATTRIBL2DPROC) get_proc("glVertexAttribL2d"); + gl3wVertexAttribL3d = (PFNGLVERTEXATTRIBL3DPROC) get_proc("glVertexAttribL3d"); + gl3wVertexAttribL4d = (PFNGLVERTEXATTRIBL4DPROC) get_proc("glVertexAttribL4d"); + gl3wVertexAttribL1dv = (PFNGLVERTEXATTRIBL1DVPROC) get_proc("glVertexAttribL1dv"); + gl3wVertexAttribL2dv = (PFNGLVERTEXATTRIBL2DVPROC) get_proc("glVertexAttribL2dv"); + gl3wVertexAttribL3dv = (PFNGLVERTEXATTRIBL3DVPROC) get_proc("glVertexAttribL3dv"); + gl3wVertexAttribL4dv = (PFNGLVERTEXATTRIBL4DVPROC) get_proc("glVertexAttribL4dv"); + gl3wVertexAttribLPointer = (PFNGLVERTEXATTRIBLPOINTERPROC) get_proc("glVertexAttribLPointer"); + gl3wGetVertexAttribLdv = (PFNGLGETVERTEXATTRIBLDVPROC) get_proc("glGetVertexAttribLdv"); + gl3wViewportArrayv = (PFNGLVIEWPORTARRAYVPROC) get_proc("glViewportArrayv"); + gl3wViewportIndexedf = (PFNGLVIEWPORTINDEXEDFPROC) get_proc("glViewportIndexedf"); + gl3wViewportIndexedfv = (PFNGLVIEWPORTINDEXEDFVPROC) get_proc("glViewportIndexedfv"); + gl3wScissorArrayv = (PFNGLSCISSORARRAYVPROC) get_proc("glScissorArrayv"); + gl3wScissorIndexed = (PFNGLSCISSORINDEXEDPROC) get_proc("glScissorIndexed"); + gl3wScissorIndexedv = (PFNGLSCISSORINDEXEDVPROC) get_proc("glScissorIndexedv"); + gl3wDepthRangeArrayv = (PFNGLDEPTHRANGEARRAYVPROC) get_proc("glDepthRangeArrayv"); + gl3wDepthRangeIndexed = (PFNGLDEPTHRANGEINDEXEDPROC) get_proc("glDepthRangeIndexed"); + gl3wGetFloati_v = (PFNGLGETFLOATI_VPROC) get_proc("glGetFloati_v"); + gl3wGetDoublei_v = (PFNGLGETDOUBLEI_VPROC) get_proc("glGetDoublei_v"); + gl3wCreateSyncFromCLeventARB = (PFNGLCREATESYNCFROMCLEVENTARBPROC) get_proc("glCreateSyncFromCLeventARB"); + gl3wDebugMessageControlARB = (PFNGLDEBUGMESSAGECONTROLARBPROC) get_proc("glDebugMessageControlARB"); + gl3wDebugMessageInsertARB = (PFNGLDEBUGMESSAGEINSERTARBPROC) get_proc("glDebugMessageInsertARB"); + gl3wDebugMessageCallbackARB = (PFNGLDEBUGMESSAGECALLBACKARBPROC) get_proc("glDebugMessageCallbackARB"); + gl3wGetDebugMessageLogARB = (PFNGLGETDEBUGMESSAGELOGARBPROC) get_proc("glGetDebugMessageLogARB"); + gl3wGetGraphicsResetStatusARB = (PFNGLGETGRAPHICSRESETSTATUSARBPROC) get_proc("glGetGraphicsResetStatusARB"); + gl3wGetnTexImageARB = (PFNGLGETNTEXIMAGEARBPROC) get_proc("glGetnTexImageARB"); + gl3wReadnPixelsARB = (PFNGLREADNPIXELSARBPROC) get_proc("glReadnPixelsARB"); + gl3wGetnCompressedTexImageARB = (PFNGLGETNCOMPRESSEDTEXIMAGEARBPROC) get_proc("glGetnCompressedTexImageARB"); + gl3wGetnUniformfvARB = (PFNGLGETNUNIFORMFVARBPROC) get_proc("glGetnUniformfvARB"); + gl3wGetnUniformivARB = (PFNGLGETNUNIFORMIVARBPROC) get_proc("glGetnUniformivARB"); + gl3wGetnUniformuivARB = (PFNGLGETNUNIFORMUIVARBPROC) get_proc("glGetnUniformuivARB"); + gl3wGetnUniformdvARB = (PFNGLGETNUNIFORMDVARBPROC) get_proc("glGetnUniformdvARB"); + gl3wDrawArraysInstancedBaseInstance = (PFNGLDRAWARRAYSINSTANCEDBASEINSTANCEPROC) get_proc("glDrawArraysInstancedBaseInstance"); + gl3wDrawElementsInstancedBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEINSTANCEPROC) get_proc("glDrawElementsInstancedBaseInstance"); + gl3wDrawElementsInstancedBaseVertexBaseInstance = (PFNGLDRAWELEMENTSINSTANCEDBASEVERTEXBASEINSTANCEPROC) get_proc("glDrawElementsInstancedBaseVertexBaseInstance"); + gl3wDrawTransformFeedbackInstanced = (PFNGLDRAWTRANSFORMFEEDBACKINSTANCEDPROC) get_proc("glDrawTransformFeedbackInstanced"); + gl3wDrawTransformFeedbackStreamInstanced = (PFNGLDRAWTRANSFORMFEEDBACKSTREAMINSTANCEDPROC) get_proc("glDrawTransformFeedbackStreamInstanced"); + gl3wGetInternalformativ = (PFNGLGETINTERNALFORMATIVPROC) get_proc("glGetInternalformativ"); + gl3wGetActiveAtomicCounterBufferiv = (PFNGLGETACTIVEATOMICCOUNTERBUFFERIVPROC) get_proc("glGetActiveAtomicCounterBufferiv"); + gl3wBindImageTexture = (PFNGLBINDIMAGETEXTUREPROC) get_proc("glBindImageTexture"); + gl3wMemoryBarrier = (PFNGLMEMORYBARRIERPROC) get_proc("glMemoryBarrier"); + gl3wTexStorage1D = (PFNGLTEXSTORAGE1DPROC) get_proc("glTexStorage1D"); + gl3wTexStorage2D = (PFNGLTEXSTORAGE2DPROC) get_proc("glTexStorage2D"); + gl3wTexStorage3D = (PFNGLTEXSTORAGE3DPROC) get_proc("glTexStorage3D"); + gl3wTextureStorage1DEXT = (PFNGLTEXTURESTORAGE1DEXTPROC) get_proc("glTextureStorage1DEXT"); + gl3wTextureStorage2DEXT = (PFNGLTEXTURESTORAGE2DEXTPROC) get_proc("glTextureStorage2DEXT"); + gl3wTextureStorage3DEXT = (PFNGLTEXTURESTORAGE3DEXTPROC) get_proc("glTextureStorage3DEXT"); + gl3wDebugMessageControl = (PFNGLDEBUGMESSAGECONTROLPROC) get_proc("glDebugMessageControl"); + gl3wDebugMessageInsert = (PFNGLDEBUGMESSAGEINSERTPROC) get_proc("glDebugMessageInsert"); + gl3wDebugMessageCallback = (PFNGLDEBUGMESSAGECALLBACKPROC) get_proc("glDebugMessageCallback"); + gl3wGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGPROC) get_proc("glGetDebugMessageLog"); + gl3wPushDebugGroup = (PFNGLPUSHDEBUGGROUPPROC) get_proc("glPushDebugGroup"); + gl3wPopDebugGroup = (PFNGLPOPDEBUGGROUPPROC) get_proc("glPopDebugGroup"); + gl3wObjectLabel = (PFNGLOBJECTLABELPROC) get_proc("glObjectLabel"); + gl3wGetObjectLabel = (PFNGLGETOBJECTLABELPROC) get_proc("glGetObjectLabel"); + gl3wObjectPtrLabel = (PFNGLOBJECTPTRLABELPROC) get_proc("glObjectPtrLabel"); + gl3wGetObjectPtrLabel = (PFNGLGETOBJECTPTRLABELPROC) get_proc("glGetObjectPtrLabel"); + gl3wClearBufferData = (PFNGLCLEARBUFFERDATAPROC) get_proc("glClearBufferData"); + gl3wClearBufferSubData = (PFNGLCLEARBUFFERSUBDATAPROC) get_proc("glClearBufferSubData"); + gl3wClearNamedBufferDataEXT = (PFNGLCLEARNAMEDBUFFERDATAEXTPROC) get_proc("glClearNamedBufferDataEXT"); + gl3wClearNamedBufferSubDataEXT = (PFNGLCLEARNAMEDBUFFERSUBDATAEXTPROC) get_proc("glClearNamedBufferSubDataEXT"); + gl3wDispatchCompute = (PFNGLDISPATCHCOMPUTEPROC) get_proc("glDispatchCompute"); + gl3wDispatchComputeIndirect = (PFNGLDISPATCHCOMPUTEINDIRECTPROC) get_proc("glDispatchComputeIndirect"); + gl3wCopyImageSubData = (PFNGLCOPYIMAGESUBDATAPROC) get_proc("glCopyImageSubData"); + gl3wTextureView = (PFNGLTEXTUREVIEWPROC) get_proc("glTextureView"); + gl3wBindVertexBuffer = (PFNGLBINDVERTEXBUFFERPROC) get_proc("glBindVertexBuffer"); + gl3wVertexAttribFormat = (PFNGLVERTEXATTRIBFORMATPROC) get_proc("glVertexAttribFormat"); + gl3wVertexAttribIFormat = (PFNGLVERTEXATTRIBIFORMATPROC) get_proc("glVertexAttribIFormat"); + gl3wVertexAttribLFormat = (PFNGLVERTEXATTRIBLFORMATPROC) get_proc("glVertexAttribLFormat"); + gl3wVertexAttribBinding = (PFNGLVERTEXATTRIBBINDINGPROC) get_proc("glVertexAttribBinding"); + gl3wVertexBindingDivisor = (PFNGLVERTEXBINDINGDIVISORPROC) get_proc("glVertexBindingDivisor"); + gl3wVertexArrayBindVertexBufferEXT = (PFNGLVERTEXARRAYBINDVERTEXBUFFEREXTPROC) get_proc("glVertexArrayBindVertexBufferEXT"); + gl3wVertexArrayVertexAttribFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBFORMATEXTPROC) get_proc("glVertexArrayVertexAttribFormatEXT"); + gl3wVertexArrayVertexAttribIFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBIFORMATEXTPROC) get_proc("glVertexArrayVertexAttribIFormatEXT"); + gl3wVertexArrayVertexAttribLFormatEXT = (PFNGLVERTEXARRAYVERTEXATTRIBLFORMATEXTPROC) get_proc("glVertexArrayVertexAttribLFormatEXT"); + gl3wVertexArrayVertexAttribBindingEXT = (PFNGLVERTEXARRAYVERTEXATTRIBBINDINGEXTPROC) get_proc("glVertexArrayVertexAttribBindingEXT"); + gl3wVertexArrayVertexBindingDivisorEXT = (PFNGLVERTEXARRAYVERTEXBINDINGDIVISOREXTPROC) get_proc("glVertexArrayVertexBindingDivisorEXT"); + gl3wFramebufferParameteri = (PFNGLFRAMEBUFFERPARAMETERIPROC) get_proc("glFramebufferParameteri"); + gl3wGetFramebufferParameteriv = (PFNGLGETFRAMEBUFFERPARAMETERIVPROC) get_proc("glGetFramebufferParameteriv"); + gl3wNamedFramebufferParameteriEXT = (PFNGLNAMEDFRAMEBUFFERPARAMETERIEXTPROC) get_proc("glNamedFramebufferParameteriEXT"); + gl3wGetNamedFramebufferParameterivEXT = (PFNGLGETNAMEDFRAMEBUFFERPARAMETERIVEXTPROC) get_proc("glGetNamedFramebufferParameterivEXT"); + gl3wGetInternalformati64v = (PFNGLGETINTERNALFORMATI64VPROC) get_proc("glGetInternalformati64v"); + gl3wInvalidateTexSubImage = (PFNGLINVALIDATETEXSUBIMAGEPROC) get_proc("glInvalidateTexSubImage"); + gl3wInvalidateTexImage = (PFNGLINVALIDATETEXIMAGEPROC) get_proc("glInvalidateTexImage"); + gl3wInvalidateBufferSubData = (PFNGLINVALIDATEBUFFERSUBDATAPROC) get_proc("glInvalidateBufferSubData"); + gl3wInvalidateBufferData = (PFNGLINVALIDATEBUFFERDATAPROC) get_proc("glInvalidateBufferData"); + gl3wInvalidateFramebuffer = (PFNGLINVALIDATEFRAMEBUFFERPROC) get_proc("glInvalidateFramebuffer"); + gl3wInvalidateSubFramebuffer = (PFNGLINVALIDATESUBFRAMEBUFFERPROC) get_proc("glInvalidateSubFramebuffer"); + gl3wMultiDrawArraysIndirect = (PFNGLMULTIDRAWARRAYSINDIRECTPROC) get_proc("glMultiDrawArraysIndirect"); + gl3wMultiDrawElementsIndirect = (PFNGLMULTIDRAWELEMENTSINDIRECTPROC) get_proc("glMultiDrawElementsIndirect"); + gl3wGetProgramInterfaceiv = (PFNGLGETPROGRAMINTERFACEIVPROC) get_proc("glGetProgramInterfaceiv"); + gl3wGetProgramResourceIndex = (PFNGLGETPROGRAMRESOURCEINDEXPROC) get_proc("glGetProgramResourceIndex"); + gl3wGetProgramResourceName = (PFNGLGETPROGRAMRESOURCENAMEPROC) get_proc("glGetProgramResourceName"); + gl3wGetProgramResourceiv = (PFNGLGETPROGRAMRESOURCEIVPROC) get_proc("glGetProgramResourceiv"); + gl3wGetProgramResourceLocation = (PFNGLGETPROGRAMRESOURCELOCATIONPROC) get_proc("glGetProgramResourceLocation"); + gl3wGetProgramResourceLocationIndex = (PFNGLGETPROGRAMRESOURCELOCATIONINDEXPROC) get_proc("glGetProgramResourceLocationIndex"); + gl3wShaderStorageBlockBinding = (PFNGLSHADERSTORAGEBLOCKBINDINGPROC) get_proc("glShaderStorageBlockBinding"); + gl3wTexBufferRange = (PFNGLTEXBUFFERRANGEPROC) get_proc("glTexBufferRange"); + gl3wTextureBufferRangeEXT = (PFNGLTEXTUREBUFFERRANGEEXTPROC) get_proc("glTextureBufferRangeEXT"); + gl3wTexStorage2DMultisample = (PFNGLTEXSTORAGE2DMULTISAMPLEPROC) get_proc("glTexStorage2DMultisample"); + gl3wTexStorage3DMultisample = (PFNGLTEXSTORAGE3DMULTISAMPLEPROC) get_proc("glTexStorage3DMultisample"); + gl3wTextureStorage2DMultisampleEXT = (PFNGLTEXTURESTORAGE2DMULTISAMPLEEXTPROC) get_proc("glTextureStorage2DMultisampleEXT"); + gl3wTextureStorage3DMultisampleEXT = (PFNGLTEXTURESTORAGE3DMULTISAMPLEEXTPROC) get_proc("glTextureStorage3DMultisampleEXT"); +} diff --git a/apps/exampleViewer/common/glfw/.appveyor.yml b/apps/exampleViewer/common/glfw/.appveyor.yml new file mode 100644 index 0000000000..ae658cc371 --- /dev/null +++ b/apps/exampleViewer/common/glfw/.appveyor.yml @@ -0,0 +1,22 @@ +branches: + only: + - ci + - master +skip_tags: true +environment: + matrix: + - BUILD_SHARED_LIBS: ON + - BUILD_SHARED_LIBS: OFF +matrix: + fast_finish: true +build_script: + - mkdir build + - cd build + - cmake -DBUILD_SHARED_LIBS=%BUILD_SHARED_LIBS% .. + - cmake --build . +notifications: + - provider: Email + to: + - ci@glfw.org + - on_build_failure: true + - on_build_success: false diff --git a/apps/exampleViewer/common/glfw/.github/CONTRIBUTING.md b/apps/exampleViewer/common/glfw/.github/CONTRIBUTING.md new file mode 100644 index 0000000000..672264dc62 --- /dev/null +++ b/apps/exampleViewer/common/glfw/.github/CONTRIBUTING.md @@ -0,0 +1,346 @@ +# Contribution Guide + +## Contents + +- [Asking a question](#asking-a-question) +- [Reporting a bug](#reporting-a-bug) + - [Reporting a compile or link bug](#reporting-a-compile-or-link-bug) + - [Reporting a segfault or other crash bug](#reporting-a-segfault-or-other-crash-bug) + - [Reporting a context creation bug](#reporting-a-context-creation-bug) + - [Reporting a monitor or video mode bug](#reporting-a-monitor-or-video-mode-bug) + - [Reporting an input or event bug](#reporting-an-input-or-event-bug) + - [Reporting some other library bug](#reporting-some-other-library-bug) + - [Reporting a documentation bug](#reporting-a-documentation-bug) + - [Reporting a website bug](#reporting-a-website-bug) +- [Requesting a feature](#requesting-a-feature) +- [Contributing a bug fix](#contributing-a-bug-fix) +- [Contributing a feature](#contributing-a-feature) + + +## Asking a question + +Questions about how to use GLFW should be asked either in the [support +section](http://discourse.glfw.org/c/support) of the forum, under the [Stack +Overflow tag](https://stackoverflow.com/questions/tagged/glfw) or [Game +Development tag](https://gamedev.stackexchange.com/questions/tagged/glfw) on +Stack Exchange or in the IRC channel `#glfw` on +[Freenode](http://freenode.net/). + +Questions about the design or implementation of GLFW or about future plans +should be asked in the [dev section](http://discourse.glfw.org/c/dev) of the +forum or in the IRC channel. Please don't open a GitHub issue to discuss design +questions without first checking with a maintainer. + + +## Reporting a bug + +If GLFW is behaving unexpectedly at run-time, start by setting an [error +callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling). +GLFW will often tell you the cause of an error via this callback. If it +doesn't, that might be a separate bug. + +If GLFW is crashing or triggering asserts, make sure that all your object +handles and other pointers are valid. + +For bugs where it makes sense, a [Short, Self Contained, Correct (Compilable), +Example](http://www.sscce.org/) is absolutely invaluable. Just put it inline in +the body text. Note that if the bug is reproducible with one of the test +programs that come with GLFW, just mention that instead. + +__Don't worry about adding too much information__. Unimportant information can +be abbreviated or removed later, but missing information can stall bug fixing, +especially when your schedule doesn't align with that of the maintainer. + +There are issue labels for both platforms and GPU manufacturers, so there is no +need to mention these in the subject line. If you do, it will be removed when +the issue is labeled. + +If your bug is already reported, please add any new information you have, or if +it already has everything, give it a :+1:. + + +### Reporting a compile or link bug + +__Note:__ GLFW needs many system APIs to do its job, which on some platforms +means linking to many system libraries. If you are using GLFW as a static +library, that means your application needs to link to these in addition to GLFW. + +__Note:__ Check the [Compiling +GLFW](http://www.glfw.org/docs/latest/compile.html) guide and or [Building +applications](http://www.glfw.org/docs/latest/build.html) guide for before +opening an issue of this kind. Most issues are caused by a missing package or +linker flag. + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`) and the __compiler name and version__ (e.g. `Visual +C++ 2015 Update 2`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include the __complete build log__ from your compiler and linker, +even if it's long. It can always be shortened later, if necessary. + + +#### Quick template + +``` +OS and version: +Compiler version: +Release or commit: +Build log: +``` + + +### Reporting a segfault or other crash bug + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include any __error messages__ provided to your application via the +[error +callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling) and +the __full call stack__ of the crash, or if the crash does not occur in debug +mode, mention that instead. + + +#### Quick template + +``` +OS and version: +Release or commit: +Error messages: +Call stack: +``` + + +### Reporting a context creation bug + +__Note:__ Windows ships with graphics drivers that do not support OpenGL. If +GLFW says that your machine lacks support for OpenGL, it very likely does. +Install drivers from the computer manufacturer or graphics card manufacturer +([Nvidia](http://www.geforce.com/drivers), +[AMD](http://support.amd.com/en-us/download), +[Intel](https://www-ssl.intel.com/content/www/us/en/support/detect.html)) to +fix this. + +__Note:__ AMD only supports OpenGL ES on Windows via EGL. See the +[GLFW\_CONTEXT\_CREATION\_API](http://www.glfw.org/docs/latest/window_guide.html#window_hints_ctx) +hint for how to select EGL. + +Please verify that context creation also fails with the `glfwinfo` tool before +reporting it as a bug. This tool is included in the GLFW source tree as +`tests/glfwinfo.c` and is built along with the library. It has switches for all +GLFW context and framebuffer hints. Run `glfwinfo -h` for a complete list. + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include the __GLFW version string__ (`3.2.0 X11 EGL clock_gettime +/dev/js XI Xf86vm`), as described +[here](http://www.glfw.org/docs/latest/intro.html#intro_version_string), the +__GPU model and driver version__ (e.g. `GeForce GTX660 with 352.79`), and the +__output of `glfwinfo`__ (with switches matching any hints you set in your +code) when reporting this kind of bug. If this tool doesn't run on the machine, +mention that instead. + + +#### Quick template + +``` +OS and version: +GPU and driver: +Release or commit: +Version string: +glfwinfo output: +``` + + +### Reporting a monitor or video mode bug + +__Note:__ On headless systems on some platforms, no monitors are reported. This +causes glfwGetPrimaryMonitor to return `NULL`, which not all applications are +prepared for. + +__Note:__ Some third-party tools report more video modes than are approved of +by the OS. For safety and compatibility, GLFW only reports video modes the OS +wants programs to use. This is not a bug. + +The `monitors` tool is included in the GLFW source tree as `tests/monitors.c` +and is built along with the library. It lists all information GLFW provides +about monitors it detects. + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include any __error messages__ provided to your application via the +[error +callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling) and +the __output of `monitors`__ when reporting this kind of bug. If this tool +doesn't run on the machine, mention this instead. + + +#### Quick template + +``` +OS and version: +Release or commit: +Error messages: +monitors output: +``` + + +### Reporting an input or event bug + +__Note:__ The exact ordering of related window events will sometimes differ. + +__Note:__ Window moving and resizing (by the user) will block the main thread on some +platforms. This is not a bug. Set a [refresh +callback](http://www.glfw.org/docs/latest/window.html#window_refresh) if you +want to keep the window contents updated during a move or size operation. + +The `events` tool is included in the GLFW source tree as `tests/events.c` and is +built along with the library. It prints all information provided to every +callback supported by GLFW as events occur. Each event is listed with the time +and a unique number to make discussions about event logs easier. The tool has +command-line options for creating multiple windows and full screen windows. + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include any __error messages__ provided to your application via the +[error +callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling) and +if relevant, the __output of `events`__ when reporting this kind of bug. If +this tool doesn't run on the machine, mention this instead. + + +#### Quick template + +``` +OS and version: +Release or commit: +Error messages: +events output: +``` + + +### Reporting some other library bug + +Always include the __operating system name and version__ (e.g. `Windows +7 64-bit` or `Ubuntu 15.10`). If you are using an official release of GLFW, +include the __GLFW release version__ (e.g. `3.1.2`), otherwise include the +__GLFW commit ID__ (e.g. `3795d78b14ef06008889cc422a1fb8d642597751`) from Git. + +Please also include any __error messages__ provided to your application via the +[error +callback](http://www.glfw.org/docs/latest/intro_guide.html#error_handling), if +relevant. + + +#### Quick template + +``` +OS and version: +Release or commit: +Error messages: +``` + + +### Reporting a documentation bug + +If you found a bug in the documentation, including this file, then it's fine to +just link to that web page or mention that source file. You don't need to match +the source to the output or vice versa. + + +### Reporting a website bug + +If the bug is in the documentation (anything under `/docs/`) then please see the +section above. Bugs in the rest of the site are reported to to the [website +source repository](https://github.com/glfw/website/issues). + + +## Requesting a feature + +Please explain why you need the feature and how you intend to use it. If you +have a specific API design in mind, please add that as well. If you have or are +planning to write code for the feature, see the section below. + +If there already is a request for the feature you need, add your specific use +case unless it is already mentioned. If it is, give it a :+1:. + + +## Contributing a bug fix + +__Note:__ You must have all necessary [intellectual +property rights](https://en.wikipedia.org/wiki/Intellectual_property) to any +code you contribute. If you did not write the code yourself, you must explain +where it came from and under what license you received it. Even code using the +same license as GLFW may not be copied without attribution. + +__There is no preferred patch size__. A one character fix is just as welcome as +a thousand line one, if that is the appropriate size for the fix. + +In addition to the code, a complete bug fix includes: + +- Change log entry in `README.md`, describing the incorrect behavior +- Credits entries for all authors of the bug fix + +Bug fixes will not be rejected because they don't include all the above parts, +but please keep in mind that maintainer time is finite and that there are many +other bugs and features to work on. + +If the patch fixes a bug introduced after the last release, it should not get +a change log entry. + + +## Contributing a feature + +__Note:__ You must have all necessary rights to any code you contribute. If you +did not write the code yourself, you must explain where it came from and under +what license. Even code using the same license as GLFW may not be copied +without attribution. + +__There is no preferred patch size__. A one character change is just as welcome +as one adding a thousand line one, if that is the appropriate size for the +feature. + +In addition to the code, a complete feature includes: + +- Change log entry in `README.md`, listing all new symbols +- News page entry, briefly describing the feature +- Guide documentation, with minimal examples, in the relevant guide +- Reference documentation, with all applicable tags +- Cross-references and mentions in appropriate places +- Credits entries for all authors of the feature + +If the feature requires platform-specific code, at minimum stubs must be added +for the new platform function to all supported and experimental platforms. + +If it adds a new callback, support for it must be added to `tests/event.c`. + +If it adds a new monitor property, support for it must be added to +`tests/monitor.c`. + +If it adds a new OpenGL, OpenGL ES or Vulkan option or extension, support +for it must be added to `tests/glfwinfo.c` and the behavior of the library when +the extension is missing documented in `docs/compat.dox`. + +Features will not be rejected because they don't include all the above parts, +but please keep in mind that maintainer time is finite and that there are many +other features and bugs to work on. + +Please also keep in mind that any part of the public API that has been included +in a release cannot be changed until the next _major_ version. Features can be +added and existing parts can sometimes be overloaded (in the general sense of +doing more things, not in the C++ sense), but code written to the API of one +minor release should both compile and run on subsequent minor releases. + diff --git a/apps/exampleViewer/common/glfw/.gitignore b/apps/exampleViewer/common/glfw/.gitignore new file mode 100644 index 0000000000..9dd0224ed5 --- /dev/null +++ b/apps/exampleViewer/common/glfw/.gitignore @@ -0,0 +1,84 @@ +# External junk +.DS_Store +_ReSharper* +*.opensdf +*.sdf +*.suo +*.dir +*.vcxproj* +*.sln +Win32 +x64 +Debug +Release +MinSizeRel +RelWithDebInfo +*.xcodeproj + +# CMake files +Makefile +CMakeCache.txt +CMakeFiles +CMakeScripts +cmake_install.cmake +cmake_uninstall.cmake + +# Generated files +docs/Doxyfile +docs/html +docs/warnings.txt +docs/doxygen_sqlite3.db +src/glfw_config.h +src/glfw3.pc +src/glfw3Config.cmake +src/glfw3ConfigVersion.cmake +src/wayland-pointer-constraints-unstable-v1-client-protocol.h +src/wayland-pointer-constraints-unstable-v1-protocol.c +src/wayland-relative-pointer-unstable-v1-client-protocol.h +src/wayland-relative-pointer-unstable-v1-protocol.c + +# Compiled binaries +src/libglfw.so +src/libglfw.so.3 +src/libglfw.so.3.3 +src/libglfw.dylib +src/libglfw.dylib +src/libglfw.3.dylib +src/libglfw.3.3.dylib +src/libglfw3.a +src/glfw3.lib +src/glfw3.dll +src/glfw3dll.lib +src/libglfw3dll.a +examples/*.app +examples/*.exe +examples/boing +examples/gears +examples/heightmap +examples/particles +examples/splitview +examples/simple +examples/wave +tests/*.app +tests/*.exe +tests/clipboard +tests/cursor +tests/empty +tests/events +tests/gamma +tests/glfwinfo +tests/icon +tests/iconify +tests/joysticks +tests/monitors +tests/msaa +tests/reopen +tests/sharing +tests/tearing +tests/threads +tests/timeout +tests/title +tests/version +tests/vulkan +tests/windows + diff --git a/apps/exampleViewer/common/glfw/.travis.yml b/apps/exampleViewer/common/glfw/.travis.yml new file mode 100644 index 0000000000..ff1c6c352b --- /dev/null +++ b/apps/exampleViewer/common/glfw/.travis.yml @@ -0,0 +1,30 @@ +language: c +compiler: clang +branches: + only: + - ci + - master +os: + - linux + - osx +sudo: false +addons: + apt: + sources: + - kubuntu-backports + packages: + - cmake +env: + - BUILD_SHARED_LIBS=ON + - BUILD_SHARED_LIBS=OFF +script: + - mkdir build + - cd build + - cmake -DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS} .. + - cmake --build . +notifications: + email: + recipients: + - ci@glfw.org + on_success: never + on_failure: always diff --git a/apps/exampleViewer/common/glfw/CMake/MacOSXBundleInfo.plist.in b/apps/exampleViewer/common/glfw/CMake/MacOSXBundleInfo.plist.in new file mode 100644 index 0000000000..684ad79087 --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/MacOSXBundleInfo.plist.in @@ -0,0 +1,38 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${MACOSX_BUNDLE_EXECUTABLE_NAME} + CFBundleGetInfoString + ${MACOSX_BUNDLE_INFO_STRING} + CFBundleIconFile + ${MACOSX_BUNDLE_ICON_FILE} + CFBundleIdentifier + ${MACOSX_BUNDLE_GUI_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleLongVersionString + ${MACOSX_BUNDLE_LONG_VERSION_STRING} + CFBundleName + ${MACOSX_BUNDLE_BUNDLE_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + ${MACOSX_BUNDLE_SHORT_VERSION_STRING} + CFBundleSignature + ???? + CFBundleVersion + ${MACOSX_BUNDLE_BUNDLE_VERSION} + CSResourcesFileMapped + + LSRequiresCarbon + + NSHumanReadableCopyright + ${MACOSX_BUNDLE_COPYRIGHT} + NSHighResolutionCapable + + + diff --git a/apps/exampleViewer/common/glfw/CMake/amd64-mingw32msvc.cmake b/apps/exampleViewer/common/glfw/CMake/amd64-mingw32msvc.cmake new file mode 100644 index 0000000000..705e251d39 --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/amd64-mingw32msvc.cmake @@ -0,0 +1,13 @@ +# Define the environment for cross compiling from Linux to Win64 +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_C_COMPILER "amd64-mingw32msvc-gcc") +SET(CMAKE_CXX_COMPILER "amd64-mingw32msvc-g++") +SET(CMAKE_RC_COMPILER "amd64-mingw32msvc-windres") +SET(CMAKE_RANLIB "amd64-mingw32msvc-ranlib") + +# Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH "/usr/amd64-mingw32msvc") +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/apps/exampleViewer/common/glfw/CMake/i586-mingw32msvc.cmake b/apps/exampleViewer/common/glfw/CMake/i586-mingw32msvc.cmake new file mode 100644 index 0000000000..393ddbda30 --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/i586-mingw32msvc.cmake @@ -0,0 +1,13 @@ +# Define the environment for cross compiling from Linux to Win32 +SET(CMAKE_SYSTEM_NAME Windows) +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_C_COMPILER "i586-mingw32msvc-gcc") +SET(CMAKE_CXX_COMPILER "i586-mingw32msvc-g++") +SET(CMAKE_RC_COMPILER "i586-mingw32msvc-windres") +SET(CMAKE_RANLIB "i586-mingw32msvc-ranlib") + +# Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH "/usr/i586-mingw32msvc") +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/apps/exampleViewer/common/glfw/CMake/i686-pc-mingw32.cmake b/apps/exampleViewer/common/glfw/CMake/i686-pc-mingw32.cmake new file mode 100644 index 0000000000..9a46aef7b3 --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/i686-pc-mingw32.cmake @@ -0,0 +1,13 @@ +# Define the environment for cross compiling from Linux to Win32 +SET(CMAKE_SYSTEM_NAME Windows) # Target system name +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_C_COMPILER "i686-pc-mingw32-gcc") +SET(CMAKE_CXX_COMPILER "i686-pc-mingw32-g++") +SET(CMAKE_RC_COMPILER "i686-pc-mingw32-windres") +SET(CMAKE_RANLIB "i686-pc-mingw32-ranlib") + +#Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH "/opt/mingw/usr/i686-pc-mingw32") +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/apps/exampleViewer/common/glfw/CMake/i686-w64-mingw32.cmake b/apps/exampleViewer/common/glfw/CMake/i686-w64-mingw32.cmake new file mode 100644 index 0000000000..9bd60936de --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/i686-w64-mingw32.cmake @@ -0,0 +1,13 @@ +# Define the environment for cross compiling from Linux to Win32 +SET(CMAKE_SYSTEM_NAME Windows) # Target system name +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_C_COMPILER "i686-w64-mingw32-gcc") +SET(CMAKE_CXX_COMPILER "i686-w64-mingw32-g++") +SET(CMAKE_RC_COMPILER "i686-w64-mingw32-windres") +SET(CMAKE_RANLIB "i686-w64-mingw32-ranlib") + +# Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH "/usr/i686-w64-mingw32") +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/apps/exampleViewer/common/glfw/CMake/modules/FindMir.cmake b/apps/exampleViewer/common/glfw/CMake/modules/FindMir.cmake new file mode 100644 index 0000000000..3f1fb0b3ee --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/modules/FindMir.cmake @@ -0,0 +1,37 @@ +# Try to find Mir on a Unix system +# +# This will define: +# +# MIR_FOUND - System has Mir +# MIR_LIBRARIES - Link these to use Mir +# MIR_INCLUDE_DIR - Include directory for Mir +# MIR_DEFINITIONS - Compiler switches required for using Mir + +if (NOT WIN32) + + find_package (PkgConfig) + pkg_check_modules (PKG_MIR QUIET mirclient) + set(MIR_DEFINITIONS ${PKG_MIR_CFLAGS_OTHER}) + + find_path(MIR_INCLUDE_DIR + NAMES xkbcommon/xkbcommon.h + HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS} + ) + + find_library(MIR_LIBRARY + NAMES mirclient + HINTS ${PKG_MIR_LIBRARIES} ${MIR_LIBRARY_DIRS} + ) + + set (MIR_INCLUDE_DIR ${PKG_MIR_INCLUDE_DIRS}) + set (MIR_LIBRARIES ${MIR_LIBRARY}) + + include (FindPackageHandleStandardArgs) + find_package_handle_standard_args (MIR DEFAULT_MSG + MIR_LIBRARIES + MIR_INCLUDE_DIR + ) + + mark_as_advanced (MIR_LIBRARIES MIR_INCLUDE_DIR) + +endif () diff --git a/apps/exampleViewer/common/glfw/CMake/modules/FindVulkan.cmake b/apps/exampleViewer/common/glfw/CMake/modules/FindVulkan.cmake new file mode 100644 index 0000000000..d3a664a8bd --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/modules/FindVulkan.cmake @@ -0,0 +1,34 @@ +# Find Vulkan +# +# VULKAN_INCLUDE_DIR +# VULKAN_LIBRARY +# VULKAN_FOUND + +if (WIN32) + find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS + "$ENV{VULKAN_SDK}/Include" + "$ENV{VK_SDK_PATH}/Include") + if (CMAKE_CL_64) + find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS + "$ENV{VULKAN_SDK}/Bin" + "$ENV{VK_SDK_PATH}/Bin") + find_library(VULKAN_STATIC_LIBRARY NAMES vkstatic.1 HINTS + "$ENV{VULKAN_SDK}/Bin" + "$ENV{VK_SDK_PATH}/Bin") + else() + find_library(VULKAN_LIBRARY NAMES vulkan-1 HINTS + "$ENV{VULKAN_SDK}/Bin32" + "$ENV{VK_SDK_PATH}/Bin32") + endif() +else() + find_path(VULKAN_INCLUDE_DIR NAMES vulkan/vulkan.h HINTS + "$ENV{VULKAN_SDK}/include") + find_library(VULKAN_LIBRARY NAMES vulkan HINTS + "$ENV{VULKAN_SDK}/lib") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Vulkan DEFAULT_MSG VULKAN_LIBRARY VULKAN_INCLUDE_DIR) + +mark_as_advanced(VULKAN_INCLUDE_DIR VULKAN_LIBRARY VULKAN_STATIC_LIBRARY) + diff --git a/apps/exampleViewer/common/glfw/CMake/modules/FindWaylandProtocols.cmake b/apps/exampleViewer/common/glfw/CMake/modules/FindWaylandProtocols.cmake new file mode 100644 index 0000000000..8eb83f27ef --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/modules/FindWaylandProtocols.cmake @@ -0,0 +1,26 @@ +find_package(PkgConfig) + +pkg_check_modules(WaylandProtocols QUIET wayland-protocols>=${WaylandProtocols_FIND_VERSION}) + +execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable=pkgdatadir wayland-protocols + OUTPUT_VARIABLE WaylandProtocols_PKGDATADIR + RESULT_VARIABLE _pkgconfig_failed) +if (_pkgconfig_failed) + message(FATAL_ERROR "Missing wayland-protocols pkgdatadir") +endif() + +string(REGEX REPLACE "[\r\n]" "" WaylandProtocols_PKGDATADIR "${WaylandProtocols_PKGDATADIR}") + +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR + WaylandProtocols_FOUND + REQUIRED_VARS + WaylandProtocols_PKGDATADIR + VERSION_VAR + WaylandProtocols_VERSION + HANDLE_COMPONENTS +) + +set(WAYLAND_PROTOCOLS_FOUND ${WaylandProtocols_FOUND}) +set(WAYLAND_PROTOCOLS_PKGDATADIR ${WaylandProtocols_PKGDATADIR}) +set(WAYLAND_PROTOCOLS_VERSION ${WaylandProtocols_VERSION}) diff --git a/apps/exampleViewer/common/glfw/CMake/modules/FindXKBCommon.cmake b/apps/exampleViewer/common/glfw/CMake/modules/FindXKBCommon.cmake new file mode 100644 index 0000000000..0f571eeacb --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/modules/FindXKBCommon.cmake @@ -0,0 +1,34 @@ +# - Try to find XKBCommon +# Once done, this will define +# +# XKBCOMMON_FOUND - System has XKBCommon +# XKBCOMMON_INCLUDE_DIRS - The XKBCommon include directories +# XKBCOMMON_LIBRARIES - The libraries needed to use XKBCommon +# XKBCOMMON_DEFINITIONS - Compiler switches required for using XKBCommon + +find_package(PkgConfig) +pkg_check_modules(PC_XKBCOMMON QUIET xkbcommon) +set(XKBCOMMON_DEFINITIONS ${PC_XKBCOMMON_CFLAGS_OTHER}) + +find_path(XKBCOMMON_INCLUDE_DIR + NAMES xkbcommon/xkbcommon.h + HINTS ${PC_XKBCOMMON_INCLUDE_DIR} ${PC_XKBCOMMON_INCLUDE_DIRS} +) + +find_library(XKBCOMMON_LIBRARY + NAMES xkbcommon + HINTS ${PC_XKBCOMMON_LIBRARY} ${PC_XKBCOMMON_LIBRARY_DIRS} +) + +set(XKBCOMMON_LIBRARIES ${XKBCOMMON_LIBRARY}) +set(XKBCOMMON_LIBRARY_DIRS ${XKBCOMMON_LIBRARY_DIRS}) +set(XKBCOMMON_INCLUDE_DIRS ${XKBCOMMON_INCLUDE_DIR}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(XKBCommon DEFAULT_MSG + XKBCOMMON_LIBRARY + XKBCOMMON_INCLUDE_DIR +) + +mark_as_advanced(XKBCOMMON_LIBRARY XKBCOMMON_INCLUDE_DIR) + diff --git a/apps/exampleViewer/common/glfw/CMake/x86_64-w64-mingw32.cmake b/apps/exampleViewer/common/glfw/CMake/x86_64-w64-mingw32.cmake new file mode 100644 index 0000000000..84b2c701ec --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMake/x86_64-w64-mingw32.cmake @@ -0,0 +1,13 @@ +# Define the environment for cross compiling from Linux to Win32 +SET(CMAKE_SYSTEM_NAME Windows) # Target system name +SET(CMAKE_SYSTEM_VERSION 1) +SET(CMAKE_C_COMPILER "x86_64-w64-mingw32-gcc") +SET(CMAKE_CXX_COMPILER "x86_64-w64-mingw32-g++") +SET(CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres") +SET(CMAKE_RANLIB "x86_64-w64-mingw32-ranlib") + +# Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH "/usr/x86_64-w64-mingw32") +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/apps/exampleViewer/common/glfw/CMakeLists.txt b/apps/exampleViewer/common/glfw/CMakeLists.txt new file mode 100644 index 0000000000..0a67af3a03 --- /dev/null +++ b/apps/exampleViewer/common/glfw/CMakeLists.txt @@ -0,0 +1,419 @@ +set(CMAKE_LEGACY_CYGWIN_WIN32 OFF) + +project(GLFW C) + +cmake_minimum_required(VERSION 2.8.12) + +if (NOT CMAKE_VERSION VERSION_LESS "3.0") + # Until all major package systems have moved to CMake 3, + # we stick with the older INSTALL_NAME_DIR mechanism + cmake_policy(SET CMP0042 OLD) +endif() + +set(GLFW_VERSION_MAJOR "3") +set(GLFW_VERSION_MINOR "3") +set(GLFW_VERSION_PATCH "0") +set(GLFW_VERSION_EXTRA "") +set(GLFW_VERSION "${GLFW_VERSION_MAJOR}.${GLFW_VERSION_MINOR}") +set(GLFW_VERSION_FULL "${GLFW_VERSION}.${GLFW_VERSION_PATCH}${GLFW_VERSION_EXTRA}") +set(LIB_SUFFIX "" CACHE STRING "Takes an empty string or 64. Directory where lib will be installed: lib or lib64") + +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +option(BUILD_SHARED_LIBS "Build shared libraries" OFF) +option(GLFW_BUILD_EXAMPLES "Build the GLFW example programs" ON) +option(GLFW_BUILD_TESTS "Build the GLFW test programs" ON) +option(GLFW_BUILD_DOCS "Build the GLFW documentation" ON) +option(GLFW_INSTALL "Generate installation target" ON) +option(GLFW_VULKAN_STATIC "Use the Vulkan loader statically linked into application" OFF) +option(GLFW_DOCUMENT_INTERNALS "Include internals in documentation" OFF) + +if (WIN32) + option(GLFW_USE_HYBRID_HPG "Force use of high-performance GPU on hybrid systems" OFF) +endif() + +if (APPLE) + option(GLFW_USE_CHDIR "Make glfwInit chdir to Contents/Resources" ON) + option(GLFW_USE_MENUBAR "Populate the menu bar on first window creation" ON) + option(GLFW_USE_RETINA "Use the full resolution of Retina displays" ON) +endif() + +if (UNIX AND NOT APPLE) + option(GLFW_USE_WAYLAND "Use Wayland for window creation" OFF) + option(GLFW_USE_MIR "Use Mir for window creation" OFF) +endif() + +if (MSVC) + option(USE_MSVC_RUNTIME_LIBRARY_DLL "Use MSVC runtime library DLL" ON) +endif() + +if (BUILD_SHARED_LIBS) + set(_GLFW_BUILD_DLL 1) +endif() + +if (BUILD_SHARED_LIBS AND UNIX) + # On Unix-like systems, shared libraries can use the soname system. + set(GLFW_LIB_NAME glfw) +else() + set(GLFW_LIB_NAME glfw3) +endif() + +if (GLFW_VULKAN_STATIC) + set(_GLFW_VULKAN_STATIC 1) +endif() + +list(APPEND CMAKE_MODULE_PATH "${GLFW_SOURCE_DIR}/CMake/modules") + +find_package(Threads REQUIRED) +find_package(Vulkan) + +if (GLFW_BUILD_DOCS) + set(DOXYGEN_SKIP_DOT TRUE) + find_package(Doxygen) +endif() + +#-------------------------------------------------------------------- +# Set compiler specific flags +#-------------------------------------------------------------------- +if (MSVC) + if (NOT USE_MSVC_RUNTIME_LIBRARY_DLL) + foreach (flag CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO) + + if (${flag} MATCHES "/MD") + string(REGEX REPLACE "/MD" "/MT" ${flag} "${${flag}}") + endif() + if (${flag} MATCHES "/MDd") + string(REGEX REPLACE "/MDd" "/MTd" ${flag} "${${flag}}") + endif() + + endforeach() + endif() +endif() + +if (MINGW) + # Workaround for legacy MinGW not providing XInput and DirectInput + include(CheckIncludeFile) + + check_include_file(dinput.h DINPUT_H_FOUND) + check_include_file(xinput.h XINPUT_H_FOUND) + if (NOT DINPUT_H_FOUND OR NOT XINPUT_H_FOUND) + list(APPEND glfw_INCLUDE_DIRS "${GLFW_SOURCE_DIR}/deps/mingw") + endif() + + # Enable link-time exploit mitigation features enabled by default on MSVC + include(CheckCCompilerFlag) + + # Compatibility with data execution prevention (DEP) + set(CMAKE_REQUIRED_FLAGS "-Wl,--nxcompat") + check_c_compiler_flag("" _GLFW_HAS_DEP) + if (_GLFW_HAS_DEP) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--nxcompat ${CMAKE_SHARED_LINKER_FLAGS}") + endif() + + # Compatibility with address space layout randomization (ASLR) + set(CMAKE_REQUIRED_FLAGS "-Wl,--dynamicbase") + check_c_compiler_flag("" _GLFW_HAS_ASLR) + if (_GLFW_HAS_ASLR) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--dynamicbase ${CMAKE_SHARED_LINKER_FLAGS}") + endif() + + # Compatibility with 64-bit address space layout randomization (ASLR) + set(CMAKE_REQUIRED_FLAGS "-Wl,--high-entropy-va") + check_c_compiler_flag("" _GLFW_HAS_64ASLR) + if (_GLFW_HAS_64ASLR) + set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--high-entropy-va ${CMAKE_SHARED_LINKER_FLAGS}") + endif() +endif() + +#-------------------------------------------------------------------- +# Detect and select backend APIs +#-------------------------------------------------------------------- +if (WIN32) + set(_GLFW_WIN32 1) + message(STATUS "Using Win32 for window creation") +elseif (APPLE) + set(_GLFW_COCOA 1) + message(STATUS "Using Cocoa for window creation") +elseif (UNIX) + if (GLFW_USE_WAYLAND) + set(_GLFW_WAYLAND 1) + message(STATUS "Using Wayland for window creation") + elseif (GLFW_USE_MIR) + set(_GLFW_MIR 1) + message(STATUS "Using Mir for window creation") + else() + set(_GLFW_X11 1) + message(STATUS "Using X11 for window creation") + endif() +else() + message(FATAL_ERROR "No supported platform was detected") +endif() + +#-------------------------------------------------------------------- +# Add Vulkan static library if requested +#-------------------------------------------------------------------- +if (GLFW_VULKAN_STATIC) + if (VULKAN_FOUND AND VULKAN_STATIC_LIBRARY) + list(APPEND glfw_LIBRARIES ${VULKAN_STATIC_LIBRARY}) + else() + if (BUILD_SHARED_LIBS OR GLFW_BUILD_EXAMPLES OR GLFW_BUILD_TESTS) + message(FATAL_ERROR "Vulkan loader static library not found") + else() + message(WARNING "Vulkan loader static library not found") + endif() + endif() +endif() + +#-------------------------------------------------------------------- +# Find and add Unix math and time libraries +#-------------------------------------------------------------------- +if (UNIX AND NOT APPLE) + find_library(RT_LIBRARY rt) + mark_as_advanced(RT_LIBRARY) + if (RT_LIBRARY) + list(APPEND glfw_LIBRARIES "${RT_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lrt") + endif() + + find_library(MATH_LIBRARY m) + mark_as_advanced(MATH_LIBRARY) + if (MATH_LIBRARY) + list(APPEND glfw_LIBRARIES "${MATH_LIBRARY}") + list(APPEND glfw_PKG_LIBS "-lm") + endif() + + if (CMAKE_DL_LIBS) + list(APPEND glfw_LIBRARIES "${CMAKE_DL_LIBS}") + list(APPEND glfw_PKG_LIBS "-l${CMAKE_DL_LIBS}") + endif() +endif() + +#-------------------------------------------------------------------- +# Use Win32 for window creation +#-------------------------------------------------------------------- +if (_GLFW_WIN32) + + list(APPEND glfw_PKG_LIBS "-lgdi32") + + if (GLFW_USE_HYBRID_HPG) + set(_GLFW_USE_HYBRID_HPG 1) + endif() +endif() + +#-------------------------------------------------------------------- +# Use X11 for window creation +#-------------------------------------------------------------------- +if (_GLFW_X11) + + find_package(X11 REQUIRED) + + list(APPEND glfw_PKG_DEPS "x11") + + # Set up library and include paths + list(APPEND glfw_INCLUDE_DIRS "${X11_X11_INCLUDE_PATH}") + list(APPEND glfw_LIBRARIES "${X11_X11_LIB}" "${CMAKE_THREAD_LIBS_INIT}") + + # Check for XRandR (modern resolution switching and gamma control) + if (NOT X11_Xrandr_FOUND) + message(FATAL_ERROR "The RandR library and headers were not found") + endif() + + list(APPEND glfw_INCLUDE_DIRS "${X11_Xrandr_INCLUDE_PATH}") + list(APPEND glfw_LIBRARIES "${X11_Xrandr_LIB}") + list(APPEND glfw_PKG_DEPS "xrandr") + + # Check for Xinerama (legacy multi-monitor support) + if (NOT X11_Xinerama_FOUND) + message(FATAL_ERROR "The Xinerama library and headers were not found") + endif() + + list(APPEND glfw_INCLUDE_DIRS "${X11_Xinerama_INCLUDE_PATH}") + list(APPEND glfw_LIBRARIES "${X11_Xinerama_LIB}") + list(APPEND glfw_PKG_DEPS "xinerama") + + # Check for Xf86VidMode (fallback gamma control) + if (X11_xf86vmode_FOUND) + list(APPEND glfw_INCLUDE_DIRS "${X11_xf86vmode_INCLUDE_PATH}") + list(APPEND glfw_PKG_DEPS "xxf86vm") + + if (X11_Xxf86vm_LIB) + list(APPEND glfw_LIBRARIES "${X11_Xxf86vm_LIB}") + else() + # Backwards compatibility (see CMake bug 0006976) + list(APPEND glfw_LIBRARIES Xxf86vm) + endif() + + set(_GLFW_HAS_XF86VM TRUE) + endif() + + # Check for Xkb (X keyboard extension) + if (NOT X11_Xkb_FOUND) + message(FATAL_ERROR "The X keyboard extension headers were not found") + endif() + + list(APPEND glfw_INCLUDE_DIR "${X11_Xkb_INCLUDE_PATH}") + + # Check for Xcursor + if (NOT X11_Xcursor_FOUND) + message(FATAL_ERROR "The Xcursor libraries and headers were not found") + endif() + + list(APPEND glfw_INCLUDE_DIR "${X11_Xcursor_INCLUDE_PATH}") + list(APPEND glfw_LIBRARIES "${X11_Xcursor_LIB}") + list(APPEND glfw_PKG_DEPS "xcursor") + +endif() + +#-------------------------------------------------------------------- +# Use Wayland for window creation +#-------------------------------------------------------------------- +if (_GLFW_WAYLAND) + find_package(ECM REQUIRED NO_MODULE) + list(APPEND CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) + + find_package(Wayland REQUIRED) + find_package(WaylandScanner REQUIRED) + find_package(WaylandProtocols 1.1 REQUIRED) + + list(APPEND glfw_PKG_DEPS "wayland-egl") + + list(APPEND glfw_INCLUDE_DIRS "${Wayland_INCLUDE_DIR}") + list(APPEND glfw_LIBRARIES "${Wayland_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") + + find_package(XKBCommon REQUIRED) + list(APPEND glfw_PKG_DEPS "xkbcommon") + list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") + list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}") +endif() + +#-------------------------------------------------------------------- +# Use Mir for window creation +#-------------------------------------------------------------------- +if (_GLFW_MIR) + find_package(Mir REQUIRED) + list(APPEND glfw_PKG_DEPS "mirclient") + + list(APPEND glfw_INCLUDE_DIRS "${MIR_INCLUDE_DIR}") + list(APPEND glfw_LIBRARIES "${MIR_LIBRARIES}" "${CMAKE_THREAD_LIBS_INIT}") + + find_package(XKBCommon REQUIRED) + list(APPEND glfw_PKG_DEPS "xkbcommon") + list(APPEND glfw_INCLUDE_DIRS "${XKBCOMMON_INCLUDE_DIRS}") + list(APPEND glfw_LIBRARIES "${XKBCOMMON_LIBRARY}") +endif() + +#-------------------------------------------------------------------- +# Use Cocoa for window creation and NSOpenGL for context creation +#-------------------------------------------------------------------- +if (_GLFW_COCOA) + + if (GLFW_USE_MENUBAR) + set(_GLFW_USE_MENUBAR 1) + endif() + + if (GLFW_USE_CHDIR) + set(_GLFW_USE_CHDIR 1) + endif() + + if (GLFW_USE_RETINA) + set(_GLFW_USE_RETINA 1) + endif() + + # Set up library and include paths + find_library(COCOA_FRAMEWORK Cocoa) + find_library(IOKIT_FRAMEWORK IOKit) + find_library(CORE_FOUNDATION_FRAMEWORK CoreFoundation) + find_library(CORE_VIDEO_FRAMEWORK CoreVideo) + mark_as_advanced(COCOA_FRAMEWORK + IOKIT_FRAMEWORK + CORE_FOUNDATION_FRAMEWORK + CORE_VIDEO_FRAMEWORK) + list(APPEND glfw_LIBRARIES "${COCOA_FRAMEWORK}" + "${IOKIT_FRAMEWORK}" + "${CORE_FOUNDATION_FRAMEWORK}" + "${CORE_VIDEO_FRAMEWORK}") + + set(glfw_PKG_DEPS "") + set(glfw_PKG_LIBS "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo") +endif() + +#-------------------------------------------------------------------- +# Export GLFW library dependencies +#-------------------------------------------------------------------- +foreach(arg ${glfw_PKG_DEPS}) + set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} ${arg}") +endforeach() +foreach(arg ${glfw_PKG_LIBS}) + set(GLFW_PKG_LIBS "${GLFW_PKG_LIBS} ${arg}") +endforeach() + +#-------------------------------------------------------------------- +# Create generated files +#-------------------------------------------------------------------- +include(CMakePackageConfigHelpers) + +set(GLFW_CONFIG_PATH "lib${LIB_SUFFIX}/cmake/glfw3") + +configure_package_config_file(src/glfw3Config.cmake.in + src/glfw3Config.cmake + INSTALL_DESTINATION "${GLFW_CONFIG_PATH}" + NO_CHECK_REQUIRED_COMPONENTS_MACRO) + +write_basic_package_version_file(src/glfw3ConfigVersion.cmake + VERSION ${GLFW_VERSION_FULL} + COMPATIBILITY SameMajorVersion) + +configure_file(src/glfw_config.h.in src/glfw_config.h @ONLY) + +configure_file(src/glfw3.pc.in src/glfw3.pc @ONLY) + +#-------------------------------------------------------------------- +# Add subdirectories +#-------------------------------------------------------------------- +add_subdirectory(src) + +if (GLFW_BUILD_EXAMPLES) + add_subdirectory(examples) +endif() + +if (GLFW_BUILD_TESTS) + add_subdirectory(tests) +endif() + +if (DOXYGEN_FOUND AND GLFW_BUILD_DOCS) + add_subdirectory(docs) +endif() + +#-------------------------------------------------------------------- +# Install files other than the library +# The library is installed by src/CMakeLists.txt +#-------------------------------------------------------------------- +if (GLFW_INSTALL) + install(DIRECTORY include/GLFW DESTINATION include + FILES_MATCHING PATTERN glfw3.h PATTERN glfw3native.h) + + install(FILES "${GLFW_BINARY_DIR}/src/glfw3Config.cmake" + "${GLFW_BINARY_DIR}/src/glfw3ConfigVersion.cmake" + DESTINATION "${GLFW_CONFIG_PATH}") + + install(EXPORT glfwTargets FILE glfw3Targets.cmake + EXPORT_LINK_INTERFACE_LIBRARIES + DESTINATION "${GLFW_CONFIG_PATH}") + install(FILES "${GLFW_BINARY_DIR}/src/glfw3.pc" + DESTINATION "lib${LIB_SUFFIX}/pkgconfig") + + # Only generate this target if no higher-level project already has + if (NOT TARGET uninstall) + configure_file(cmake_uninstall.cmake.in + cmake_uninstall.cmake IMMEDIATE @ONLY) + + add_custom_target(uninstall + "${CMAKE_COMMAND}" -P + "${GLFW_BINARY_DIR}/cmake_uninstall.cmake") + endif() +endif() + diff --git a/apps/exampleViewer/common/glfw/COPYING.txt b/apps/exampleViewer/common/glfw/COPYING.txt new file mode 100644 index 0000000000..ad16462a9e --- /dev/null +++ b/apps/exampleViewer/common/glfw/COPYING.txt @@ -0,0 +1,22 @@ +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2016 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. + diff --git a/apps/exampleViewer/common/glfw/README.md b/apps/exampleViewer/common/glfw/README.md new file mode 100644 index 0000000000..a17895952a --- /dev/null +++ b/apps/exampleViewer/common/glfw/README.md @@ -0,0 +1,262 @@ +# GLFW + +[![Build status](https://travis-ci.org/glfw/glfw.svg?branch=master)](https://travis-ci.org/glfw/glfw) +[![Build status](https://ci.appveyor.com/api/projects/status/0kf0ct9831i5l6sp/branch/master?svg=true)](https://ci.appveyor.com/project/elmindreda/glfw) +[![Coverity Scan](https://scan.coverity.com/projects/4884/badge.svg)](https://scan.coverity.com/projects/glfw-glfw) + +## Introduction + +GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan +application development. It provides a simple, platform-independent API for +creating windows, contexts and surfaces, reading input, handling events, etc. + +GLFW is licensed under the [zlib/libpng +license](https://opensource.org/licenses/Zlib). + +The latest stable release is version 3.2.1. + +See the [downloads](http://www.glfw.org/download.html) page for details and +files, or fetch the `latest` branch, which always points to the latest stable +release. Each release starting with 3.0 also has a corresponding [annotated +tag](https://github.com/glfw/glfw/releases) with source and binary archives. + +This is a development branch for version 3.3, which is _not yet described_. + +If you are new to GLFW, you may find the +[tutorial](http://www.glfw.org/docs/latest/quick.html) for GLFW +3 useful. If you have used GLFW 2 in the past, there is a +[transition guide](http://www.glfw.org/docs/latest/moving.html) for moving to +the GLFW 3 API. + + +## Compiling GLFW + +GLFW itself requires only the headers and libraries for your window system. It +does not need the headers for any context creation API (WGL, GLX, EGL, NSGL) or +rendering API (OpenGL, OpenGL ES, Vulkan) to enable support for them. + +GLFW supports compilation on Windows with Visual C++ 2010 and later, MinGW and +MinGW-w64, on macOS with Clang and on Linux and other Unix-like systems with GCC +and Clang. It will likely compile in other environments as well, but this is +not regularly tested. + +There are also [pre-compiled Windows +binaries](http://www.glfw.org/download.html) available for all compilers +supported on that platform. + +See the [compilation guide](http://www.glfw.org/docs/latest/compile.html) in the +documentation for more information. + + +## Using GLFW + +See the [building application guide](http://www.glfw.org/docs/latest/build.html) +guide in the documentation for more information. + + +## System requirements + +GLFW supports Windows XP and later, OS X 10.7 Lion and later, and Linux and +other Unix-like systems with the X Window System. Experimental implementations +for the Wayland protocol and the Mir display server are available but not yet +officially supported. + +See the [compatibility guide](http://www.glfw.org/docs/latest/compat.html) +in the documentation for more information. + + +## Dependencies + +GLFW itself depends only on the headers and libraries for your window system. + +The examples and test programs depend on a number of tiny libraries. These are +located in the `deps/` directory. + + - [getopt\_port](https://github.com/kimgr/getopt_port/) for examples + with command-line options + - [TinyCThread](https://github.com/tinycthread/tinycthread) for threaded + examples + - An OpenGL 3.2 core loader generated by + [glad](https://github.com/Dav1dde/glad) for examples using modern OpenGL + - [linmath.h](https://github.com/datenwolf/linmath.h) for linear algebra in + examples + - [Nuklear](https://github.com/vurtun/nuklear) for test and example UI + - [Vulkan headers](https://www.khronos.org/registry/vulkan/) for Vulkan tests + +The Vulkan example additionally requires the Vulkan SDK to be installed, or it +will not be included in the build. + +The documentation is generated with [Doxygen](http://doxygen.org/). If CMake +does not find Doxygen, the documentation will not be generated when you build. + + +## Reporting bugs + +Bugs are reported to our [issue tracker](https://github.com/glfw/glfw/issues). +Please check the [contribution +guide](https://github.com/glfw/glfw/blob/master/.github/CONTRIBUTING.md) for +information on what to include when reporting a bug. + + +## Changelog + +- Added `glfwGetKeyScancode` function that allows retrieving platform dependent + scancodes for keys (#830) +- Added `glfwSetWindowMaximizeCallback` and `GLFWwindowmaximizefun` for + receiving window maximization events (#778) +- Bugfix: Calling `glfwMaximizeWindow` on a full screen window was not ignored +- Bugfix: `GLFW_INCLUDE_VULKAN` could not be combined with the corresponding + OpenGL and OpenGL ES header macros +- [Win32] Bugfix: Undecorated windows could not be iconified by the user (#861) +- [Cocoa] Bugfix: Disabling window aspect ratio would assert (#852) +- [Cocoa] Bugfix: Window creation failed to set first responder (#876,#883) +- [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) + + +## Contact + +On [glfw.org](http://www.glfw.org/) you can find the latest version of GLFW, as +well as news, documentation and other information about the project. + +If you have questions related to the use of GLFW, we have a +[forum](http://discourse.glfw.org/), and the `#glfw` IRC channel on +[Freenode](http://freenode.net/). + +If you have a bug to report, a patch to submit or a feature you'd like to +request, please file it in the +[issue tracker](https://github.com/glfw/glfw/issues) on GitHub. + +Finally, if you're interested in helping out with the development of GLFW or +porting it to your favorite platform, join us on the forum, GitHub or IRC. + + +## Acknowledgements + +GLFW exists because people around the world donated their time and lent their +skills. + + - Bobyshev Alexander + - artblanc + - arturo + - Matt Arsenault + - Keith Bauer + - John Bartholomew + - Niklas Behrens + - Niklas Bergström + - Doug Binks + - blanco + - Kyle Brenneman + - Martin Capitanio + - Chi-kwan Chan + - Lambert Clara + - Andrew Corrigan + - Noel Cower + - Jarrod Davis + - Olivier Delannoy + - Paul R. Deppe + - Michael Dickens + - Роман Донченко + - Mario Dorn + - Jonathan Dummer + - Ralph Eastwood + - Siavash Eliasi + - Michael Fogleman + - Gerald Franz + - Mário Freitas + - GeO4d + - Marcus Geelnard + - Eloi Marín Gratacós + - Stefan Gustavson + - Sylvain Hellegouarch + - Matthew Henry + - heromyth + - Lucas Hinderberger + - Paul Holden + - Warren Hu + - IntellectualKitty + - Aaron Jacobs + - Erik S. V. Jansson + - Toni Jovanoski + - Arseny Kapoulkine + - Osman Keskin + - Cameron King + - Peter Knut + - Christoph Kubisch + - Eric Larson + - Robin Leffmann + - Glenn Lewis + - Shane Liesegang + - Eyal Lotem + - Дмитри Малышев + - Martins Mozeiko + - Tristam MacDonald + - Hans Mackowiak + - Zbigniew Mandziejewicz + - Kyle McDonald + - David Medlock + - Bryce Mehring + - Jonathan Mercier + - Marcel Metz + - Jonathan Miller + - Kenneth Miller + - Bruce Mitchener + - Jack Moffitt + - Jeff Molofee + - Jon Morton + - Pierre Moulon + - Julian Møller + - Kamil Nowakowski + - Ozzy + - Andri Pálsson + - Peoro + - Braden Pellett + - Arturo J. Pérez + - Orson Peters + - Emmanuel Gil Peyrot + - Cyril Pichard + - Pieroman + - Philip Rideout + - Jorge Rodriguez + - Ed Ropple + - Aleksey Rybalkin + - Riku Salminen + - Brandon Schaefer + - Sebastian Schuberth + - Matt Sealey + - SephiRok + - Steve Sexton + - Systemcluster + - Yoshiki Shibukawa + - Dmitri Shuralyov + - Daniel Skorupski + - Bradley Smith + - Patrick Snape + - Julian Squires + - Johannes Stein + - Michael Stocker + - Justin Stoecker + - Elviss Strazdins + - Nathan Sweet + - TTK-Bandit + - Sergey Tikhomirov + - Arthur Tombs + - Ioannis Tsakpinis + - Samuli Tuomola + - urraka + - Elias Vanderstuyft + - Jari Vetoniemi + - Ricardo Vieira + - Nicholas Vitovitch + - Simon Voordouw + - Torsten Walluhn + - Patrick Walton + - Xo Wang + - Jay Weisskopf + - Frank Wille + - yuriks + - Santi Zupancic + - Jonas Ådahl + - Lasse Öörni + - All the unmentioned and anonymous contributors in the GLFW community, for bug + reports, patches, feedback, testing and encouragement + diff --git a/apps/exampleViewer/common/glfw/cmake_uninstall.cmake.in b/apps/exampleViewer/common/glfw/cmake_uninstall.cmake.in new file mode 100644 index 0000000000..4ea57b1cfb --- /dev/null +++ b/apps/exampleViewer/common/glfw/cmake_uninstall.cmake.in @@ -0,0 +1,29 @@ + +if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif() + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") + +foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (EXISTS "$ENV{DESTDIR}${file}") + exec_program("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if (NOT "${rm_retval}" STREQUAL 0) + MESSAGE(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif() + elseif (IS_SYMLINK "$ENV{DESTDIR}${file}") + EXEC_PROGRAM("@CMAKE_COMMAND@" ARGS "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if (NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing symlink \"$ENV{DESTDIR}${file}\"") + endif() + else() + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif() +endforeach() + diff --git a/apps/exampleViewer/common/glfw/docs/CMakeLists.txt b/apps/exampleViewer/common/glfw/docs/CMakeLists.txt new file mode 100644 index 0000000000..c030ac735d --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/CMakeLists.txt @@ -0,0 +1,32 @@ + +set(glfw_DOCS_SOURCES + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h" + "${GLFW_SOURCE_DIR}/docs/main.dox" + "${GLFW_SOURCE_DIR}/docs/news.dox" + "${GLFW_SOURCE_DIR}/docs/moving.dox" + "${GLFW_SOURCE_DIR}/docs/quick.dox" + "${GLFW_SOURCE_DIR}/docs/compile.dox" + "${GLFW_SOURCE_DIR}/docs/build.dox" + "${GLFW_SOURCE_DIR}/docs/intro.dox" + "${GLFW_SOURCE_DIR}/docs/context.dox" + "${GLFW_SOURCE_DIR}/docs/monitor.dox" + "${GLFW_SOURCE_DIR}/docs/window.dox" + "${GLFW_SOURCE_DIR}/docs/input.dox" + "${GLFW_SOURCE_DIR}/docs/vulkan.dox" + "${GLFW_SOURCE_DIR}/docs/compat.dox") + +if (GLFW_DOCUMENT_INTERNALS) + list(APPEND glfw_DOCS_SOURCES "${GLFW_SOURCE_DIR}/src/internal.h") +endif() + +foreach(arg ${glfw_DOCS_SOURCES}) + set(GLFW_DOCS_SOURCES "${GLFW_DOCS_SOURCES} ${arg}") +endforeach() + +configure_file(Doxyfile.in Doxyfile @ONLY) + +add_custom_target(docs ALL "${DOXYGEN_EXECUTABLE}" + WORKING_DIRECTORY "${GLFW_BINARY_DIR}/docs" + COMMENT "Generating HTML documentation" VERBATIM) + diff --git a/apps/exampleViewer/common/glfw/docs/Doxyfile.in b/apps/exampleViewer/common/glfw/docs/Doxyfile.in new file mode 100644 index 0000000000..6dd8dbe44e --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/Doxyfile.in @@ -0,0 +1,1860 @@ +# Doxyfile 1.8.3.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = "GLFW" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @GLFW_VERSION_FULL@ + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "A multi-platform library for OpenGL, window and input" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @GLFW_BINARY_DIR@/docs + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = NO + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = YES + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. Note that you specify absolute paths here, but also +# relative paths, which will be relative from the directory where doxygen is +# started. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = "thread_safety=@par Thread safety\n" \ + "pointer_lifetime=@par Pointer lifetime\n" \ + "analysis=@par Analysis\n" \ + "reentrancy=@par Reentrancy\n" \ + "errors=@par Errors\n" \ + "glfw3=@par\n__GLFW 3:__" \ + "x11=__X11:__" \ + "wayland=__Wayland:__" \ + "win32=__Windows:__" \ + "macos=__macOS:__" + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, +# and language is one of the parsers supported by doxygen: IDL, Java, +# Javascript, CSharp, C, C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, +# C++. For instance to make doxygen treat .inc files as Fortran files (default +# is PHP), and .f files as C (default is Fortran), use: inc=Fortran f=C. Note +# that for custom extensions you also need to set FILE_PATTERNS otherwise the +# files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# When enabled doxygen tries to link words that correspond to documented classes, +# or namespaces to their corresponding documentation. Such a link can be +# prevented in individual cases by by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES (the +# default) will make doxygen replace the get and set methods by a property in +# the documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = NO + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal +# scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = YES + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if section-label ... \endif +# and \cond section-label ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. Do not use +# file names with spaces, bibtex cannot handle them. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = @GLFW_BINARY_DIR@/docs/warnings.txt + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @GLFW_DOCS_SOURCES@ + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.h *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = APIENTRY GLFWAPI + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @GLFW_SOURCE_DIR@/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MD_FILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page (index.html). +# This can be useful if you have a project on for instance GitHub and want reuse +# the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = glfw GLFW_ + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = @GLFW_SOURCE_DIR@/docs/header.html + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @GLFW_SOURCE_DIR@/docs/footer.html + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If left blank doxygen will +# generate a default style sheet. Note that it is recommended to use +# HTML_EXTRA_STYLESHEET instead of this one, as it is more robust and this +# tag will in the future become obsolete. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify an additional +# user-defined cascading style sheet that is included after the standard +# style sheets created by doxygen. Using this option one can overrule +# certain style aspects. This is preferred over using HTML_STYLESHEET +# since it does not replace the standard style sheet and is therefor more +# robust against future updates. Doxygen will copy the style sheet file to +# the output directory. + +HTML_EXTRA_STYLESHEET = @GLFW_SOURCE_DIR@/docs/extra.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = @GLFW_SOURCE_DIR@/docs/spaces.svg + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely +# identify the documentation publisher. This should be a reverse domain-name +# style string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# +# Qt Help Project / Custom Filters. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# +# Qt Help Project / Filter Attributes. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 300 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# thA MathJax output. Supported types are HTML-CSS, NativeMML (i.e. MathML) and +# SVG. The default value is HTML-CSS, which is slower, but has the best +# compatibility. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a web server instead of a web client using Javascript. +# There are two flavours of web server based search depending on the +# EXTERNAL_SEARCH setting. When disabled, doxygen will generate a PHP script for +# searching and an index file used by the script. When EXTERNAL_SEARCH is +# enabled the indexing and searching needs to be provided by external tools. +# See the manual for details. + +SERVER_BASED_SEARCH = NO + +# When EXTERNAL_SEARCH is enabled doxygen will no longer generate the PHP +# script for searching. Instead the search results are written to an XML file +# which needs to be processed by an external indexer. Doxygen will invoke an +# external search engine pointed to by the SEARCHENGINE_URL option to obtain +# the search results. Doxygen ships with an example indexer (doxyindexer) and +# search engine (doxysearch.cgi) which are based on the open source search engine +# library Xapian. See the manual for configuration details. + +EXTERNAL_SEARCH = NO + +# The SEARCHENGINE_URL should point to a search engine hosted by a web server +# which will returned the search results when EXTERNAL_SEARCH is enabled. +# Doxygen ships with an example search engine (doxysearch) which is based on +# the open source search engine library Xapian. See the manual for configuration +# details. + +SEARCHENGINE_URL = + +# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed +# search data is written to a file for indexing by an external tool. With the +# SEARCHDATA_FILE tag the name of this file can be specified. + +SEARCHDATA_FILE = searchdata.xml + +# When SERVER_BASED_SEARCH AND EXTERNAL_SEARCH are both enabled the +# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is +# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple +# projects and redirect the results back to the right project. + +EXTERNAL_SEARCH_ID = + +# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen +# projects other than the one defined by this configuration file, but that are +# all added to the same external search index. Each project needs to have a +# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id +# of to a relative location where the documentation can be found. +# The format is: EXTRA_SEARCH_MAPPINGS = id1=loc1 id2=loc2 ... + +EXTRA_SEARCH_MAPPINGS = + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = YES + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = GLFWAPI= \ + GLFW_EXPOSE_NATIVE_WIN32 \ + GLFW_EXPOSE_NATIVE_WGL \ + GLFW_EXPOSE_NATIVE_X11 \ + GLFW_EXPOSE_NATIVE_WAYLAND \ + GLFW_EXPOSE_NATIVE_MIR \ + GLFW_EXPOSE_NATIVE_GLX \ + GLFW_EXPOSE_NATIVE_COCOA \ + GLFW_EXPOSE_NATIVE_NSGL \ + GLFW_EXPOSE_NATIVE_EGL \ + VK_VERSION_1_0 + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = NO + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/apps/exampleViewer/common/glfw/docs/DoxygenLayout.xml b/apps/exampleViewer/common/glfw/docs/DoxygenLayout.xml new file mode 100644 index 0000000000..7917f91ed7 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/DoxygenLayout.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/exampleViewer/common/glfw/docs/compat.dox b/apps/exampleViewer/common/glfw/docs/compat.dox new file mode 100644 index 0000000000..624dc3b17d --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/compat.dox @@ -0,0 +1,224 @@ +/*! + +@page compat_guide Standards conformance + +@tableofcontents + +This guide describes the various API extensions used by this version of GLFW. +It lists what are essentially implementation details, but which are nonetheless +vital knowledge for developers intending to deploy their applications on a wide +range of machines. + +The information in this guide is not a part of GLFW API, but merely +preconditions for some parts of the library to function on a given machine. Any +part of this information may change in future versions of GLFW and that will not +be considered a breaking API change. + + +@section compat_x11 X11 extensions, protocols and IPC standards + +As GLFW uses Xlib directly, without any intervening toolkit +library, it has sole responsibility for interacting well with the many and +varied window managers in use on Unix-like systems. In order for applications +and window managers to work well together, a number of standards and +conventions have been developed that regulate behavior outside the scope of the +X11 API; most importantly the +[Inter-Client Communication Conventions Manual](http://www.tronche.com/gui/x/icccm/) +(ICCCM) and +[Extended Window Manager Hints](http://standards.freedesktop.org/wm-spec/wm-spec-latest.html) +(EWMH) standards. + +GLFW uses the `_MOTIF_WM_HINTS` window property to support borderless windows. +If the running window manager does not support this property, the +`GLFW_DECORATED` hint will have no effect. + +GLFW uses the ICCCM `WM_DELETE_WINDOW` protocol to intercept the user +attempting to close the GLFW window. If the running window manager does not +support this protocol, the close callback will never be called. + +GLFW uses the EWMH `_NET_WM_PING` protocol, allowing the window manager notify +the user when the application has stopped responding, i.e. when it has ceased to +process events. If the running window manager does not support this protocol, +the user will not be notified if the application locks up. + +GLFW uses the EWMH `_NET_WM_STATE_FULLSCREEN` window state to tell the window +manager to make the GLFW window full screen. If the running window manager does +not support this state, full screen windows may not work properly. GLFW has +a fallback code path in case this state is unavailable, but every window manager +behaves slightly differently in this regard. + +GLFW uses the EWMH `_NET_WM_BYPASS_COMPOSITOR` window property to tell a +compositing window manager to un-redirect full screen GLFW windows. If the +running window manager uses compositing but does not support this property then +additional copying may be performed for each buffer swap of full screen windows. + +GLFW uses the +[clipboard manager protocol](http://www.freedesktop.org/wiki/ClipboardManager/) +to push a clipboard string (i.e. selection) owned by a GLFW window about to be +destroyed to the clipboard manager. If there is no running clipboard manager, +the clipboard string will be unavailable once the window has been destroyed. + +GLFW uses the +[X drag-and-drop protocol](http://www.freedesktop.org/wiki/Specifications/XDND/) +to provide file drop events. If the application originating the drag does not +support this protocol, drag and drop will not work. + +GLFW uses the XRandR 1.3 extension to provide multi-monitor support. If the +running X server does not support this version of this extension, multi-monitor +support will not function and only a single, desktop-spanning monitor will be +reported. + +GLFW uses the XRandR 1.3 and Xf86vidmode extensions to provide gamma ramp +support. If the running X server does not support either or both of these +extensions, gamma ramp support will not function. + +GLFW uses the Xkb extension and detectable auto-repeat to provide keyboard +input. If the running X server does not support this extension, a non-Xkb +fallback path is used. + + +@section compat_glx GLX extensions + +The GLX API is the default API used to create OpenGL contexts on Unix-like +systems using the X Window System. + +GLFW uses the GLX 1.3 `GLXFBConfig` functions to enumerate and select framebuffer pixel +formats. If GLX 1.3 is not supported, @ref glfwInit will fail. + +GLFW uses the `GLX_MESA_swap_control,` `GLX_EXT_swap_control` and +`GLX_SGI_swap_control` extensions to provide vertical retrace synchronization +(or _vsync_), in that order of preference. Where none of these extension are +available, calling @ref glfwSwapInterval will have no effect. + +GLFW uses the `GLX_ARB_multisample` extension to create contexts with +multisampling anti-aliasing. Where this extension is unavailable, the +`GLFW_SAMPLES` hint will have no effect. + +GLFW uses the `GLX_ARB_create_context` extension when available, even when +creating OpenGL contexts of version 2.1 and below. Where this extension is +unavailable, the `GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` +hints will only be partially supported, the `GLFW_OPENGL_DEBUG_CONTEXT` hint +will have no effect, and setting the `GLFW_OPENGL_PROFILE` or +`GLFW_OPENGL_FORWARD_COMPAT` hints to `GLFW_TRUE` will cause @ref +glfwCreateWindow to fail. + +GLFW uses the `GLX_ARB_create_context_profile` extension to provide support for +context profiles. Where this extension is unavailable, setting the +`GLFW_OPENGL_PROFILE` hint to anything but `GLFW_OPENGL_ANY_PROFILE`, or setting +`GLFW_CLIENT_API` to anything but `GLFW_OPENGL_API` or `GLFW_NO_API` will cause +@ref glfwCreateWindow to fail. + +GLFW uses the `GLX_ARB_context_flush_control` extension to provide control over +whether a context is flushed when it is released (made non-current). Where this +extension is unavailable, the `GLFW_CONTEXT_RELEASE_BEHAVIOR` hint will have no +effect and the context will always be flushed when released. + +GLFW uses the `GLX_ARB_framebuffer_sRGB` and `GLX_EXT_framebuffer_sRGB` +extensions to provide support for sRGB framebuffers. Where both of these +extensions are unavailable, the `GLFW_SRGB_CAPABLE` hint will have no effect. + + +@section compat_wgl WGL extensions + +The WGL API is used to create OpenGL contexts on Microsoft Windows and other +implementations of the Win32 API, such as Wine. + +GLFW uses either the `WGL_EXT_extension_string` or the +`WGL_ARB_extension_string` extension to check for the presence of all other WGL +extensions listed below. If both are available, the EXT one is preferred. If +neither is available, no other extensions are used and many GLFW features +related to context creation will have no effect or cause errors when used. + +GLFW uses the `WGL_EXT_swap_control` extension to provide vertical retrace +synchronization (or _vsync_). Where this extension is unavailable, calling @ref +glfwSwapInterval will have no effect. + +GLFW uses the `WGL_ARB_pixel_format` and `WGL_ARB_multisample` extensions to +create contexts with multisampling anti-aliasing. Where these extensions are +unavailable, the `GLFW_SAMPLES` hint will have no effect. + +GLFW uses the `WGL_ARB_create_context` extension when available, even when +creating OpenGL contexts of version 2.1 and below. Where this extension is +unavailable, the `GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` +hints will only be partially supported, the `GLFW_OPENGL_DEBUG_CONTEXT` hint +will have no effect, and setting the `GLFW_OPENGL_PROFILE` or +`GLFW_OPENGL_FORWARD_COMPAT` hints to `GLFW_TRUE` will cause @ref +glfwCreateWindow to fail. + +GLFW uses the `WGL_ARB_create_context_profile` extension to provide support for +context profiles. Where this extension is unavailable, setting the +`GLFW_OPENGL_PROFILE` hint to anything but `GLFW_OPENGL_ANY_PROFILE` will cause +@ref glfwCreateWindow to fail. + +GLFW uses the `WGL_ARB_context_flush_control` extension to provide control over +whether a context is flushed when it is released (made non-current). Where this +extension is unavailable, the `GLFW_CONTEXT_RELEASE_BEHAVIOR` hint will have no +effect and the context will always be flushed when released. + +GLFW uses the `WGL_ARB_framebuffer_sRGB` and `WGL_EXT_framebuffer_sRGB` +extensions to provide support for sRGB framebuffers. Where both of these +extension are unavailable, the `GLFW_SRGB_CAPABLE` hint will have no effect. + + +@section compat_osx OpenGL 3.2 and later on macOS + +Support for OpenGL 3.2 and above was introduced with OS X 10.7 and even then +only forward-compatible, core profile contexts are supported. Support for +OpenGL 4.1 was introduced with OS X 10.9, also limited to forward-compatible, +core profile contexts. There is also still no mechanism for requesting debug +contexts. Versions of Mac OS X earlier than 10.7 support at most OpenGL +version 2.1. + +Because of this, on OS X 10.7 and later, the `GLFW_CONTEXT_VERSION_MAJOR` and +`GLFW_CONTEXT_VERSION_MINOR` hints will cause @ref glfwCreateWindow to fail if +given version 3.0 or 3.1, the `GLFW_OPENGL_FORWARD_COMPAT` hint must be set to +`GLFW_TRUE` and the `GLFW_OPENGL_PROFILE` hint must be set to +`GLFW_OPENGL_CORE_PROFILE` when creating OpenGL 3.2 and later contexts and the +`GLFW_OPENGL_DEBUG_CONTEXT` hint is ignored. + +Also, on Mac OS X 10.6 and below, the `GLFW_CONTEXT_VERSION_MAJOR` and +`GLFW_CONTEXT_VERSION_MINOR` hints will fail if given a version above 2.1, +setting the `GLFW_OPENGL_PROFILE` or `GLFW_OPENGL_FORWARD_COMPAT` hints to +a non-default value will cause @ref glfwCreateWindow to fail and the +`GLFW_OPENGL_DEBUG_CONTEXT` hint is ignored. + + +@section compat_vulkan Vulkan loader and API + +GLFW uses the standard system-wide Vulkan loader to access the Vulkan API. +This should be installed by graphics drivers and Vulkan SDKs. If this is not +available, @ref glfwVulkanSupported will return `GLFW_FALSE` and all other +Vulkan-related functions will fail with an @ref GLFW_API_UNAVAILABLE error. + + +@section compat_wsi Vulkan WSI extensions + +The Vulkan WSI extensions are used to create Vulkan surfaces for GLFW windows on +all supported platforms. + +GLFW uses the `VK_KHR_surface` and `VK_KHR_win32_surface` extensions to create +surfaces on Microsoft Windows. If any of these extensions are not available, +@ref glfwGetRequiredInstanceExtensions will return an empty list and window +surface creation will fail. + +GLFW uses the `VK_KHR_surface` and either the `VK_KHR_xlib_surface` or +`VK_KHR_xcb_surface` extensions to create surfaces on X11. If `VK_KHR_surface` +or both `VK_KHR_xlib_surface` and `VK_KHR_xcb_surface` are not available, @ref +glfwGetRequiredInstanceExtensions will return an empty list and window surface +creation will fail. + +GLFW uses the `VK_KHR_surface` and `VK_KHR_wayland_surface` extensions to create +surfaces on Wayland. If any of these extensions are not available, @ref +glfwGetRequiredInstanceExtensions will return an empty list and window surface +creation will fail. + +GLFW uses the `VK_KHR_surface` and `VK_KHR_mir_surface` extensions to create +surfaces on Mir. If any of these extensions are not available, @ref +glfwGetRequiredInstanceExtensions will return an empty list and window surface +creation will fail. + +GLFW does not support any extensions for window surface creation on macOS, +meaning@ref glfwGetRequiredInstanceExtensions will return an empty list and +window surface creation will fail. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/compile.dox b/apps/exampleViewer/common/glfw/docs/compile.dox new file mode 100644 index 0000000000..002b26bf76 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/compile.dox @@ -0,0 +1,285 @@ +/*! + +@page compile_guide Compiling GLFW + +@tableofcontents + +This is about compiling the GLFW library itself. For information on how to +build applications that use GLFW, see @ref build_guide. + + +@section compile_cmake Using CMake + +GLFW uses [CMake](http://www.cmake.org/) to generate project files or makefiles +for a particular development environment. If you are on a Unix-like system such +as Linux or FreeBSD or have a package system like Fink, MacPorts, Cygwin or +Homebrew, you can simply install its CMake package. If not, you can download +installers for Windows and macOS from the +[CMake website](http://www.cmake.org/). + +@note CMake only generates project files or makefiles. It does not compile the +actual GLFW library. To compile GLFW, first generate these files for your +chosen development environment and then use them to compile the actual GLFW +library. + + +@subsection compile_deps Dependencies + +Once you have installed CMake, make sure that all other dependencies are +available. On some platforms, GLFW needs a few additional packages to be +installed. See the section for your chosen platform and development environment +below. + + +@subsubsection compile_deps_msvc Dependencies for Visual C++ on Windows + +The Microsoft Platform SDK that is installed along with Visual C++ already +contains all the necessary headers, link libraries and tools except for CMake. +Move on to @ref compile_generate. + + +@subsubsection compile_deps_mingw Dependencies for MinGW or MinGW-w64 on Windows + +Both the MinGW and the MinGW-w64 packages already contain all the necessary +headers, link libraries and tools except for CMake. Move on to @ref +compile_generate. + + +@subsubsection compile_deps_mingw_cross Dependencies for MinGW or MinGW-w64 cross-compilation + +Both Cygwin and many Linux distributions have MinGW or MinGW-w64 packages. For +example, Cygwin has the `mingw64-i686-gcc` and `mingw64-x86_64-gcc` packages +for 32- and 64-bit version of MinGW-w64, while Debian GNU/Linux and derivatives +like Ubuntu have the `mingw-w64` package for both. + +GLFW has CMake toolchain files in the `CMake/` directory that allow for easy +cross-compilation of Windows binaries. To use these files you need to add a +special parameter when generating the project files or makefiles: + +@code{.sh} +cmake -DCMAKE_TOOLCHAIN_FILE= . +@endcode + +The exact toolchain file to use depends on the prefix used by the MinGW or +MinGW-w64 binaries on your system. You can usually see this in the /usr +directory. For example, both the Debian/Ubuntu and Cygwin MinGW-w64 packages +have `/usr/x86_64-w64-mingw32` for the 64-bit compilers, so the correct +invocation would be: + +@code{.sh} +cmake -DCMAKE_TOOLCHAIN_FILE=CMake/x86_64-w64-mingw32.cmake . +@endcode + +For more details see the article +[CMake Cross Compiling](http://www.paraview.org/Wiki/CMake_Cross_Compiling) on +the CMake wiki. + +Once you have this set up, move on to @ref compile_generate. + + +@subsubsection compile_deps_xcode Dependencies for Xcode on macOS + +Xcode comes with all necessary tools except for CMake. The required headers +and libraries are included in the core macOS frameworks. Xcode can be +downloaded from the Mac App Store or from the ADC Member Center. + +Once you have Xcode installed, move on to @ref compile_generate. + + +@subsubsection compile_deps_x11 Dependencies for Linux and X11 + +To compile GLFW for X11, you need to have the X11 packages installed, as well as +the basic development tools like GCC and make. For example, on Ubuntu and other +distributions based on Debian GNU/Linux, you need to install the `xorg-dev` +package, which pulls in all X.org header packages. + +Once you have installed the necessary packages, move on to @ref +compile_generate. + + +@subsection compile_generate Generating build files with CMake + +Once you have all necessary dependencies it is time to generate the project +files or makefiles for your development environment. CMake needs to know two +paths for this: the path to the _root_ directory of the GLFW source tree (i.e. +_not_ the `src` subdirectory) and the target path for the generated files and +compiled binaries. If these are the same, it is called an in-tree build, +otherwise it is called an out-of-tree build. + +One of several advantages of out-of-tree builds is that you can generate files +and compile for different development environments using a single source tree. + +@note This section is about generating the project files or makefiles necessary +to compile the GLFW library, not about compiling the actual library. + + +@subsubsection compile_generate_cli Generating files with the CMake command-line tool + +To make an in-tree build, enter the _root_ directory of the GLFW source tree +(i.e. _not_ the `src` subdirectory) and run CMake. The current directory is +used as target path, while the path provided as an argument is used to find the +source tree. + +@code{.sh} +cd +cmake . +@endcode + +To make an out-of-tree build, make a directory outside of the source tree, enter +it and run CMake with the (relative or absolute) path to the root of the source +tree as an argument. + +@code{.sh} +mkdir glfw-build +cd glfw-build +cmake +@endcode + +Once you have generated the project files or makefiles for your chosen +development environment, move on to @ref compile_compile. + + +@subsubsection compile_generate_gui Generating files with the CMake GUI + +If you are using the GUI version, choose the root of the GLFW source tree as +source location and the same directory or another, empty directory as the +destination for binaries. Choose _Configure_, change any options you wish to, +_Configure_ again to let the changes take effect and then _Generate_. + +Once you have generated the project files or makefiles for your chosen +development environment, move on to @ref compile_compile. + + +@subsection compile_compile Compiling the library + +You should now have all required dependencies and the project files or makefiles +necessary to compile GLFW. Go ahead and compile the actual GLFW library with +these files, as you would with any other project. + +Once the GLFW library is compiled, you are ready to build your applications, +linking it to the GLFW library. See @ref build_guide for more information. + + +@subsection compile_options CMake options + +The CMake files for GLFW provide a number of options, although not all are +available on all supported platforms. Some of these are de facto standards +among projects using CMake and so have no `GLFW_` prefix. + +If you are using the GUI version of CMake, these are listed and can be changed +from there. If you are using the command-line version of CMake you can use the +`ccmake` ncurses GUI to set options. Some package systems like Ubuntu and other +distributions based on Debian GNU/Linux have this tool in a separate +`cmake-curses-gui` package. + +Finally, if you don't want to use any GUI, you can set options from the `cmake` +command-line with the `-D` flag. + +@code{.sh} +cmake -DBUILD_SHARED_LIBS=ON . +@endcode + + +@subsubsection compile_options_shared Shared CMake options + +`BUILD_SHARED_LIBS` determines whether GLFW is built as a static +library or as a DLL / shared library / dynamic library. + +`LIB_SUFFIX` affects where the GLFW shared /dynamic library is installed. If it +is empty, it is installed to `${CMAKE_INSTALL_PREFIX}/lib`. If it is set to +`64`, it is installed to `${CMAKE_INSTALL_PREFIX}/lib64`. + +`GLFW_BUILD_EXAMPLES` determines whether the GLFW examples are built +along with the library. + +`GLFW_BUILD_TESTS` determines whether the GLFW test programs are +built along with the library. + +`GLFW_BUILD_DOCS` determines whether the GLFW documentation is built along with +the library. + +`GLFW_VULKAN_STATIC` determines whether to use the Vulkan loader linked +statically into the application. + + +@subsubsection compile_options_osx macOS specific CMake options + +`GLFW_USE_CHDIR` determines whether `glfwInit` changes the current +directory of bundled applications to the `Contents/Resources` directory. + +`GLFW_USE_MENUBAR` determines whether the first call to +`glfwCreateWindow` sets up a minimal menu bar. + +`GLFW_USE_RETINA` determines whether windows will use the full resolution of +Retina displays. + + +@subsubsection compile_options_win32 Windows specific CMake options + +`USE_MSVC_RUNTIME_LIBRARY_DLL` determines whether to use the DLL version or the +static library version of the Visual C++ runtime library. If set to `ON`, the +DLL version of the Visual C++ library is used. + +`GLFW_USE_HYBRID_HPG` determines whether to export the `NvOptimusEnablement` and +`AmdPowerXpressRequestHighPerformance` symbols, which force the use of the +high-performance GPU on Nvidia Optimus and AMD PowerXpress systems. These symbols +need to be exported by the EXE to be detected by the driver, so the override +will not work if GLFW is built as a DLL. + + +@section compile_manual Compiling GLFW manually + +If you wish to compile GLFW without its CMake build environment then you will +have to do at least some of the platform detection yourself. GLFW needs +a configuration macro to be defined in order to know what window system it's +being compiled for and also has optional, platform-specific ones for various +features. + +When building with CMake, the `glfw_config.h` configuration header is generated +based on the current platform and CMake options. The GLFW CMake environment +defines `_GLFW_USE_CONFIG_H`, which causes this header to be included by +`internal.h`. Without this macro, GLFW will expect the necessary configuration +macros to be defined on the command-line. + +The window creation API is used to create windows, handle input, monitors, gamma +ramps and clipboard. The options are: + + - `_GLFW_COCOA` to use the Cocoa frameworks + - `_GLFW_WIN32` to use the Win32 API + - `_GLFW_X11` to use the X Window System + - `_GLFW_WAYLAND` to use the Wayland API (experimental and incomplete) + - `_GLFW_MIR` to use the Mir API (experimental and incomplete) + +If you are building GLFW as a shared library / dynamic library / DLL then you +must also define `_GLFW_BUILD_DLL`. Otherwise, you must not define it. + +If you are linking the Vulkan loader statically into your application then you +must also define `_GLFW_VULKAN_STATIC`. Otherwise, GLFW will attempt to use the +external version. + +For the EGL context creation API, the following options are available: + + - `_GLFW_USE_EGLPLATFORM_H` to use `EGL/eglplatform.h` for native handle + definitions (fallback) + +If you are using the X11 window creation API, support for the following X11 +extensions can be enabled: + + - `_GLFW_HAS_XF86VM` to use Xxf86vm as a fallback when RandR gamma is broken + (recommended) + +If you are using the Cocoa window creation API, the following options are +available: + + - `_GLFW_USE_CHDIR` to `chdir` to the `Resources` subdirectory of the + application bundle during @ref glfwInit (recommended) + - `_GLFW_USE_MENUBAR` to create and populate the menu bar when the first window + is created (recommended) + - `_GLFW_USE_RETINA` to have windows use the full resolution of Retina displays + (recommended) + +@note None of the @ref build_macros may be defined during the compilation of +GLFW. If you define any of these in your build files, make sure they are not +applied to the GLFW sources. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/context.dox b/apps/exampleViewer/common/glfw/docs/context.dox new file mode 100644 index 0000000000..3f972828ca --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/context.dox @@ -0,0 +1,345 @@ +/*! + +@page context_guide Context guide + +@tableofcontents + +This guide introduces the OpenGL and OpenGL ES context related functions of +GLFW. For details on a specific function in this category, see the @ref +context. There are also guides for the other areas of the GLFW API. + + - @ref intro_guide + - @ref window_guide + - @ref vulkan_guide + - @ref monitor_guide + - @ref input_guide + + +@section context_object Context objects + +A window object encapsulates both a top-level window and an OpenGL or OpenGL ES +context. It is created with @ref glfwCreateWindow and destroyed with @ref +glfwDestroyWindow or @ref glfwTerminate. See @ref window_creation for more +information. + +As the window and context are inseparably linked, the window object also serves +as the context handle. + +To test the creation of various kinds of contexts and see their properties, run +the `glfwinfo` test program. + +@note Vulkan does not have a context and the Vulkan instance is created via the +Vulkan API itself. If you will be using Vulkan to render to a window, disable +context creation by setting the [GLFW_CLIENT_API](@ref window_hints_ctx) hint to +`GLFW_NO_API`. For more information, see the @ref vulkan_guide. + + +@subsection context_hints Context creation hints + +There are a number of hints, specified using @ref glfwWindowHint, related to +what kind of context is created. See +[context related hints](@ref window_hints_ctx) in the window guide. + + +@subsection context_sharing Context object sharing + +When creating a window and its OpenGL or OpenGL ES context with @ref +glfwCreateWindow, you can specify another window whose context the new one +should share its objects (textures, vertex and element buffers, etc.) with. + +@code +GLFWwindow* second_window = glfwCreateWindow(640, 480, "Second Window", NULL, first_window); +@endcode + +Object sharing is implemented by the operating system and graphics driver. On +platforms where it is possible to choose which types of objects are shared, GLFW +requests that all types are shared. + +See the relevant chapter of the [OpenGL](https://www.opengl.org/registry/) or +[OpenGL ES](http://www.khronos.org/opengles/) reference documents for more +information. The name and number of this chapter unfortunately varies between +versions and APIs, but has at times been named _Shared Objects and Multiple +Contexts_. + +GLFW comes with a simple object sharing test program called `sharing`. + + +@subsection context_offscreen Offscreen contexts + +GLFW doesn't support creating contexts without an associated window. However, +contexts with hidden windows can be created with the +[GLFW_VISIBLE](@ref window_hints_wnd) window hint. + +@code +glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + +GLFWwindow* offscreen_context = glfwCreateWindow(640, 480, "", NULL, NULL); +@endcode + +The window never needs to be shown and its context can be used as a plain +offscreen context. Depending on the window manager, the size of a hidden +window's framebuffer may not be usable or modifiable, so framebuffer +objects are recommended for rendering with such contexts. + +You should still [process events](@ref events) as long as you have at least one +window, even if none of them are visible. + +@macos The first time a window is created the menu bar is populated with +common commands like Hide, Quit and About. This is not desirable for example +when writing a command-line only application. The menu bar setup can be +disabled with a [compile-time option](@ref compile_options_osx). + + +@subsection context_less Windows without contexts + +You can disable context creation by setting the +[GLFW_CLIENT_API](@ref window_hints_ctx) hint to `GLFW_NO_API`. Windows without +contexts must not be passed to @ref glfwMakeContextCurrent or @ref +glfwSwapBuffers. + + +@section context_current Current context + +Before you can make OpenGL or OpenGL ES calls, you need to have a current +context of the correct type. A context can only be current for a single thread +at a time, and a thread can only have a single context current at a time. + +The context of a window is made current with @ref glfwMakeContextCurrent. + +@code +glfwMakeContextCurrent(window); +@endcode + +The window of the current context is returned by @ref glfwGetCurrentContext. + +@code +GLFWwindow* window = glfwGetCurrentContext(); +@endcode + +The following GLFW functions require a context to be current. Calling any these +functions without a current context will generate a @ref GLFW_NO_CURRENT_CONTEXT +error. + + - @ref glfwSwapInterval + - @ref glfwExtensionSupported + - @ref glfwGetProcAddress + + +@section context_swap Buffer swapping + +Buffer swapping is part of the window and framebuffer, not the context. See +@ref buffer_swap. + + +@section context_glext OpenGL and OpenGL ES extensions + +One of the benefits of OpenGL and OpenGL ES is their extensibility. +Hardware vendors may include extensions in their implementations that extend the +API before that functionality is included in a new version of the OpenGL or +OpenGL ES specification, and some extensions are never included and remain +as extensions until they become obsolete. + +An extension is defined by: + +- An extension name (e.g. `GL_ARB_debug_output`) +- New OpenGL tokens (e.g. `GL_DEBUG_SEVERITY_HIGH_ARB`) +- New OpenGL functions (e.g. `glGetDebugMessageLogARB`) + +Note the `ARB` affix, which stands for Architecture Review Board and is used +for official extensions. The extension above was created by the ARB, but there +are many different affixes, like `NV` for Nvidia and `AMD` for, well, AMD. Any +group may also use the generic `EXT` affix. Lists of extensions, together with +their specifications, can be found at the +[OpenGL Registry](http://www.opengl.org/registry/) and +[OpenGL ES Registry](https://www.khronos.org/registry/gles/). + + +@subsection context_glext_auto Loading extension with a loader library + +An extension loader library is the easiest and best way to access both OpenGL and +OpenGL ES extensions and modern versions of the core OpenGL or OpenGL ES APIs. +They will take care of all the details of declaring and loading everything you +need. One such library is [glad](https://github.com/Dav1dde/glad) and there are +several others. + +The following example will use glad but all extension loader libraries work +similarly. + +First you need to generate the source files using the glad Python script. This +example generates a loader for any version of OpenGL, which is the default for +both GLFW and glad, but loaders for OpenGL ES, as well as loaders for specific +API versions and extension sets can be generated. The generated files are +written to the `output` directory. + +@code{.sh} +python main.py --generator c --no-loader --out-path output +@endcode + +The `--no-loader` option is added because GLFW already provides a function for +loading OpenGL and OpenGL ES function pointers, one that automatically uses the +selected context creation API, and glad can call this instead of having to +implement its own. There are several other command-line options as well. See +the glad documentation for details. + +Add the generated `output/src/glad.c`, `output/include/glad/glad.h` and +`output/include/KHR/khrplatform.h` files to your build. Then you need to +include the glad header file, which will replace the OpenGL header of your +development environment. By including the glad header before the GLFW header, +it suppresses the development environment's OpenGL or OpenGL ES header. + +@code +#include +#include +@endcode + +Finally you need to initialize glad once you have a suitable current context. + +@code +window = glfwCreateWindow(640, 480, "My Window", NULL, NULL); +if (!window) +{ + ... +} + +glfwMakeContextCurrent(window); + +gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); +@endcode + +Once glad has been loaded, you have access to all OpenGL core and extension +functions supported by both the context you created and the glad loader you +generated and you are ready to start rendering. + +You can specify a minimum required OpenGL or OpenGL ES version with +[context hints](@ref window_hints_ctx). If your needs are more complex, you can +check the actual OpenGL or OpenGL ES version with +[context attributes](@ref window_attribs_ctx), or you can check whether +a specific version is supported by the current context with the +`GLAD_GL_VERSION_x_x` booleans. + +@code +if (GLAD_GL_VERSION_3_2) +{ + // Call OpenGL 3.2+ specific code +} +@endcode + +To check whether a specific extension is supported, use the `GLAD_GL_xxx` +booleans. + +@code +if (GLAD_GL_ARB_debug_output) +{ + // Use GL_ARB_debug_output +} +@endcode + + +@subsection context_glext_manual Loading extensions manually + +__Do not use this technique__ unless it is absolutely necessary. An +[extension loader library](@ref context_glext_auto) will save you a ton of +tedious, repetitive, error prone work. + +To use a certain extension, you must first check whether the context supports +that extension and then, if it introduces new functions, retrieve the pointers +to those functions. GLFW provides @ref glfwExtensionSupported and @ref +glfwGetProcAddress for manual loading of extensions and new API functions. + +This section will demonstrate manual loading of OpenGL extensions. The loading +of OpenGL ES extensions is identical except for the name of the extension header. + + +@subsubsection context_glext_header The glext.h header + +The `glext.h` extension header is a continually updated file that defines the +interfaces for all OpenGL extensions. The latest version of this can always be +found at the [OpenGL Registry](http://www.opengl.org/registry/). There are also +extension headers for the various versions of OpenGL ES at the +[OpenGL ES Registry](https://www.khronos.org/registry/gles/). It it strongly +recommended that you use your own copy of the extension header, as the one +included in your development environment may be several years out of date and +may not include the extensions you wish to use. + +The header defines function pointer types for all functions of all extensions it +supports. These have names like `PFNGLGETDEBUGMESSAGELOGARBPROC` (for +`glGetDebugMessageLogARB`), i.e. the name is made uppercase and `PFN` (pointer +to function) and `PROC` (procedure) are added to the ends. + +To include the extension header, define [GLFW_INCLUDE_GLEXT](@ref build_macros) +before including the GLFW header. + +@code +#define GLFW_INCLUDE_GLEXT +#include +@endcode + + +@subsubsection context_glext_string Checking for extensions + +A given machine may not actually support the extension (it may have older +drivers or a graphics card that lacks the necessary hardware features), so it +is necessary to check at run-time whether the context supports the extension. +This is done with @ref glfwExtensionSupported. + +@code +if (glfwExtensionSupported("GL_ARB_debug_output")) +{ + // The extension is supported by the current context +} +@endcode + +The argument is a null terminated ASCII string with the extension name. If the +extension is supported, @ref glfwExtensionSupported returns `GLFW_TRUE`, +otherwise it returns `GLFW_FALSE`. + + +@subsubsection context_glext_proc Fetching function pointers + +Many extensions, though not all, require the use of new OpenGL functions. +These functions often do not have entry points in the client API libraries of +your operating system, making it necessary to fetch them at run time. You can +retrieve pointers to these functions with @ref glfwGetProcAddress. + +@code +PFNGLGETDEBUGMESSAGELOGARBPROC pfnGetDebugMessageLog = glfwGetProcAddress("glGetDebugMessageLogARB"); +@endcode + +In general, you should avoid giving the function pointer variables the (exact) +same name as the function, as this may confuse your linker. Instead, you can +use a different prefix, like above, or some other naming scheme. + +Now that all the pieces have been introduced, here is what they might look like +when used together. + +@code +#define GLFW_INCLUDE_GLEXT +#include + +#define glGetDebugMessageLogARB pfnGetDebugMessageLog +PFNGLGETDEBUGMESSAGELOGARBPROC pfnGetDebugMessageLog; + +// Flag indicating whether the extension is supported +int has_ARB_debug_output = 0; + +void load_extensions(void) +{ + if (glfwExtensionSupported("GL_ARB_debug_output")) + { + pfnGetDebugMessageLog = (PFNGLGETDEBUGMESSAGELOGARBPROC) + glfwGetProcAddress("glGetDebugMessageLogARB"); + has_ARB_debug_output = 1; + } +} + +void some_function(void) +{ + if (has_ARB_debug_output) + { + // Now the extension function can be called as usual + glGetDebugMessageLogARB(...); + } +} +@endcode + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/extra.css b/apps/exampleViewer/common/glfw/docs/extra.css new file mode 100644 index 0000000000..e9896fae14 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/extra.css @@ -0,0 +1 @@ +#navrow1,#navrow2,#navrow3,#navrow4,.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code{background:none}#titlearea,.footer,.contents,div.header,.memdoc,table.doxtable td,table.doxtable th,hr,.memSeparator{border:none}.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.reflist dt a.el,.levels span,.directory .levels span{text-shadow:none}.memdoc,dl.reflist dd{box-shadow:none}div.headertitle,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,table.doxtable code{padding:0}#nav-path,.directory .levels,span.lineno{display:none}html,#titlearea,.footer,tr.even,.directory tr.even,.doxtable tr:nth-child(even),.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,code{background:#f2f2f2}body{color:#4d4d4d}h1,h2,h2.groupheader,h3,div.toc h3,h4,h5,h6,strong,em{color:#1a1a1a;border-bottom:none}h1{padding-top:0.5em;font-size:180%}h2{padding-top:0.5em;margin-bottom:0;font-size:140%}h3{padding-top:0.5em;margin-bottom:0;font-size:110%}.glfwheader{font-size:16px;height:64px;max-width:920px;min-width:800px;padding:0 32px;margin:0 auto}#glfwhome{line-height:64px;padding-right:48px;color:#666;font-size:2.5em;background:url("http://www.glfw.org/css/arrow.png") no-repeat right}.glfwnavbar{list-style-type:none;margin:0 auto;float:right}#glfwhome,.glfwnavbar li{float:left}.glfwnavbar a,.glfwnavbar a:visited{line-height:64px;margin-left:2em;display:block;color:#666}#glfwhome,.glfwnavbar a,.glfwnavbar a:visited{transition:.35s ease}#titlearea,.footer{color:#666}address.footer{text-align:center;padding:2em;margin-top:3em}#top{background:#666}#navrow1,#navrow2,#navrow3,#navrow4{max-width:920px;min-width:800px;margin:0 auto;font-size:13px}.tablist{height:36px;display:block;position:relative}.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a{color:#f2f2f2}.tablist li.current a{background:linear-gradient(to bottom, #ffa733 0, #f60 100%);box-shadow:inset 0 0 32px #f60;text-shadow:0 -1px 1px #b34700;color:#fff}.contents{min-height:590px}div.contents,div.header{max-width:920px;margin:0 auto;padding:0 32px;background:#fff none}table.doxtable th,dl.reflist dt{background:linear-gradient(to bottom, #ffa733 0, #f60 100%);box-shadow:inset 0 0 32px #f60;text-shadow:0 -1px 1px #b34700;color:#fff}dl.reflist dt a.el{color:#f60;padding:.2em;border-radius:4px;background-color:#ffe0cc}div.toc{float:none;width:auto}div.toc h3{font-size:1.17em}div.toc ul{padding-left:1.5em}div.toc li{font-size:1em;padding-left:0;list-style-type:disc}div.toc,.memproto,div.qindex,div.ah{background:linear-gradient(to bottom, #f2f2f2 0, #e6e6e6 100%);box-shadow:inset 0 0 32px #e6e6e6;text-shadow:0 1px 1px #fff;color:#1a1a1a;border:2px solid #e6e6e6;border-radius:4px}.paramname{color:#803300}dl.reflist dt{border:2px solid #f60;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom:none}dl.reflist dd{border:2px solid #f60;border-bottom-right-radius:4px;border-bottom-left-radius:4px;border-top:none}table.doxtable{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,.tablist a:hover,span.lineno a:hover{color:#f60;text-decoration:none}div.directory{border-collapse:inherit;border-spacing:0;border:2px solid #f60;border-radius:4px}hr,.memSeparator{height:2px;background:linear-gradient(to right, #f2f2f2 0, #d9d9d9 50%, #f2f2f2 100%)}dl.note,dl.pre,dl.post,dl.invariant{background:linear-gradient(to bottom, #ddfad1 0, #cbf7ba 100%);box-shadow:inset 0 0 32px #baf5a3;color:#1e5309;border:2px solid #afe599}dl.warning,dl.attention{background:linear-gradient(to bottom, #fae8d1 0, #f7ddba 100%);box-shadow:inset 0 0 32px #f5d1a3;color:#533309;border:2px solid #e5c499}dl.deprecated,dl.bug{background:linear-gradient(to bottom, #fad1e3 0, #f7bad6 100%);box-shadow:inset 0 0 32px #f5a3c8;color:#53092a;border:2px solid #e599bb}dl.todo,dl.test{background:linear-gradient(to bottom, #d1ecfa 0, #bae3f7 100%);box-shadow:inset 0 0 32px #a3daf5;color:#093a53;border:2px solid #99cce5}dl.note,dl.pre,dl.post,dl.invariant,dl.warning,dl.attention,dl.deprecated,dl.bug,dl.todo,dl.test{border-radius:4px;padding:1em;text-shadow:0 1px 1px #fff;margin:1em 0}.note a,.pre a,.post a,.invariant a,.warning a,.attention a,.deprecated a,.bug a,.todo a,.test a,.note a:visited,.pre a:visited,.post a:visited,.invariant a:visited,.warning a:visited,.attention a:visited,.deprecated a:visited,.bug a:visited,.todo a:visited,.test a:visited{color:inherit}div.line{line-height:inherit}div.fragment,pre.fragment{background:#f2f2f2;border-radius:4px;border:none;padding:1em;overflow:auto;border-left:4px solid #ccc;margin:1em 0}.lineno a,.lineno a:visited,.line,pre.fragment{color:#4d4d4d}span.preprocessor,span.comment{color:#007899}a.code,a.code:visited{color:#e64500}span.keyword,span.keywordtype,span.keywordflow{color:#404040;font-weight:bold}span.stringliteral{color:#360099}code{padding:.1em;border-radius:4px} diff --git a/apps/exampleViewer/common/glfw/docs/extra.less b/apps/exampleViewer/common/glfw/docs/extra.less new file mode 100644 index 0000000000..9d0ce9759f --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/extra.less @@ -0,0 +1,370 @@ +// NOTE: Please use this file to perform modifications on default style sheets. +// +// You need to install a few Ruby gems to generate extra.css from this file: +// gem install less therubyracer +// +// Run this command to regenerate extra.css after you're finished with changes: +// lessc --compress extra.less > extra.css +// +// Alternatively you can use online services to regenerate extra.css. + + +// Default text color for page contents +@default-text-color: hsl(0,0%,30%); + +// Page header, footer, table rows, inline codes and definition lists +@header-footer-background-color: hsl(0,0%,95%); + +// Page header, footer links and navigation bar background +@header-footer-link-color: hsl(0,0%,40%); + +// Doxygen navigation bar links +@navbar-link-color: @header-footer-background-color; + +// Page content background color +@content-background-color: hsl(0,0%,100%); + +// Bold, italic, h1, h2, ... and table of contents +@heading-color: hsl(0,0%,10%); + +// Function, enum and macro definition separator +@def-separator-color: @header-footer-background-color; + +// Base color hue +@base-hue: 24; + +// Default color used for links +@default-link-color: hsl(@base-hue,100%,50%); + +// Doxygen navigation bar active tab +@tab-text-color: hsl(0,0%,100%); +@tab-background-color1: @default-link-color; +@tab-background-color2: lighten(spin(@tab-background-color1, 10), 10%); + +// Table borders +@default-border-color: @default-link-color; + +// Table header +@table-text-color: @tab-text-color; +@table-background-color1: @tab-background-color1; +@table-background-color2: @tab-background-color2; + +// Table of contents, data structure index and prototypes +@toc-background-color1: hsl(0,0%,90%); +@toc-background-color2: lighten(@toc-background-color1, 5%); + +// Function prototype parameters color +@prototype-param-color: darken(@default-link-color, 25%); + +// Message box color: note, pre, post and invariant +@box-note-color: hsl(103,80%,85%); + +// Message box color: warning and attention +@box-warning-color: hsl(34,80%,85%); + +// Message box color: deprecated and bug +@box-bug-color: hsl(333,80%,85%); + +// Message box color: todo and test +@box-todo-color: hsl(200,80%,85%); + +// Message box helper function +.message-box(@base-color) { + background:linear-gradient(to bottom,lighten(@base-color, 5%) 0%,@base-color 100%); + box-shadow:inset 0 0 32px darken(@base-color, 5%); + color:darken(@base-color, 67%); + border:2px solid desaturate(darken(@base-color, 10%), 20%); +} + + +#navrow1,#navrow2,#navrow3,#navrow4,.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.memdoc,dl.reflist dd,div.toc li,.ah,span.lineno,span.lineno a,span.lineno a:hover,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,.doxtable code { + background:none; +} + +#titlearea,.footer,.contents,div.header,.memdoc,table.doxtable td,table.doxtable th,hr,.memSeparator { + border:none; +} + +.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a,.reflist dt a.el,.levels span,.directory .levels span { + text-shadow:none; +} + +.memdoc,dl.reflist dd { + box-shadow:none; +} + +div.headertitle,.note code,.pre code,.post code,.invariant code,.warning code,.attention code,.deprecated code,.bug code,.todo code,.test code,table.doxtable code { + padding:0; +} + +#nav-path,.directory .levels,span.lineno { + display:none; +} + +html,#titlearea,.footer,tr.even,.directory tr.even,.doxtable tr:nth-child(even),.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,code { + background:@header-footer-background-color; +} + +body { + color:@default-text-color; +} + +h1,h2,h2.groupheader,h3,div.toc h3,h4,h5,h6,strong,em { + color:@heading-color; + border-bottom:none; +} + +h1 { + padding-top:0.5em; + font-size:180%; +} + +h2 { + padding-top:0.5em; + margin-bottom:0; + font-size:140%; +} + +h3 { + padding-top:0.5em; + margin-bottom:0; + font-size:110%; +} + +.glfwheader { + font-size:16px; + height:64px; + max-width:920px; + min-width:800px; + padding:0 32px; + margin:0 auto; +} + +#glfwhome { + line-height:64px; + padding-right:48px; + color:@header-footer-link-color; + font-size:2.5em; + background:url("http://www.glfw.org/css/arrow.png") no-repeat right; +} + +.glfwnavbar { + list-style-type:none; + margin:0 auto; + float:right; +} + +#glfwhome,.glfwnavbar li { + float:left; +} + +.glfwnavbar a,.glfwnavbar a:visited { + line-height:64px; + margin-left:2em; + display:block; + color:@header-footer-link-color; +} + +#glfwhome,.glfwnavbar a,.glfwnavbar a:visited { + transition:.35s ease; +} + +#titlearea,.footer { + color:@header-footer-link-color; +} + +address.footer { + text-align:center; + padding:2em; + margin-top:3em; +} + +#top { + background:@header-footer-link-color; +} + +#navrow1,#navrow2,#navrow3,#navrow4 { + max-width:920px; + min-width:800px; + margin:0 auto; + font-size:13px; +} + +.tablist { + height:36px; + display:block; + position:relative; +} + +.tablist a,.tablist a:visited,.tablist a:hover,.tablist li,.tablist li.current a { + color:@navbar-link-color; +} + +.tablist li.current a { + background:linear-gradient(to bottom,@tab-background-color2 0%,@tab-background-color1 100%); + box-shadow:inset 0 0 32px @tab-background-color1; + text-shadow:0 -1px 1px darken(@tab-background-color1, 15%); + color:@tab-text-color; +} + +.contents { + min-height:590px; +} + +div.contents,div.header { + max-width:920px; + margin:0 auto; + padding:0 32px; + background:@content-background-color none; +} + +table.doxtable th,dl.reflist dt { + background:linear-gradient(to bottom,@table-background-color2 0%,@table-background-color1 100%); + box-shadow:inset 0 0 32px @table-background-color1; + text-shadow:0 -1px 1px darken(@table-background-color1, 15%); + color:@table-text-color; +} + +dl.reflist dt a.el { + color:@default-link-color; + padding:.2em; + border-radius:4px; + background-color:lighten(@default-link-color, 40%); +} + +div.toc { + float:none; + width:auto; +} + +div.toc h3 { + font-size:1.17em; +} + +div.toc ul { + padding-left:1.5em; +} + +div.toc li { + font-size:1em; + padding-left:0; + list-style-type:disc; +} + +div.toc,.memproto,div.qindex,div.ah { + background:linear-gradient(to bottom,@toc-background-color2 0%,@toc-background-color1 100%); + box-shadow:inset 0 0 32px @toc-background-color1; + text-shadow:0 1px 1px lighten(@toc-background-color2, 10%); + color:@heading-color; + border:2px solid @toc-background-color1; + border-radius:4px; +} + +.paramname { + color:@prototype-param-color; +} + +dl.reflist dt { + border:2px solid @default-border-color; + border-top-left-radius:4px; + border-top-right-radius:4px; + border-bottom:none; +} + +dl.reflist dd { + border:2px solid @default-border-color; + border-bottom-right-radius:4px; + border-bottom-left-radius:4px; + border-top:none; +} + +table.doxtable { + border-collapse:inherit; + border-spacing:0; + border:2px solid @default-border-color; + border-radius:4px; +} + +a,a:hover,a:visited,a:visited:hover,.contents a:visited,.el,a.el:visited,#glfwhome:hover,.tablist a:hover,span.lineno a:hover { + color:@default-link-color; + text-decoration:none; +} + +div.directory { + border-collapse:inherit; + border-spacing:0; + border:2px solid @default-border-color; + border-radius:4px; +} + +hr,.memSeparator { + height:2px; + background:linear-gradient(to right,@def-separator-color 0%,darken(@def-separator-color, 10%) 50%,@def-separator-color 100%); +} + +dl.note,dl.pre,dl.post,dl.invariant { + .message-box(@box-note-color); +} + +dl.warning,dl.attention { + .message-box(@box-warning-color); +} + +dl.deprecated,dl.bug { + .message-box(@box-bug-color); +} + +dl.todo,dl.test { + .message-box(@box-todo-color); +} + +dl.note,dl.pre,dl.post,dl.invariant,dl.warning,dl.attention,dl.deprecated,dl.bug,dl.todo,dl.test { + border-radius:4px; + padding:1em; + text-shadow:0 1px 1px hsl(0,0%,100%); + margin:1em 0; +} + +.note a,.pre a,.post a,.invariant a,.warning a,.attention a,.deprecated a,.bug a,.todo a,.test a,.note a:visited,.pre a:visited,.post a:visited,.invariant a:visited,.warning a:visited,.attention a:visited,.deprecated a:visited,.bug a:visited,.todo a:visited,.test a:visited { + color:inherit; +} + +div.line { + line-height:inherit; +} + +div.fragment,pre.fragment { + background:hsl(0,0%,95%); + border-radius:4px; + border:none; + padding:1em; + overflow:auto; + border-left:4px solid hsl(0,0%,80%); + margin:1em 0; +} + +.lineno a,.lineno a:visited,.line,pre.fragment { + color:@default-text-color; +} + +span.preprocessor,span.comment { + color:hsl(193,100%,30%); +} + +a.code,a.code:visited { + color:hsl(18,100%,45%); +} + +span.keyword,span.keywordtype,span.keywordflow { + color:darken(@default-text-color, 5%); + font-weight:bold; +} + +span.stringliteral { + color:hsl(261,100%,30%); +} + +code { + padding:.1em; + border-radius:4px; +} diff --git a/apps/exampleViewer/common/glfw/docs/footer.html b/apps/exampleViewer/common/glfw/docs/footer.html new file mode 100644 index 0000000000..b0434ca183 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/footer.html @@ -0,0 +1,7 @@ + + + diff --git a/apps/exampleViewer/common/glfw/docs/header.html b/apps/exampleViewer/common/glfw/docs/header.html new file mode 100644 index 0000000000..9759d8b026 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/header.html @@ -0,0 +1,34 @@ + + + + + + +$projectname: $title +$title + + + +$treeview +$search +$mathjax + +$extrastylesheet + + +
+ + + + + diff --git a/apps/exampleViewer/common/glfw/docs/input.dox b/apps/exampleViewer/common/glfw/docs/input.dox new file mode 100644 index 0000000000..497fe67698 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/input.dox @@ -0,0 +1,676 @@ +/*! + +@page input_guide Input guide + +@tableofcontents + +This guide introduces the input related functions of GLFW. For details on +a specific function in this category, see the @ref input. There are also guides +for the other areas of GLFW. + + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref vulkan_guide + - @ref monitor_guide + +GLFW provides many kinds of input. While some can only be polled, like time, or +only received via callbacks, like scrolling, there are those that provide both +callbacks and polling. Where a callback is provided, that is the recommended +way to receive that kind of input. The more you can use callbacks the less time +your users' machines will need to spend polling. + +All input callbacks receive a window handle. By using the +[window user pointer](@ref window_userptr), you can access non-global structures +or objects from your callbacks. + +To get a better feel for how the various events callbacks behave, run the +`events` test program. It register every callback supported by GLFW and prints +out all arguments provided for every event, along with time and sequence +information. + + +@section events Event processing + +GLFW needs to communicate regularly with the window system both in order to +receive events and to show that the application hasn't locked up. Event +processing must be done regularly while you have any windows and is normally +done each frame after [buffer swapping](@ref buffer_swap). Even when you have +no windows, event polling needs to be done in order to receive monitor +connection events. + +There are two functions for processing pending events. @ref glfwPollEvents, +processes only those events that have already been received and then returns +immediately. + +@code +glfwPollEvents(); +@endcode + +This is the best choice when rendering continually, like most games do. + +If you only need to update the contents of the window when you receive new +input, @ref glfwWaitEvents is a better choice. + +@code +glfwWaitEvents(); +@endcode + +It puts the thread to sleep until at least one event has been received and then +processes all received events. This saves a great deal of CPU cycles and is +useful for, for example, editing tools. There must be at least one GLFW window +for this function to sleep. + +If you want to wait for events but have UI elements that need periodic updates, +call @ref glfwWaitEventsTimeout. + +@code +glfwWaitEventsTimeout(0.7); +@endcode + +It puts the thread to sleep until at least one event has been received, or until +the specified number of seconds have elapsed. It then processes any received +events. + +If the main thread is sleeping in @ref glfwWaitEvents, you can wake it from +another thread by posting an empty event to the event queue with @ref +glfwPostEmptyEvent. + +@code +glfwPostEmptyEvent(); +@endcode + +Do not assume that callbacks will _only_ be called through either of the above +functions. While it is necessary to process events in the event queue, some +window systems will send some events directly to the application, which in turn +causes callbacks to be called outside of regular event processing. + + +@section input_keyboard Keyboard input + +GLFW divides keyboard input into two categories; key events and character +events. Key events relate to actual physical keyboard keys, whereas character +events relate to the Unicode code points generated by pressing some of them. + +Keys and characters do not map 1:1. A single key press may produce several +characters, and a single character may require several keys to produce. This +may not be the case on your machine, but your users are likely not all using the +same keyboard layout, input method or even operating system as you. + + +@subsection input_key Key input + +If you wish to be notified when a physical key is pressed or released or when it +repeats, set a key callback. + +@code +glfwSetKeyCallback(window, key_callback); +@endcode + +The callback function receives the [keyboard key](@ref keys), platform-specific +scancode, key action and [modifier bits](@ref mods). + +@code +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_E && action == GLFW_PRESS) + activate_airship(); +} +@endcode + +The action is one of `GLFW_PRESS`, `GLFW_REPEAT` or `GLFW_RELEASE`. The key +will be `GLFW_KEY_UNKNOWN` if GLFW lacks a key token for it, for example +_E-mail_ and _Play_ keys. + +The scancode is unique for every key, regardless of whether it has a key token. +Scancodes are platform-specific but consistent over time, so keys will have +different scancodes depending on the platform but they are safe to save to disk. + +Key states for [named keys](@ref keys) are also saved in per-window state arrays +that can be polled with @ref glfwGetKey. + +@code +int state = glfwGetKey(window, GLFW_KEY_E); +if (state == GLFW_PRESS) + activate_airship(); +@endcode + +The returned state is one of `GLFW_PRESS` or `GLFW_RELEASE`. + +This function only returns cached key event state. It does not poll the +system for the current state of the key. + +Whenever you poll state, you risk missing the state change you are looking for. +If a pressed key is released again before you poll its state, you will have +missed the key press. The recommended solution for this is to use a +key callback, but there is also the `GLFW_STICKY_KEYS` input mode. + +@code +glfwSetInputMode(window, GLFW_STICKY_KEYS, 1); +@endcode + +When sticky keys mode is enabled, the pollable state of a key will remain +`GLFW_PRESS` until the state of that key is polled with @ref glfwGetKey. Once +it has been polled, if a key release event had been processed in the meantime, +the state will reset to `GLFW_RELEASE`, otherwise it will remain `GLFW_PRESS`. + +The `GLFW_KEY_LAST` constant holds the highest value of any +[named key](@ref keys). + + +@subsection input_char Text input + +GLFW supports text input in the form of a stream of +[Unicode code points](https://en.wikipedia.org/wiki/Unicode), as produced by the +operating system text input system. Unlike key input, text input obeys keyboard +layouts and modifier keys and supports composing characters using +[dead keys](https://en.wikipedia.org/wiki/Dead_key). Once received, you can +encode the code points into +[UTF-8](https://en.wikipedia.org/wiki/UTF-8) or any other encoding you prefer. + +Because an `unsigned int` is 32 bits long on all platforms supported by GLFW, +you can treat the code point argument as native endian +[UTF-32](https://en.wikipedia.org/wiki/UTF-32). + +There are two callbacks for receiving Unicode code points. If you wish to +offer regular text input, set a character callback. + +@code +glfwSetCharCallback(window, character_callback); +@endcode + +The callback function receives Unicode code points for key events that would +have led to regular text input and generally behaves as a standard text field on +that platform. + +@code +void character_callback(GLFWwindow* window, unsigned int codepoint) +{ +} +@endcode + +If you wish to receive even those Unicode code points generated with modifier +key combinations that a plain text field would ignore, or just want to know +exactly what modifier keys were used, set a character with modifiers callback. + +@code +glfwSetCharModsCallback(window, charmods_callback); +@endcode + +The callback function receives Unicode code points and +[modifier bits](@ref mods). + +@code +void charmods_callback(GLFWwindow* window, unsigned int codepoint, int mods) +{ +} +@endcode + + +@subsection input_key_name Key names + +If you wish to refer to keys by name, you can query the keyboard layout +dependent name of printable keys with @ref glfwGetKeyName. + +@code +const char* key_name = glfwGetKeyName(GLFW_KEY_W, 0); +show_tutorial_hint("Press %s to move forward", key_name); +@endcode + +This function can handle both [keys and scancodes](@ref input_key). If the +specified key is `GLFW_KEY_UNKNOWN` then the scancode is used, otherwise it is +ignored. This matches the behavior of the key callback, meaning the callback +arguments can always be passed unmodified to this function. + + +@subsection input_key_scancode Key scancodes + +If you need the platform dependent scancode for a [named key](@ref keys), you +can query it with @ref glfwGetKeyScancode. + +@code +const int scancode = glfwGetKeyScancode(GLFW_KEY_X); +set_key_mapping(scancode, swap_weapons); +@endcode + + +@section input_mouse Mouse input + +Mouse input comes in many forms, including cursor motion, button presses and +scrolling offsets. The cursor appearance can also be changed, either to +a custom image or a standard cursor shape from the system theme. + + +@subsection cursor_pos Cursor position + +If you wish to be notified when the cursor moves over the window, set a cursor +position callback. + +@code +glfwSetCursorPosCallback(window, cursor_pos_callback); +@endcode + +The callback functions receives the cursor position, measured in screen +coordinates but relative to the top-left corner of the window client area. On +platforms that provide it, the full sub-pixel cursor position is passed on. + +@code +static void cursor_position_callback(GLFWwindow* window, double xpos, double ypos) +{ +} +@endcode + +The cursor position is also saved per-window and can be polled with @ref +glfwGetCursorPos. + +@code +double xpos, ypos; +glfwGetCursorPos(window, &xpos, &ypos); +@endcode + + +@subsection cursor_mode Cursor modes + +The `GLFW_CURSOR` input mode provides several cursor modes for special forms of +mouse motion input. By default, the cursor mode is `GLFW_CURSOR_NORMAL`, +meaning the regular arrow cursor (or another cursor set with @ref glfwSetCursor) +is used and cursor motion is not limited. + +If you wish to implement mouse motion based camera controls or other input +schemes that require unlimited mouse movement, set the cursor mode to +`GLFW_CURSOR_DISABLED`. + +@code +glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); +@endcode + +This will hide the cursor and lock it to the specified window. GLFW will then +take care of all the details of cursor re-centering and offset calculation and +providing the application with a virtual cursor position. This virtual position +is provided normally via both the cursor position callback and through polling. + +@note You should not implement your own version of this functionality using +other features of GLFW. It is not supported and will not work as robustly as +`GLFW_CURSOR_DISABLED`. + +If you just wish the cursor to become hidden when it is over a window, set +the cursor mode to `GLFW_CURSOR_HIDDEN`. + +@code +glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); +@endcode + +This mode puts no limit on the motion of the cursor. + +To exit out of either of these special modes, restore the `GLFW_CURSOR_NORMAL` +cursor mode. + +@code +glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); +@endcode + + +@subsection cursor_object Cursor objects + +GLFW supports creating both custom and system theme cursor images, encapsulated +as @ref GLFWcursor objects. They are created with @ref glfwCreateCursor or @ref +glfwCreateStandardCursor and destroyed with @ref glfwDestroyCursor, or @ref +glfwTerminate, if any remain. + + +@subsubsection cursor_custom Custom cursor creation + +A custom cursor is created with @ref glfwCreateCursor, which returns a handle to +the created cursor object. For example, this creates a 16x16 white square +cursor with the hot-spot in the upper-left corner: + +@code +unsigned char pixels[16 * 16 * 4]; +memset(pixels, 0xff, sizeof(pixels)); + +GLFWimage image; +image.width = 16; +image.height = 16; +image.pixels = pixels; + +GLFWcursor* cursor = glfwCreateCursor(&image, 0, 0); +@endcode + +If cursor creation fails, `NULL` will be returned, so it is necessary to check +the return value. + +The image data is 32-bit, little-endian, non-premultiplied RGBA, i.e. eight bits +per channel. The pixels are arranged canonically as sequential rows, starting +from the top-left corner. + + +@subsubsection cursor_standard Standard cursor creation + +A cursor with a [standard shape](@ref shapes) from the current system cursor +theme can be can be created with @ref glfwCreateStandardCursor. + +@code +GLFWcursor* cursor = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); +@endcode + +These cursor objects behave in the exact same way as those created with @ref +glfwCreateCursor except that the system cursor theme provides the actual image. + + +@subsubsection cursor_destruction Cursor destruction + +When a cursor is no longer needed, destroy it with @ref glfwDestroyCursor. + +@code +glfwDestroyCursor(cursor); +@endcode + +Cursor destruction always succeeds. All cursors remaining when @ref +glfwTerminate is called are destroyed as well. + + +@subsubsection cursor_set Cursor setting + +A cursor can be set as current for a window with @ref glfwSetCursor. + +@code +glfwSetCursor(window, cursor); +@endcode + +Once set, the cursor image will be used as long as the system cursor is over the +client area of the window and the [cursor mode](@ref cursor_mode) is set +to `GLFW_CURSOR_NORMAL`. + +A single cursor may be set for any number of windows. + +To remove a cursor from a window, set the cursor of that window to `NULL`. + +@code +glfwSetCursor(window, NULL); +@endcode + +When a cursor is destroyed, it is removed from any window where it is set. This +does not affect the cursor modes of those windows. + + +@subsection cursor_enter Cursor enter/leave events + +If you wish to be notified when the cursor enters or leaves the client area of +a window, set a cursor enter/leave callback. + +@code +glfwSetCursorEnterCallback(window, cursor_enter_callback); +@endcode + +The callback function receives the new classification of the cursor. + +@code +void cursor_enter_callback(GLFWwindow* window, int entered) +{ + if (entered) + { + // The cursor entered the client area of the window + } + else + { + // The cursor left the client area of the window + } +} +@endcode + + +@subsection input_mouse_button Mouse button input + +If you wish to be notified when a mouse button is pressed or released, set +a mouse button callback. + +@code +glfwSetMouseButtonCallback(window, mouse_button_callback); +@endcode + +The callback function receives the [mouse button](@ref buttons), button action +and [modifier bits](@ref mods). + +@code +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + if (button == GLFW_MOUSE_BUTTON_RIGHT && action == GLFW_PRESS) + popup_menu(); +} +@endcode + +The action is one of `GLFW_PRESS` or `GLFW_RELEASE`. + +Mouse button states for [named buttons](@ref buttons) are also saved in +per-window state arrays that can be polled with @ref glfwGetMouseButton. + +@code +int state = glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT); +if (state == GLFW_PRESS) + upgrade_cow(); +@endcode + +The returned state is one of `GLFW_PRESS` or `GLFW_RELEASE`. + +This function only returns cached mouse button event state. It does not poll +the system for the current state of the mouse button. + +Whenever you poll state, you risk missing the state change you are looking for. +If a pressed mouse button is released again before you poll its state, you will have +missed the button press. The recommended solution for this is to use a +mouse button callback, but there is also the `GLFW_STICKY_MOUSE_BUTTONS` +input mode. + +@code +glfwSetInputMode(window, GLFW_STICKY_MOUSE_BUTTONS, 1); +@endcode + +When sticky mouse buttons mode is enabled, the pollable state of a mouse button +will remain `GLFW_PRESS` until the state of that button is polled with @ref +glfwGetMouseButton. Once it has been polled, if a mouse button release event +had been processed in the meantime, the state will reset to `GLFW_RELEASE`, +otherwise it will remain `GLFW_PRESS`. + +The `GLFW_MOUSE_BUTTON_LAST` constant holds the highest value of any +[named button](@ref buttons). + + +@subsection scrolling Scroll input + +If you wish to be notified when the user scrolls, whether with a mouse wheel or +touchpad gesture, set a scroll callback. + +@code +glfwSetScrollCallback(window, scroll_callback); +@endcode + +The callback function receives two-dimensional scroll offsets. + +@code +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) +{ +} +@endcode + +A simple mouse wheel, being vertical, provides offsets along the Y-axis. + + +@section joystick Joystick input + +The joystick functions expose connected joysticks and controllers, with both +referred to as joysticks. It supports up to sixteen joysticks, ranging from +`GLFW_JOYSTICK_1`, `GLFW_JOYSTICK_2` up to `GLFW_JOYSTICK_LAST`. You can test +whether a [joystick](@ref joysticks) is present with @ref glfwJoystickPresent. + +@code +int present = glfwJoystickPresent(GLFW_JOYSTICK_1); +@endcode + +When GLFW is initialized, detected joysticks are added to to the beginning of +the array, starting with `GLFW_JOYSTICK_1`. Once a joystick is detected, it +keeps its assigned index until it is disconnected, so as joysticks are connected +and disconnected, they will become spread out. + +Joystick state is updated as needed when a joystick function is called and does +not require a window to be created or @ref glfwPollEvents or @ref glfwWaitEvents +to be called. + + +@subsection joystick_axis Joystick axis states + +The positions of all axes of a joystick are returned by @ref +glfwGetJoystickAxes. See the reference documentation for the lifetime of the +returned array. + +@code +int count; +const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &count); +@endcode + +Each element in the returned array is a value between -1.0 and 1.0. + + +@subsection joystick_button Joystick button states + +The states of all buttons of a joystick are returned by @ref +glfwGetJoystickButtons. See the reference documentation for the lifetime of the +returned array. + +@code +int count; +const unsigned char* axes = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &count); +@endcode + +Each element in the returned array is either `GLFW_PRESS` or `GLFW_RELEASE`. + + +@subsection joystick_name Joystick name + +The human-readable, UTF-8 encoded name of a joystick is returned by @ref +glfwGetJoystickName. See the reference documentation for the lifetime of the +returned string. + +@code +const char* name = glfwGetJoystickName(GLFW_JOYSTICK_1); +@endcode + +Joystick names are not guaranteed to be unique. Two joysticks of the same model +and make may have the same name. Only the [joystick token](@ref joysticks) is +guaranteed to be unique, and only until that joystick is disconnected. + + +@subsection joystick_event Joystick configuration changes + +If you wish to be notified when a joystick is connected or disconnected, set +a joystick callback. + +@code +glfwSetJoystickCallback(joystick_callback); +@endcode + +The callback function receives the ID of the joystick that has been connected +and disconnected and the event that occurred. + +@code +void joystick_callback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + { + // The joystick was connected + } + else if (event == GLFW_DISCONNECTED) + { + // The joystick was disconnected + } +} +@endcode + + +@section time Time input + +GLFW provides high-resolution time input, in seconds, with @ref glfwGetTime. + +@code +double seconds = glfwGetTime(); +@endcode + +It returns the number of seconds since the timer was started when the library +was initialized with @ref glfwInit. The platform-specific time sources used +usually have micro- or nanosecond resolution. + +You can modify the reference time with @ref glfwSetTime. + +@code +glfwSetTime(4.0); +@endcode + +This sets the timer to the specified time, in seconds. + +You can also access the raw timer value, measured in 1 / frequency +seconds, with @ref glfwGetTimerValue. + +@code +uint64_t value = glfwGetTimerValue(); +@endcode + +The frequency of the raw timer varies depending on what time sources are +available on the machine. You can query its frequency, in Hz, with @ref +glfwGetTimerFrequency. + +@code +uint64_t freqency = glfwGetTimerFrequency(); +@endcode + + +@section clipboard Clipboard input and output + +If the system clipboard contains a UTF-8 encoded string or if it can be +converted to one, you can retrieve it with @ref glfwGetClipboardString. See the +reference documentation for the lifetime of the returned string. + +@code +const char* text = glfwGetClipboardString(window); +if (text) + insert_text(text); +@endcode + +If the clipboard is empty or if its contents could not be converted, `NULL` is +returned. + +The contents of the system clipboard can be set to a UTF-8 encoded string with +@ref glfwSetClipboardString. + +@code +glfwSetClipboardString(window, "A string with words in it"); +@endcode + +The clipboard functions take a window handle argument because some window +systems require a window to communicate with the system clipboard. Any valid +window may be used. + + +@section path_drop Path drop input + +If you wish to receive the paths of files and/or directories dropped on +a window, set a file drop callback. + +@code +glfwSetDropCallback(window, drop_callback); +@endcode + +The callback function receives an array of paths encoded as UTF-8. + +@code +void drop_callback(GLFWwindow* window, int count, const char** paths) +{ + int i; + for (i = 0; i < count; i++) + handle_dropped_file(paths[i]); +} +@endcode + +The path array and its strings are only valid until the file drop callback +returns, as they may have been generated specifically for that event. You need +to make a deep copy of the array if you want to keep the paths. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/internal.dox b/apps/exampleViewer/common/glfw/docs/internal.dox new file mode 100644 index 0000000000..3eb0aee560 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/internal.dox @@ -0,0 +1,116 @@ +/*! + +@page internals_guide Internal structure + +@tableofcontents + +There are several interfaces inside GLFW. Each interface has its own area of +responsibility and its own naming conventions. + + +@section internals_public Public interface + +The most well-known is the public interface, described in the glfw3.h header +file. This is implemented in source files shared by all platforms and these +files contain no platform-specific code. This code usually ends up calling the +platform and internal interfaces to do the actual work. + +The public interface uses the OpenGL naming conventions except with GLFW and +glfw instead of GL and gl. For struct members, where OpenGL sets no precedent, +it use headless camel case. + +Examples: @ref glfwCreateWindow, @ref GLFWwindow, @ref GLFWvidmode.redBits, +`GLFW_RED_BITS` + + +@section internals_native Native interface + +The [native interface](@ref native) is a small set of publicly available +but platform-specific functions, described in the glfw3native.h header file and +used to gain access to the underlying window, context and (on some platforms) +display handles used by the platform interface. + +The function names of the native interface are similar to those of the public +interface, but embeds the name of the interface that the returned handle is +from. + +Examples: @ref glfwGetX11Window, @ref glfwGetWGLContext + + +@section internals_internal Internal interface + +The internal interface consists of utility functions used by all other +interfaces. It is shared code implemented in the same shared source files as +the public and event interfaces. The internal interface is described in the +internal.h header file. + +The internal interface is in charge of GLFW's global data, which it stores in +a `_GLFWlibrary` struct named `_glfw`. + +The internal interface uses the same style as the public interface, except all +global names have a leading underscore. + +Examples: @ref _glfwIsValidContextConfig, @ref _GLFWwindow, `_glfw.currentRamp` + + +@section internals_platform Platform interface + +The platform interface implements all platform-specific operations as a service +to the public interface. This includes event processing. The platform +interface is never directly called by application code and never directly calls +application-provided callbacks. It is also prohibited from modifying the +platform-independent part of the internal structs. Instead, it calls the event +interface when events interesting to GLFW are received. + +The platform interface mirrors those parts of the public interface that needs to +perform platform-specific operations on some or all platforms. The are also +named the same except that the glfw function prefix is replaced by +_glfwPlatform. + +Examples: @ref _glfwPlatformCreateWindow + +The platform interface also defines structs that contain platform-specific +global and per-object state. Their names mirror those of the internal +interface, except that an interface-specific suffix is added. + +Examples: `_GLFWwindowX11`, `_GLFWcontextWGL` + +These structs are incorporated as members into the internal interface structs +using special macros that name them after the specific interface used. This +prevents shared code from accidentally using these members. + +Examples: `window.win32.handle`, `_glfw.x11.display` + + +@section internals_event Event interface + +The event interface is implemented in the same shared source files as the public +interface and is responsible for delivering the events it receives to the +application, either via callbacks, via window state changes or both. + +The function names of the event interface use a `_glfwInput` prefix and the +ObjectEvent pattern. + +Examples: @ref _glfwInputWindowFocus, @ref _glfwInputCursorMotion + + +@section internals_static Static functions + +Static functions may be used by any interface and have no prefixes or suffixes. +These use headless camel case. + +Examples: `clearScrollOffsets` + + +@section internals_config Configuration macros + +GLFW uses a number of configuration macros to select at compile time which +interfaces and code paths to use. They are defined in the glfw_config.h header file, +which is generated from the `glfw_config.h.in` file by CMake. + +Configuration macros the same style as tokens in the public interface, except +with a leading underscore. + +Examples: `_GLFW_HAS_XF86VM` + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/intro.dox b/apps/exampleViewer/common/glfw/docs/intro.dox new file mode 100644 index 0000000000..f784080046 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/intro.dox @@ -0,0 +1,372 @@ +/*! + +@page intro_guide Introduction to the API + +@tableofcontents + +This guide introduces the basic concepts of GLFW and describes initialization, +error handling and API guarantees and limitations. For a broad but shallow +tutorial, see @ref quick_guide instead. For details on a specific function in +this category, see the @ref init. + +There are also guides for the other areas of GLFW. + + - @ref window_guide + - @ref context_guide + - @ref vulkan_guide + - @ref monitor_guide + - @ref input_guide + + +@section intro_init Initialization and termination + +Before most GLFW functions may be called, the library must be initialized. +This initialization checks what features are available on the machine, +enumerates monitors and joysticks, initializes the timer and performs any +required platform-specific initialization. + +Only the following functions may be called before the library has been +successfully initialized, and only from the main thread. + + - @ref glfwGetVersion + - @ref glfwGetVersionString + - @ref glfwSetErrorCallback + - @ref glfwInit + - @ref glfwTerminate + +Calling any other function before successful initialization will cause a @ref +GLFW_NOT_INITIALIZED error. + + +@subsection intro_init_init Initializing GLFW + +The library is initialized with @ref glfwInit, which returns `GLFW_FALSE` if an +error occurred. + +@code +if (!glfwInit()) +{ + // Handle initialization failure +} +@endcode + +If any part of initialization fails, any parts that succeeded are terminated as +if @ref glfwTerminate had been called. The library only needs to be initialized +once and additional calls to an already initialized library will simply return +`GLFW_TRUE` immediately. + +Once the library has been successfully initialized, it should be terminated +before the application exits. Modern systems are very good at freeing resources +allocated by programs that simply exit, but GLFW sometimes has to change global +system settings and these might not be restored without termination. + + +@subsection intro_init_terminate Terminating GLFW + +Before your application exits, you should terminate the GLFW library if it has +been initialized. This is done with @ref glfwTerminate. + +@code +glfwTerminate(); +@endcode + +This will destroy any remaining window, monitor and cursor objects, restore any +modified gamma ramps, re-enable the screensaver if it had been disabled and free +any other resources allocated by GLFW. + +Once the library is terminated, it is as if it had never been initialized and +you will need to initialize it again before being able to use GLFW. If the +library was not initialized or had already been terminated, it return +immediately. + + +@section error_handling Error handling + +Some GLFW functions have return values that indicate an error, but this is often +not very helpful when trying to figure out _why_ the error occurred. Some +functions also return otherwise valid values on error. Finally, far from all +GLFW functions have return values. + +This is where the error callback comes in. This callback is called whenever an +error occurs. It is set with @ref glfwSetErrorCallback, a function that may be +called regardless of whether GLFW is initialized. + +@code +glfwSetErrorCallback(error_callback); +@endcode + +The error callback receives a human-readable description of the error and (when +possible) its cause. The description encoded as UTF-8. The callback is also +provided with an [error code](@ref errors). + +@code +void error_callback(int error, const char* description) +{ + puts(description); +} +@endcode + +The error code indicates the general category of the error. Some error codes, +such as @ref GLFW_NOT_INITIALIZED has only a single meaning, whereas others like +@ref GLFW_PLATFORM_ERROR are used for many different errors. + +The description string is only valid until the error callback returns, as it may +have been generated specifically for that error. This lets GLFW provide much +more specific error descriptions but means you must make a copy if you want to +keep the description string. + +@note Relying on erroneous behavior is not forward compatible. In other words, +do not rely on a currently invalid call to generate a specific error, as that +same call may in future versions generate a different error or become valid. + + +@section coordinate_systems Coordinate systems + +GLFW has two primary coordinate systems: the _virtual screen_ and the window +_client area_ or _content area_. Both use the same unit: _virtual screen +coordinates_, or just _screen coordinates_, which don't necessarily correspond +to pixels. + + + +Both the virtual screen and the client area coordinate systems have the X-axis +pointing to the right and the Y-axis pointing down. + +Window and monitor positions are specified as the position of the upper-left +corners of their content areas relative to the virtual screen, while cursor +positions are specified relative to a window's client area. + +Because the origin of the window's client area coordinate system is also the +point from which the window position is specified, you can translate client area +coordinates to the virtual screen by adding the window position. The window +frame, when present, extends out from the client area but does not affect the +window position. + +Almost all positions and sizes in GLFW are measured in screen coordinates +relative to one of the two origins above. This includes cursor positions, +window positions and sizes, window frame sizes, monitor positions and video mode +resolutions. + +Two exceptions are the [monitor physical size](@ref monitor_size), which is +measured in millimetres, and [framebuffer size](@ref window_fbsize), which is +measured in pixels. + +Pixels and screen coordinates may map 1:1 on your machine, but they won't on +every other machine, for example on a Mac with a Retina display. The ratio +between screen coordinates and pixels may also change at run-time depending on +which monitor the window is currently considered to be on. + + +@section guarantees_limitations Guarantees and limitations + +This section describes the conditions under which GLFW can be expected to +function, barring bugs in the operating system or drivers. Use of GLFW outside +of these limits may work on some platforms, or on some machines, or some of the +time, or on some versions of GLFW, but it may break at any time and this will +not be considered a bug. + + +@subsection lifetime Pointer lifetimes + +GLFW will never free any pointer you provide to it and you must never free any +pointer it provides to you. + +Many GLFW functions return pointers to dynamically allocated structures, strings +or arrays, and some callbacks are provided with strings or arrays. These are +always managed by GLFW and should never be freed by the application. The +lifetime of these pointers is documented for each GLFW function and callback. +If you need to keep this data, you must copy it before its lifetime expires. + +Many GLFW functions accept pointers to structures or strings allocated by the +application. These are never freed by GLFW and are always the responsibility of +the application. If GLFW needs to keep the data in these structures or strings, +it is copied before the function returns. + +Pointer lifetimes are guaranteed not to be shortened in future minor or patch +releases. + + +@subsection reentrancy Reentrancy + +GLFW event processing and object creation and destruction are not reentrant. +This means that the following functions must not be called from any callback +function: + + - @ref glfwCreateWindow + - @ref glfwDestroyWindow + - @ref glfwCreateCursor + - @ref glfwCreateStandardCursor + - @ref glfwDestroyCursor + - @ref glfwPollEvents + - @ref glfwWaitEvents + - @ref glfwWaitEventsTimeout + - @ref glfwTerminate + +These functions may be made reentrant in future minor or patch releases, but +functions not on this list will not be made non-reentrant. + + +@subsection thread_safety Thread safety + +Most GLFW functions must only be called from the main thread, but some may be +called from any thread. However, no GLFW function may be called from any thread +but the main thread until GLFW has been successfully initialized, including +functions that may called before initialization. + +The reference documentation for every GLFW function states whether it is limited +to the main thread. + +Initialization and termination, event processing and the creation and +destruction of windows, contexts and cursors are all limited to the main thread +due to limitations of one or several platforms. + +Because event processing must be performed on the main thread, all callbacks +except for the error callback will only be called on that thread. The error +callback may be called on any thread, as any GLFW function may generate errors. + +The posting of empty events may be done from any thread. The window user +pointer and close flag may also be accessed and modified from any thread, but +this is not synchronized by GLFW. The following window related functions may +be called from any thread: + + - @ref glfwPostEmptyEvent + - @ref glfwGetWindowUserPointer + - @ref glfwSetWindowUserPointer + - @ref glfwWindowShouldClose + - @ref glfwSetWindowShouldClose + +Rendering may be done on any thread. The following context related functions +may be called from any thread: + + - @ref glfwMakeContextCurrent + - @ref glfwGetCurrentContext + - @ref glfwSwapBuffers + - @ref glfwSwapInterval + - @ref glfwExtensionSupported + - @ref glfwGetProcAddress + +The raw timer may be queried from any thread. The following raw timer related +functions may be called from any thread: + + - @ref glfwGetTimerFrequency + - @ref glfwGetTimerValue + +The regular timer may be used from any thread, but the reading and writing of +the timer offset is not synchronized by GLFW. The following timer related +functions may be called from any thread: + + - @ref glfwGetTime + - @ref glfwSetTime + +Library version information may be queried from any thread. The following +version related functions may be called from any thread: + + - @ref glfwGetVersion + - @ref glfwGetVersionString + +Vulkan objects may be created and information queried from any thread. The +following Vulkan related functions may be called from any thread: + + - @ref glfwVulkanSupported + - @ref glfwGetRequiredInstanceExtensions + - @ref glfwGetInstanceProcAddress + - @ref glfwGetPhysicalDevicePresentationSupport + - @ref glfwCreateWindowSurface + +GLFW uses no synchronization objects internally except for thread-local storage +to keep track of the current context for each thread. Synchronization is left +to the application. + +Functions that may currently be called from any thread will always remain so, +but functions that are currently limited to the main thread may be updated to +allow calls from any thread in future releases. + + +@subsection compatibility Version compatibility + +GLFW guarantees source and binary backward compatibility with earlier minor +versions of the API. This means that you can drop in a newer version of the +library and existing programs will continue to compile and existing binaries +will continue to run. + +Once a function or constant has been added, the signature of that function or +value of that constant will remain unchanged until the next major version of +GLFW. No compatibility of any kind is guaranteed between major versions. + +Undocumented behavior, i.e. behavior that is not described in the documentation, +may change at any time until it is documented. + +If the reference documentation and the implementation differ, the reference +documentation is correct and the implementation will be fixed in the next +release. + + +@subsection event_order Event order + +The order of arrival of related events is not guaranteed to be consistent +across platforms. The exception is synthetic key and mouse button release +events, which are always delivered after the window defocus event. + + +@section intro_version Version management + +GLFW provides mechanisms for identifying what version of GLFW your application +was compiled against as well as what version it is currently running against. +If you are loading GLFW dynamically (not just linking dynamically), you can use +this to verify that the library binary is compatible with your application. + + +@subsection intro_version_compile Compile-time version + +The compile-time version of GLFW is provided by the GLFW header with the +`GLFW_VERSION_MAJOR`, `GLFW_VERSION_MINOR` and `GLFW_VERSION_REVISION` macros. + +@code +printf("Compiled against GLFW %i.%i.%i\n", + GLFW_VERSION_MAJOR, + GLFW_VERSION_MINOR, + GLFW_VERSION_REVISION); +@endcode + + +@subsection intro_version_runtime Run-time version + +The run-time version can be retrieved with @ref glfwGetVersion, a function that +may be called regardless of whether GLFW is initialized. + +@code +int major, minor, revision; +glfwGetVersion(&major, &minor, &revision); + +printf("Running against GLFW %i.%i.%i\n", major, minor, revision); +@endcode + + +@subsection intro_version_string Version string + +GLFW 3 also provides a compile-time generated version string that describes the +version, platform, compiler and any platform-specific compile-time options. +This is primarily intended for submitting bug reports, to allow developers to +see which code paths are enabled in a binary. + +The version string is returned by @ref glfwGetVersionString, a function that may +be called regardless of whether GLFW is initialized. + +__Do not use the version string__ to parse the GLFW library version. The @ref +glfwGetVersion function already provides the version of the running library +binary. + +The format of the string is as follows: + - The version of GLFW + - The name of the window system API + - The name of the context creation API + - Any additional options or APIs + +For example, when compiling GLFW 3.0 with MinGW using the Win32 and WGL +back ends, the version string may look something like this: + +@code +3.0.0 Win32 WGL MinGW +@endcode + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/main.dox b/apps/exampleViewer/common/glfw/docs/main.dox new file mode 100644 index 0000000000..fc95cc4eb8 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/main.dox @@ -0,0 +1,47 @@ +/*! + +@mainpage notitle + +@section main_intro Introduction + +GLFW is a free, Open Source, multi-platform library for OpenGL, OpenGL ES and +Vulkan application development. It provides a simple, platform-independent API +for creating windows, contexts and surfaces, reading input, handling events, etc. + +See @ref news_33 for release highlights or the +[version history](http://www.glfw.org/changelog.html) for details. + +@ref quick_guide is a guide for users new to GLFW. It takes you through how to +write a small but complete program. + +There are guides for each section of the API: + + - @ref intro_guide – initialization, error handling and high-level design + - @ref window_guide – creating and working with windows and framebuffers + - @ref context_guide – working with OpenGL and OpenGL ES contexts + - @ref vulkan_guide - working with Vulkan objects and extensions + - @ref monitor_guide – enumerating and working with monitors and video modes + - @ref input_guide – receiving events, polling and processing input + +Once you have written a program, see @ref compile_guide and @ref build_guide. + +The [reference documentation](modules.html) provides more detailed information +about specific functions. + +@ref moving_guide explains what has changed and how to update existing code to +use the new API. + +There is a section on @ref guarantees_limitations for pointer lifetimes, +reentrancy, thread safety, event order and backward and forward compatibility. + +The [FAQ](http://www.glfw.org/faq.html) answers many common questions about the +design, implementation and use of GLFW. + +Finally, @ref compat_guide explains what APIs, standards and protocols GLFW uses +and what happens when they are not present on a given machine. + +This documentation was generated with Doxygen. The sources for it are available +in both the [source distribution](http://www.glfw.org/download.html) and +[GitHub repository](https://github.com/glfw/glfw). + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/monitor.dox b/apps/exampleViewer/common/glfw/docs/monitor.dox new file mode 100644 index 0000000000..0aa0b05053 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/monitor.dox @@ -0,0 +1,216 @@ +/*! + +@page monitor_guide Monitor guide + +@tableofcontents + +This guide introduces the monitor related functions of GLFW. For details on +a specific function in this category, see the @ref monitor. There are also +guides for the other areas of GLFW. + + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref vulkan_guide + - @ref input_guide + + +@section monitor_object Monitor objects + +A monitor object represents a currently connected monitor and is represented as +a pointer to the [opaque](https://en.wikipedia.org/wiki/Opaque_data_type) type +@ref GLFWmonitor. Monitor objects cannot be created or destroyed by the +application and retain their addresses until the monitors they represent are +disconnected or until the library is [terminated](@ref intro_init_terminate). + +Each monitor has a current video mode, a list of supported video modes, +a virtual position, a human-readable name, an estimated physical size and +a gamma ramp. One of the monitors is the primary monitor. + +The virtual position of a monitor is in +[screen coordinates](@ref coordinate_systems) and, together with the current +video mode, describes the viewports that the connected monitors provide into the +virtual desktop that spans them. + +To see how GLFW views your monitor setup and its available video modes, run the +`monitors` test program. + + +@subsection monitor_monitors Retrieving monitors + +The primary monitor is returned by @ref glfwGetPrimaryMonitor. It is the user's +preferred monitor and is usually the one with global UI elements like task bar +or menu bar. + +@code +GLFWmonitor* primary = glfwGetPrimaryMonitor(); +@endcode + +You can retrieve all currently connected monitors with @ref glfwGetMonitors. +See the reference documentation for the lifetime of the returned array. + +@code +int count; +GLFWmonitor** monitors = glfwGetMonitors(&count); +@endcode + +The primary monitor is always the first monitor in the returned array, but other +monitors may be moved to a different index when a monitor is connected or +disconnected. + + +@subsection monitor_event Monitor configuration changes + +If you wish to be notified when a monitor is connected or disconnected, set +a monitor callback. + +@code +glfwSetMonitorCallback(monitor_callback); +@endcode + +The callback function receives the handle for the monitor that has been +connected or disconnected and the event that occurred. + +@code +void monitor_callback(GLFWmonitor* monitor, int event) +{ + if (event == GLFW_CONNECTED) + { + // The monitor was connected + } + else if (event == GLFW_DISCONNECTED) + { + // The monitor was disconnected + } +} +@endcode + +If a monitor is disconnected, any windows that are full screen on it get forced +into windowed mode. + + +@section monitor_properties Monitor properties + +Each monitor has a current video mode, a list of supported video modes, +a virtual position, a human-readable name, an estimated physical size and +a gamma ramp. + + +@subsection monitor_modes Video modes + +GLFW generally does a good job selecting a suitable video mode when you create +a full screen window, change its video mode or or make a windowed one full +screen, but it is sometimes useful to know exactly which video modes are +supported. + +Video modes are represented as @ref GLFWvidmode structures. You can get an +array of the video modes supported by a monitor with @ref glfwGetVideoModes. +See the reference documentation for the lifetime of the returned array. + +@code +int count; +GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); +@endcode + +To get the current video mode of a monitor call @ref glfwGetVideoMode. See the +reference documentation for the lifetime of the returned pointer. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); +@endcode + +The resolution of a video mode is specified in +[screen coordinates](@ref coordinate_systems), not pixels. + + +@subsection monitor_size Physical size + +The physical size of a monitor in millimetres, or an estimation of it, can be +retrieved with @ref glfwGetMonitorPhysicalSize. This has no relation to its +current _resolution_, i.e. the width and height of its current +[video mode](@ref monitor_modes). + +@code +int widthMM, heightMM; +glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); +@endcode + +This can, for example, be used together with the current video mode to calculate +the DPI of a monitor. + +@code +const double dpi = mode->width / (widthMM / 25.4); +@endcode + + +@subsection monitor_pos Virtual position + +The position of the monitor on the virtual desktop, in +[screen coordinates](@ref coordinate_systems), can be retrieved with @ref +glfwGetMonitorPos. + +@code +int xpos, ypos; +glfwGetMonitorPos(monitor, &xpos, &ypos); +@endcode + + +@subsection monitor_name Human-readable name + +The human-readable, UTF-8 encoded name of a monitor is returned by @ref +glfwGetMonitorName. See the reference documentation for the lifetime of the +returned string. + +@code +const char* name = glfwGetMonitorName(monitor); +@endcode + +Monitor names are not guaranteed to be unique. Two monitors of the same model +and make may have the same name. Only the monitor handle is guaranteed to be +unique, and only until that monitor is disconnected. + + +@subsection monitor_gamma Gamma ramp + +The gamma ramp of a monitor can be set with @ref glfwSetGammaRamp, which accepts +a monitor handle and a pointer to a @ref GLFWgammaramp structure. + +@code +GLFWgammaramp ramp; +unsigned short red[256], green[256], blue[256]; + +ramp.size = 256; +ramp.red = red; +ramp.green = green; +ramp.blue = blue; + +for (i = 0; i < ramp.size; i++) +{ + // Fill out gamma ramp arrays as desired +} + +glfwSetGammaRamp(monitor, &ramp); +@endcode + +The gamma ramp data is copied before the function returns, so there is no need +to keep it around once the ramp has been set. + +@note It is recommended to use gamma ramps of size 256, as that is the size +supported by all graphics cards on all platforms. + +The current gamma ramp for a monitor is returned by @ref glfwGetGammaRamp. See +the reference documentation for the lifetime of the returned structure. + +@code +const GLFWgammaramp* ramp = glfwGetGammaRamp(monitor); +@endcode + +If you wish to set a regular gamma ramp, you can have GLFW calculate it for you +from the desired exponent with @ref glfwSetGamma, which in turn calls @ref +glfwSetGammaRamp with the resulting ramp. + +@code +glfwSetGamma(monitor, 1.0); +@endcode + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/moving.dox b/apps/exampleViewer/common/glfw/docs/moving.dox new file mode 100644 index 0000000000..7baab6f19f --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/moving.dox @@ -0,0 +1,506 @@ +/*! + +@page moving_guide Moving from GLFW 2 to 3 + +@tableofcontents + +This is a transition guide for moving from GLFW 2 to 3. It describes what has +changed or been removed, but does _not_ include +[new features](@ref news) unless they are required when moving an existing code +base onto the new API. For example, the new multi-monitor functions are +required to create full screen windows with GLFW 3. + + +@section moving_removed Changed and removed features + +@subsection moving_renamed_files Renamed library and header file + +The GLFW 3 header is named @ref glfw3.h and moved to the `GLFW` directory, to +avoid collisions with the headers of other major versions. Similarly, the GLFW +3 library is named `glfw3,` except when it's installed as a shared library on +Unix-like systems, where it uses the +[soname](https://en.wikipedia.org/wiki/soname) `libglfw.so.3`. + +@par Old syntax +@code +#include +@endcode + +@par New syntax +@code +#include +@endcode + + +@subsection moving_threads Removal of threading functions + +The threading functions have been removed, including the per-thread sleep +function. They were fairly primitive, under-used, poorly integrated and took +time away from the focus of GLFW (i.e. context, input and window). There are +better threading libraries available and native threading support is available +in both [C++11](http://en.cppreference.com/w/cpp/thread) and +[C11](http://en.cppreference.com/w/c/thread), both of which are gaining +traction. + +If you wish to use the C++11 or C11 facilities but your compiler doesn't yet +support them, see the +[TinyThread++](https://gitorious.org/tinythread/tinythreadpp) and +[TinyCThread](https://github.com/tinycthread/tinycthread) projects created by +the original author of GLFW. These libraries implement a usable subset of the +threading APIs in C++11 and C11, and in fact some GLFW 3 test programs use +TinyCThread. + +However, GLFW 3 has better support for _use from multiple threads_ than GLFW +2 had. Contexts can be made current on any thread, although only a single +thread at a time, and the documentation explicitly states which functions may be +used from any thread and which must only be used from the main thread. + +@par Removed functions +`glfwSleep`, `glfwCreateThread`, `glfwDestroyThread`, `glfwWaitThread`, +`glfwGetThreadID`, `glfwCreateMutex`, `glfwDestroyMutex`, `glfwLockMutex`, +`glfwUnlockMutex`, `glfwCreateCond`, `glfwDestroyCond`, `glfwWaitCond`, +`glfwSignalCond`, `glfwBroadcastCond` and `glfwGetNumberOfProcessors`. + +@par Removed types +`GLFWthreadfun` + + +@subsection moving_image Removal of image and texture loading + +The image and texture loading functions have been removed. They only supported +the Targa image format, making them mostly useful for beginner level examples. +To become of sufficiently high quality to warrant keeping them in GLFW 3, they +would need not only to support other formats, but also modern extensions to +OpenGL texturing. This would either add a number of external +dependencies (libjpeg, libpng, etc.), or force GLFW to ship with inline versions +of these libraries. + +As there already are libraries doing this, it is unnecessary both to duplicate +the work and to tie the duplicate to GLFW. The resulting library would also be +platform-independent, as both OpenGL and stdio are available wherever GLFW is. + +@par Removed functions +`glfwReadImage`, `glfwReadMemoryImage`, `glfwFreeImage`, `glfwLoadTexture2D`, +`glfwLoadMemoryTexture2D` and `glfwLoadTextureImage2D`. + + +@subsection moving_stdcall Removal of GLFWCALL macro + +The `GLFWCALL` macro, which made callback functions use +[__stdcall](http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx) on Windows, +has been removed. GLFW is written in C, not Pascal. Removing this macro means +there's one less thing for application programmers to remember, i.e. the +requirement to mark all callback functions with `GLFWCALL`. It also simplifies +the creation of DLLs and DLL link libraries, as there's no need to explicitly +disable `@n` entry point suffixes. + +@par Old syntax +@code +void GLFWCALL callback_function(...); +@endcode + +@par New syntax +@code +void callback_function(...); +@endcode + + +@subsection moving_window_handles Window handle parameters + +Because GLFW 3 supports multiple windows, window handle parameters have been +added to all window-related GLFW functions and callbacks. The handle of +a newly created window is returned by @ref glfwCreateWindow (formerly +`glfwOpenWindow`). Window handles are pointers to the +[opaque](https://en.wikipedia.org/wiki/Opaque_data_type) type @ref GLFWwindow. + +@par Old syntax +@code +glfwSetWindowTitle("New Window Title"); +@endcode + +@par New syntax +@code +glfwSetWindowTitle(window, "New Window Title"); +@endcode + + +@subsection moving_monitor Explicit monitor selection + +GLFW 3 provides support for multiple monitors. To request a full screen mode window, +instead of passing `GLFW_FULLSCREEN` you specify which monitor you wish the +window to use. The @ref glfwGetPrimaryMonitor function returns the monitor that +GLFW 2 would have selected, but there are many other +[monitor functions](@ref monitor_guide). Monitor handles are pointers to the +[opaque](https://en.wikipedia.org/wiki/Opaque_data_type) type @ref GLFWmonitor. + +@par Old basic full screen +@code +glfwOpenWindow(640, 480, 8, 8, 8, 0, 24, 0, GLFW_FULLSCREEN); +@endcode + +@par New basic full screen +@code +window = glfwCreateWindow(640, 480, "My Window", glfwGetPrimaryMonitor(), NULL); +@endcode + +@note The framebuffer bit depth parameters of `glfwOpenWindow` have been turned +into [window hints](@ref window_hints), but as they have been given +[sane defaults](@ref window_hints_values) you rarely need to set these hints. + + +@subsection moving_autopoll Removal of automatic event polling + +GLFW 3 does not automatically poll for events in @ref glfwSwapBuffers, meaning +you need to call @ref glfwPollEvents or @ref glfwWaitEvents yourself. Unlike +buffer swap, which acts on a single window, the event processing functions act +on all windows at once. + +@par Old basic main loop +@code +while (...) +{ + // Process input + // Render output + glfwSwapBuffers(); +} +@endcode + +@par New basic main loop +@code +while (...) +{ + // Process input + // Render output + glfwSwapBuffers(window); + glfwPollEvents(); +} +@endcode + + +@subsection moving_context Explicit context management + +Each GLFW 3 window has its own OpenGL context and only you, the application +programmer, can know which context should be current on which thread at any +given time. Therefore, GLFW 3 leaves that decision to you. + +This means that you need to call @ref glfwMakeContextCurrent after creating +a window before you can call any OpenGL functions. + + +@subsection moving_hidpi Separation of window and framebuffer sizes + +Window positions and sizes now use screen coordinates, which may not be the same +as pixels on machines with high-DPI monitors. This is important as OpenGL uses +pixels, not screen coordinates. For example, the rectangle specified with +`glViewport` needs to use pixels. Therefore, framebuffer size functions have +been added. You can retrieve the size of the framebuffer of a window with @ref +glfwGetFramebufferSize function. A framebuffer size callback has also been +added, which can be set with @ref glfwSetFramebufferSizeCallback. + +@par Old basic viewport setup +@code +glfwGetWindowSize(&width, &height); +glViewport(0, 0, width, height); +@endcode + +@par New basic viewport setup +@code +glfwGetFramebufferSize(window, &width, &height); +glViewport(0, 0, width, height); +@endcode + + +@subsection moving_window_close Window closing changes + +The `GLFW_OPENED` window parameter has been removed. As long as the window has +not been destroyed, whether through @ref glfwDestroyWindow or @ref +glfwTerminate, the window is "open". + +A user attempting to close a window is now just an event like any other. Unlike +GLFW 2, windows and contexts created with GLFW 3 will never be destroyed unless +you choose them to be. Each window now has a close flag that is set to +`GLFW_TRUE` when the user attempts to close that window. By default, nothing else +happens and the window stays visible. It is then up to you to either destroy +the window, take some other action or simply ignore the request. + +You can query the close flag at any time with @ref glfwWindowShouldClose and set +it at any time with @ref glfwSetWindowShouldClose. + +@par Old basic main loop +@code +while (glfwGetWindowParam(GLFW_OPENED)) +{ + ... +} +@endcode + +@par New basic main loop +@code +while (!glfwWindowShouldClose(window)) +{ + ... +} +@endcode + +The close callback no longer returns a value. Instead, it is called after the +close flag has been set so it can override its value, if it chooses to, before +event processing completes. You may however not call @ref glfwDestroyWindow +from the close callback (or any other window related callback). + +@par Old syntax +@code +int GLFWCALL window_close_callback(void); +@endcode + +@par New syntax +@code +void window_close_callback(GLFWwindow* window); +@endcode + +@note GLFW never clears the close flag to `GLFW_FALSE`, meaning you can use it +for other reasons to close the window as well, for example the user choosing +Quit from an in-game menu. + + +@subsection moving_hints Persistent window hints + +The `glfwOpenWindowHint` function has been renamed to @ref glfwWindowHint. + +Window hints are no longer reset to their default values on window creation, but +instead retain their values until modified by @ref glfwWindowHint or @ref +glfwDefaultWindowHints, or until the library is terminated and re-initialized. + + +@subsection moving_video_modes Video mode enumeration + +Video mode enumeration is now per-monitor. The @ref glfwGetVideoModes function +now returns all available modes for a specific monitor instead of requiring you +to guess how large an array you need. The `glfwGetDesktopMode` function, which +had poorly defined behavior, has been replaced by @ref glfwGetVideoMode, which +returns the current mode of a monitor. + + +@subsection moving_char_up Removal of character actions + +The action parameter of the [character callback](@ref GLFWcharfun) has been +removed. This was an artefact of the origin of GLFW, i.e. being developed in +English by a Swede. However, many keyboard layouts require more than one key to +produce characters with diacritical marks. Even the Swedish keyboard layout +requires this for uncommon cases like ü. + +@par Old syntax +@code +void GLFWCALL character_callback(int character, int action); +@endcode + +@par New syntax +@code +void character_callback(GLFWwindow* window, int character); +@endcode + + +@subsection moving_cursorpos Cursor position changes + +The `glfwGetMousePos` function has been renamed to @ref glfwGetCursorPos, +`glfwSetMousePos` to @ref glfwSetCursorPos and `glfwSetMousePosCallback` to @ref +glfwSetCursorPosCallback. + +The cursor position is now `double` instead of `int`, both for the direct +functions and for the callback. Some platforms can provide sub-pixel cursor +movement and this data is now passed on to the application where available. On +platforms where this is not provided, the decimal part is zero. + +GLFW 3 only allows you to position the cursor within a window using @ref +glfwSetCursorPos (formerly `glfwSetMousePos`) when that window is active. +Unless the window is active, the function fails silently. + + +@subsection moving_wheel Wheel position replaced by scroll offsets + +The `glfwGetMouseWheel` function has been removed. Scrolling is the input of +offsets and has no absolute position. The mouse wheel callback has been +replaced by a [scroll callback](@ref GLFWscrollfun) that receives +two-dimensional floating point scroll offsets. This allows you to receive +precise scroll data from for example modern touchpads. + +@par Old syntax +@code +void GLFWCALL mouse_wheel_callback(int position); +@endcode + +@par New syntax +@code +void scroll_callback(GLFWwindow* window, double xoffset, double yoffset); +@endcode + +@par Removed functions +`glfwGetMouseWheel` + + +@subsection moving_repeat Key repeat action + +The `GLFW_KEY_REPEAT` enable has been removed and key repeat is always enabled +for both keys and characters. A new key action, `GLFW_REPEAT`, has been added +to allow the [key callback](@ref GLFWkeyfun) to distinguish an initial key press +from a repeat. Note that @ref glfwGetKey still returns only `GLFW_PRESS` or +`GLFW_RELEASE`. + + +@subsection moving_keys Physical key input + +GLFW 3 key tokens map to physical keys, unlike in GLFW 2 where they mapped to +the values generated by the current keyboard layout. The tokens are named +according to the values they would have using the standard US layout, but this +is only a convenience, as most programmers are assumed to know that layout. +This means that (for example) `GLFW_KEY_LEFT_BRACKET` is always a single key and +is the same key in the same place regardless of what keyboard layouts the users +of your program has. + +The key input facility was never meant for text input, although using it that +way worked slightly better in GLFW 2. If you were using it to input text, you +should be using the character callback instead, on both GLFW 2 and 3. This will +give you the characters being input, as opposed to the keys being pressed. + +GLFW 3 has key tokens for all keys on a standard 105 key keyboard, so instead of +having to remember whether to check for `'a'` or `'A'`, you now check for +`GLFW_KEY_A`. + + +@subsection moving_joystick Joystick function changes + +The `glfwGetJoystickPos` function has been renamed to @ref glfwGetJoystickAxes. + +The `glfwGetJoystickParam` function and the `GLFW_PRESENT`, `GLFW_AXES` and +`GLFW_BUTTONS` tokens have been replaced by the @ref glfwJoystickPresent +function as well as axis and button counts returned by the @ref +glfwGetJoystickAxes and @ref glfwGetJoystickButtons functions. + + +@subsection moving_mbcs Win32 MBCS support + +The Win32 port of GLFW 3 will not compile in +[MBCS mode](http://msdn.microsoft.com/en-us/library/5z097dxa.aspx). +However, because the use of the Unicode version of the Win32 API doesn't affect +the process as a whole, but only those windows created using it, it's perfectly +possible to call MBCS functions from other parts of the same application. +Therefore, even if an application using GLFW has MBCS mode code, there's no need +for GLFW itself to support it. + + +@subsection moving_windows Support for versions of Windows older than XP + +All explicit support for version of Windows older than XP has been removed. +There is no code that actively prevents GLFW 3 from running on these earlier +versions, but it uses Win32 functions that those versions lack. + +Windows XP was released in 2001, and by now (January 2015) it has not only +replaced almost all earlier versions of Windows, but is itself rapidly being +replaced by Windows 7 and 8. The MSDN library doesn't even provide +documentation for version older than Windows 2000, making it difficult to +maintain compatibility with these versions even if it was deemed worth the +effort. + +The Win32 API has also not stood still, and GLFW 3 uses many functions only +present on Windows XP or later. Even supporting an OS as new as XP (new +from the perspective of GLFW 2, which still supports Windows 95) requires +runtime checking for a number of functions that are present only on modern +version of Windows. + + +@subsection moving_syskeys Capture of system-wide hotkeys + +The ability to disable and capture system-wide hotkeys like Alt+Tab has been +removed. Modern applications, whether they're games, scientific visualisations +or something else, are nowadays expected to be good desktop citizens and allow +these hotkeys to function even when running in full screen mode. + + +@subsection moving_terminate Automatic termination + +GLFW 3 does not register @ref glfwTerminate with `atexit` at initialization, +because `exit` calls registered functions from the calling thread and while it +is permitted to call `exit` from any thread, @ref glfwTerminate must only be +called from the main thread. + +To release all resources allocated by GLFW, you should call @ref glfwTerminate +yourself, from the main thread, before the program terminates. Note that this +destroys all windows not already destroyed with @ref glfwDestroyWindow, +invalidating any window handles you may still have. + + +@subsection moving_glu GLU header inclusion + +GLFW 3 does not by default include the GLU header and GLU itself has been +deprecated by [Khronos](https://en.wikipedia.org/wiki/Khronos_Group). __New +projects should not use GLU__, but if you need it for legacy code that +has been moved to GLFW 3, you can request that the GLFW header includes it by +defining `GLFW_INCLUDE_GLU` before the inclusion of the GLFW header. + +@par Old syntax +@code +#include +@endcode + +@par New syntax +@code +#define GLFW_INCLUDE_GLU +#include +@endcode + + +@section moving_tables Name change tables + + +@subsection moving_renamed_functions Renamed functions + +| GLFW 2 | GLFW 3 | Notes | +| --------------------------- | ----------------------------- | ----- | +| `glfwOpenWindow` | @ref glfwCreateWindow | All channel bit depths are now hints +| `glfwCloseWindow` | @ref glfwDestroyWindow | | +| `glfwOpenWindowHint` | @ref glfwWindowHint | Now accepts all `GLFW_*_BITS` tokens | +| `glfwEnable` | @ref glfwSetInputMode | | +| `glfwDisable` | @ref glfwSetInputMode | | +| `glfwGetMousePos` | @ref glfwGetCursorPos | | +| `glfwSetMousePos` | @ref glfwSetCursorPos | | +| `glfwSetMousePosCallback` | @ref glfwSetCursorPosCallback | | +| `glfwSetMouseWheelCallback` | @ref glfwSetScrollCallback | Accepts two-dimensional scroll offsets as doubles | +| `glfwGetJoystickPos` | @ref glfwGetJoystickAxes | | +| `glfwGetWindowParam` | @ref glfwGetWindowAttrib | | +| `glfwGetGLVersion` | @ref glfwGetWindowAttrib | Use `GLFW_CONTEXT_VERSION_MAJOR`, `GLFW_CONTEXT_VERSION_MINOR` and `GLFW_CONTEXT_REVISION` | +| `glfwGetDesktopMode` | @ref glfwGetVideoMode | Returns the current mode of a monitor | +| `glfwGetJoystickParam` | @ref glfwJoystickPresent | The axis and button counts are provided by @ref glfwGetJoystickAxes and @ref glfwGetJoystickButtons | + + +@subsection moving_renamed_types Renamed types + +| GLFW 2 | GLFW 3 | Notes | +| ------------------- | --------------------- | | +| `GLFWmousewheelfun` | @ref GLFWscrollfun | | +| `GLFWmouseposfun` | @ref GLFWcursorposfun | | + + +@subsection moving_renamed_tokens Renamed tokens + +| GLFW 2 | GLFW 3 | Notes | +| --------------------------- | ---------------------------- | ----- | +| `GLFW_OPENGL_VERSION_MAJOR` | `GLFW_CONTEXT_VERSION_MAJOR` | Renamed as it applies to OpenGL ES as well | +| `GLFW_OPENGL_VERSION_MINOR` | `GLFW_CONTEXT_VERSION_MINOR` | Renamed as it applies to OpenGL ES as well | +| `GLFW_FSAA_SAMPLES` | `GLFW_SAMPLES` | Renamed to match the OpenGL API | +| `GLFW_ACTIVE` | `GLFW_FOCUSED` | Renamed to match the window focus callback | +| `GLFW_WINDOW_NO_RESIZE` | `GLFW_RESIZABLE` | The default has been inverted | +| `GLFW_MOUSE_CURSOR` | `GLFW_CURSOR` | Used with @ref glfwSetInputMode | +| `GLFW_KEY_ESC` | `GLFW_KEY_ESCAPE` | | +| `GLFW_KEY_DEL` | `GLFW_KEY_DELETE` | | +| `GLFW_KEY_PAGEUP` | `GLFW_KEY_PAGE_UP` | | +| `GLFW_KEY_PAGEDOWN` | `GLFW_KEY_PAGE_DOWN` | | +| `GLFW_KEY_KP_NUM_LOCK` | `GLFW_KEY_NUM_LOCK` | | +| `GLFW_KEY_LCTRL` | `GLFW_KEY_LEFT_CONTROL` | | +| `GLFW_KEY_LSHIFT` | `GLFW_KEY_LEFT_SHIFT` | | +| `GLFW_KEY_LALT` | `GLFW_KEY_LEFT_ALT` | | +| `GLFW_KEY_LSUPER` | `GLFW_KEY_LEFT_SUPER` | | +| `GLFW_KEY_RCTRL` | `GLFW_KEY_RIGHT_CONTROL` | | +| `GLFW_KEY_RSHIFT` | `GLFW_KEY_RIGHT_SHIFT` | | +| `GLFW_KEY_RALT` | `GLFW_KEY_RIGHT_ALT` | | +| `GLFW_KEY_RSUPER` | `GLFW_KEY_RIGHT_SUPER` | | + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/news.dox b/apps/exampleViewer/common/glfw/docs/news.dox new file mode 100644 index 0000000000..3301915775 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/news.dox @@ -0,0 +1,391 @@ +/*! + +@page news New features + +@section news_33 New features in 3.3 + +@subsection news_33_maximize Window maximization callback + +GLFW now supports window maximization notifications with @ref +glfwSetWindowMaximizeCallback. + + +@subsection news_33_keyscancode Platform-specific key scancode query + +GLFW now supports querying the platform dependent scancode of any key with +@ref glfwGetKeyScancode. + + +@section news_32 New features in 3.2 + + +@subsection news_32_vulkan Support for Vulkan + +GLFW now supports basic integration with Vulkan with @ref glfwVulkanSupported, +@ref glfwGetRequiredInstanceExtensions, @ref glfwGetInstanceProcAddress, @ref +glfwGetPhysicalDevicePresentationSupport and @ref glfwCreateWindowSurface. +Vulkan header inclusion can be selected with +[GLFW_INCLUDE_VULKAN](@ref build_macros). + + +@subsection news_32_setwindowmonitor Window mode switching + +GLFW now supports switching between windowed and full screen modes and updating +the monitor and desired resolution and refresh rate of full screen windows with +@ref glfwSetWindowMonitor. + + +@subsection news_32_maximize Window maxmimization support + +GLFW now supports window maximization with @ref glfwMaximizeWindow and the +[GLFW_MAXIMIZED](@ref window_attribs_wnd) window hint and attribute. + + +@subsection news_32_focus Window input focus control + +GLFW now supports giving windows input focus with @ref glfwFocusWindow. + + +@subsection news_32_sizelimits Window size limit support + +GLFW now supports setting both absolute and relative window size limits with +@ref glfwSetWindowSizeLimits and @ref glfwSetWindowAspectRatio. + + +@subsection news_32_keyname Localized key names + +GLFW now supports querying the localized name of printable keys with @ref +glfwGetKeyName, either by key token or by scancode. + + +@subsection news_32_waittimeout Wait for events with timeout + +GLFW now supports waiting for events for a set amount of time with @ref +glfwWaitEventsTimeout. + + +@subsection news_32_icon Window icon support + +GLFW now supports setting the icon of windows with @ref glfwSetWindowIcon. + + +@subsection news_32_timer Raw timer access + +GLFW now supports raw timer values with @ref glfwGetTimerValue and @ref +glfwGetTimerFrequency. + + +@subsection news_32_joystick Joystick connection callback + +GLFW now supports notifying when a joystick has been connected or disconnected +with @ref glfwSetJoystickCallback. + + +@subsection news_32_noapi Context-less windows + +GLFW now supports creating windows without a OpenGL or OpenGL ES context with +[GLFW_NO_API](@ref window_hints_ctx). + + +@subsection news_32_contextapi Run-time context creation API selection + +GLFW now supports selecting the context creation API at run-time with the +[GLFW_CONTEXT_CREATION_API](@ref window_hints_ctx) window hint value. + + +@subsection news_32_noerror Error-free context creation + +GLFW now supports creating OpenGL and OpenGL ES contexts that do not emit errors +with the [GLFW_CONTEXT_NO_ERROR](@ref window_hints_ctx) window hint, provided +the machine supports the `GL_KHR_no_error` extension. + + +@subsection news_32_cmake CMake config-file package support + +GLFW now supports being used as a +[config-file package](@ref build_link_cmake_package) from other projects for +easy linking with the library and its dependencies. + + +@section news_31 New features in 3.1 + +These are the release highlights. For a full list of changes see the +[version history](http://www.glfw.org/changelog.html). + + +@subsection news_31_cursor Custom mouse cursor images + +GLFW now supports creating and setting both custom cursor images and standard +cursor shapes. They are created with @ref glfwCreateCursor or @ref +glfwCreateStandardCursor, set with @ref glfwSetCursor and destroyed with @ref +glfwDestroyCursor. + +@see @ref cursor_object + + +@subsection news_31_drop Path drop event + +GLFW now provides a callback for receiving the paths of files and directories +dropped onto GLFW windows. The callback is set with @ref glfwSetDropCallback. + +@see @ref path_drop + + +@subsection news_31_emptyevent Main thread wake-up + +GLFW now provides the @ref glfwPostEmptyEvent function for posting an empty +event from another thread to the main thread event queue, causing @ref +glfwWaitEvents to return. + +@see @ref events + + +@subsection news_31_framesize Window frame size query + +GLFW now supports querying the size, on each side, of the frame around the +client area of a window, with @ref glfwGetWindowFrameSize. + +@see [Window size](@ref window_size) + + +@subsection news_31_autoiconify Simultaneous multi-monitor rendering + +GLFW now supports disabling auto-iconification of full screen windows with +the [GLFW_AUTO_ICONIFY](@ref window_hints_wnd) window hint. This is intended +for people building multi-monitor installations, where you need windows to stay +in full screen despite losing input focus. + + +@subsection news_31_floating Floating windows + +GLFW now supports floating windows, also called topmost or always on top, for +easier debugging with the [GLFW_FLOATING](@ref window_hints_wnd) window hint. + + +@subsection news_31_focused Initially unfocused windows + +GLFW now supports preventing a windowed mode window from gaining input focus on +creation, with the [GLFW_FOCUSED](@ref window_hints_wnd) window hint. + + +@subsection news_31_direct Direct access for window attributes and cursor position + +GLFW now queries the window input focus, visibility and iconification attributes +and the cursor position directly instead of returning cached data. + + +@subsection news_31_charmods Character with modifiers callback + +GLFW now provides a callback for character events with modifier key bits. The +callback is set with @ref glfwSetCharModsCallback. Unlike the regular character +callback, this will report character events that will not result in a character +being input, for example if the Control key is held down. + +@see @ref input_char + + +@subsection news_31_single Single buffered framebuffers + +GLFW now supports the creation of single buffered windows, with the +[GLFW_DOUBLEBUFFER](@ref window_hints_fb) window hint. + + +@subsection news_31_glext Macro for including extension header + +GLFW now includes the extension header appropriate for the chosen OpenGL or +OpenGL ES header when [GLFW_INCLUDE_GLEXT](@ref build_macros) is defined. GLFW +does not provide these headers. They must be provided by your development +environment or your OpenGL or OpenGL ES SDK. + + +@subsection news_31_release Context release behaviors + +GLFW now supports controlling whether the pipeline is flushed when a context is +made non-current, with the +[GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint, provided the +machine supports the `GL_KHR_context_flush_control` extension. + + +@subsection news_31_wayland (Experimental) Wayland support + +GLFW now has an _experimental_ Wayland display protocol backend that can be +selected on Linux with a CMake option. + + +@subsection news_31_mir (Experimental) Mir support + +GLFW now has an _experimental_ Mir display server backend that can be selected +on Linux with a CMake option. + + +@section news_30 New features in 3.0 + +These are the release highlights. For a full list of changes see the +[version history](http://www.glfw.org/changelog.html). + + +@subsection news_30_cmake CMake build system + +GLFW now uses the CMake build system instead of the various makefiles and +project files used by earlier versions. CMake is available for all platforms +supported by GLFW, is present in most package systems and can generate +makefiles and/or project files for most popular development environments. + +For more information on how to use CMake, see the +[CMake manual](http://cmake.org/cmake/help/documentation.html). + + +@subsection news_30_multiwnd Multi-window support + +GLFW now supports the creation of multiple windows, each with their own OpenGL +or OpenGL ES context, and all window functions now take a window handle. Event +callbacks are now per-window and are provided with the handle of the window that +received the event. The @ref glfwMakeContextCurrent function has been added to +select which context is current on a given thread. + + +@subsection news_30_multimon Multi-monitor support + +GLFW now explicitly supports multiple monitors. They can be enumerated with +@ref glfwGetMonitors, queried with @ref glfwGetVideoModes, @ref +glfwGetMonitorPos, @ref glfwGetMonitorName and @ref glfwGetMonitorPhysicalSize, +and specified at window creation to make the newly created window full screen on +that specific monitor. + + +@subsection news_30_unicode Unicode support + +All string arguments to GLFW functions and all strings returned by GLFW now use +the UTF-8 encoding. This includes the window title, error string, clipboard +text, monitor and joystick names as well as the extension function arguments (as +ASCII is a subset of UTF-8). + + +@subsection news_30_clipboard Clipboard text I/O + +GLFW now supports reading and writing plain text to and from the system +clipboard, with the @ref glfwGetClipboardString and @ref glfwSetClipboardString +functions. + + +@subsection news_30_gamma Gamma ramp support + +GLFW now supports setting and reading back the gamma ramp of monitors, with the +@ref glfwGetGammaRamp and @ref glfwSetGammaRamp functions. There is also @ref +glfwSetGamma, which generates a ramp from a gamma value and then sets it. + + +@subsection news_30_gles OpenGL ES support + +GLFW now supports the creation of OpenGL ES contexts, by setting the +`GLFW_CLIENT_API` window hint to `GLFW_OPENGL_ES_API`, where creation of such +contexts are supported. Note that GLFW _does not implement_ OpenGL ES, so your +driver must provide support in a way usable by GLFW. Modern Nvidia and Intel +drivers support creation of OpenGL ES context using the GLX and WGL APIs, while +AMD provides an EGL implementation instead. + + +@subsection news_30_egl (Experimental) EGL support + +GLFW now has an experimental EGL context creation back end that can be selected +through CMake options. + + +@subsection news_30_hidpi High-DPI support + +GLFW now supports high-DPI monitors on both Windows and macOS, giving windows +full resolution framebuffers where other UI elements are scaled up. To achieve +this, @ref glfwGetFramebufferSize and @ref glfwSetFramebufferSizeCallback have +been added. These work with pixels, while the rest of the GLFW API works with +screen coordinates. This is important as OpenGL uses pixels, not screen +coordinates. + + +@subsection news_30_error Error callback + +GLFW now has an error callback, which can provide your application with much +more detailed diagnostics than was previously possible. The callback is passed +an error code and a description string. + + +@subsection news_30_wndptr Per-window user pointer + +Each window now has a user-defined pointer, retrieved with @ref +glfwGetWindowUserPointer and set with @ref glfwSetWindowUserPointer, to make it +easier to integrate GLFW into C++ code. + + +@subsection news_30_iconifyfun Window iconification callback + +Each window now has a callback for iconification and restoration events, +which is set with @ref glfwSetWindowIconifyCallback. + + +@subsection news_30_wndposfun Window position callback + +Each window now has a callback for position events, which is set with @ref +glfwSetWindowPosCallback. + + +@subsection news_30_wndpos Window position query + +The position of a window can now be retrieved using @ref glfwGetWindowPos. + + +@subsection news_30_focusfun Window focus callback + +Each windows now has a callback for focus events, which is set with @ref +glfwSetWindowFocusCallback. + + +@subsection news_30_enterleave Cursor enter/leave callback + +Each window now has a callback for when the mouse cursor enters or leaves its +client area, which is set with @ref glfwSetCursorEnterCallback. + + +@subsection news_30_wndtitle Initial window title + +The title of a window is now specified at creation time, as one of the arguments +to @ref glfwCreateWindow. + + +@subsection news_30_hidden Hidden windows + +Windows can now be hidden with @ref glfwHideWindow, shown using @ref +glfwShowWindow and created initially hidden with the `GLFW_VISIBLE` window hint. +This allows for off-screen rendering in a way compatible with most drivers, as +well as moving a window to a specific position before showing it. + + +@subsection news_30_undecorated Undecorated windows + +Windowed mode windows can now be created without decorations, e.g. things like +a frame, a title bar, with the `GLFW_DECORATED` window hint. This allows for +the creation of things like splash screens. + + +@subsection news_30_keymods Modifier key bit masks + +[Modifier key bit mask](@ref mods) parameters have been added to the +[mouse button](@ref GLFWmousebuttonfun) and [key](@ref GLFWkeyfun) callbacks. + + +@subsection news_30_scancode Platform-specific scancodes + +A scancode parameter has been added to the [key callback](@ref GLFWkeyfun). Keys +that don't have a [key token](@ref keys) still get passed on with the key +parameter set to `GLFW_KEY_UNKNOWN`. These scancodes will vary between machines +and are intended to be used for key bindings. + + +@subsection news_30_jsname Joystick names + +The name of a joystick can now be retrieved using @ref glfwGetJoystickName. + + +@subsection news_30_doxygen Doxygen documentation + +You are reading it. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/quick.dox b/apps/exampleViewer/common/glfw/docs/quick.dox new file mode 100644 index 0000000000..dc1a2f8d92 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/quick.dox @@ -0,0 +1,364 @@ +/*! + +@page quick_guide Getting started + +@tableofcontents + +This guide takes you through writing a simple application using GLFW 3. The +application will create a window and OpenGL context, render a rotating triangle +and exit when the user closes the window or presses _Escape_. This guide will +introduce a few of the most commonly used functions, but there are many more. + +This guide assumes no experience with earlier versions of GLFW. If you +have used GLFW 2 in the past, read @ref moving_guide, as some functions +behave differently in GLFW 3. + + +@section quick_steps Step by step + +@subsection quick_include Including the GLFW header + +In the source files of your application where you use OpenGL or GLFW, you need +to include the GLFW 3 header file. + +@code +#include +@endcode + +This defines all the constants, types and function prototypes of the GLFW API. +It also includes the OpenGL header from your development environment and +defines all the constants and types necessary for it to work on your platform +without including any platform-specific headers. + +In other words: + +- Do _not_ include the OpenGL header yourself, as GLFW does this for you in + a platform-independent way +- Do _not_ include `windows.h` or other platform-specific headers unless + you plan on using those APIs yourself +- If you _do_ need to include such headers, include them _before_ the GLFW + header and it will detect this + +On some platforms supported by GLFW the OpenGL header and link library only +expose older versions of OpenGL. The most extreme case is Windows, which only +exposes OpenGL 1.2. The easiest way to work around this is to use an +[extension loader library](@ref context_glext_auto). + +If you are using such a library then you should include its header _before_ the +GLFW header. This lets it replace the OpenGL header included by GLFW without +conflicts. This example uses +[glad](https://github.com/Dav1dde/glad), but the same rule applies to all such +libraries. + +@code +#include +#include +@endcode + + +@subsection quick_init_term Initializing and terminating GLFW + +Before you can use most GLFW functions, the library must be initialized. On +successful initialization, `GLFW_TRUE` is returned. If an error occurred, +`GLFW_FALSE` is returned. + +@code +if (!glfwInit()) +{ + // Initialization failed +} +@endcode + +Note that `GLFW_TRUE` and `GLFW_FALSE` are and will always be just one and zero. + +When you are done using GLFW, typically just before the application exits, you +need to terminate GLFW. + +@code +glfwTerminate(); +@endcode + +This destroys any remaining windows and releases any other resources allocated by +GLFW. After this call, you must initialize GLFW again before using any GLFW +functions that require it. + + +@subsection quick_capture_error Setting an error callback + +Most events are reported through callbacks, whether it's a key being pressed, +a GLFW window being moved, or an error occurring. Callbacks are simply +C functions (or C++ static methods) that are called by GLFW with arguments +describing the event. + +In case a GLFW function fails, an error is reported to the GLFW error callback. +You can receive these reports with an error callback. This function must have +the signature below. This simple error callback just prints the error +description to `stderr`. + +@code +void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} +@endcode + +Callback functions must be set, so GLFW knows to call them. The function to set +the error callback is one of the few GLFW functions that may be called before +initialization, which lets you be notified of errors both during and after +initialization. + +@code +glfwSetErrorCallback(error_callback); +@endcode + + +@subsection quick_create_window Creating a window and context + +The window and its OpenGL context are created with a single call to @ref +glfwCreateWindow, which returns a handle to the created combined window and +context object + +@code +GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL); +if (!window) +{ + // Window or OpenGL context creation failed +} +@endcode + +This creates a 640 by 480 windowed mode window with an OpenGL context. If +window or OpenGL context creation fails, `NULL` will be returned. You should +always check the return value. While window creation rarely fails, context +creation depends on properly installed drivers and may fail even on machines +with the necessary hardware. + +By default, the OpenGL context GLFW creates may have any version. You can +require a minimum OpenGL version by setting the `GLFW_CONTEXT_VERSION_MAJOR` and +`GLFW_CONTEXT_VERSION_MINOR` hints _before_ creation. If the required minimum +version is not supported on the machine, context (and window) creation fails. + +@code +glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); +glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); +GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL); +if (!window) +{ + // Window or context creation failed +} +@endcode + +The window handle is passed to all window related functions and is provided to +along to all window related callbacks, so they can tell which window received +the event. + +When a window and context is no longer needed, destroy it. + +@code +glfwDestroyWindow(window); +@endcode + +Once this function is called, no more events will be delivered for that window +and its handle becomes invalid. + + +@subsection quick_context_current Making the OpenGL context current + +Before you can use the OpenGL API, you must have a current OpenGL context. + +@code +glfwMakeContextCurrent(window); +@endcode + +The context will remain current until you make another context current or until +the window owning the current context is destroyed. + +If you are using an [extension loader library](@ref context_glext_auto) to +access modern OpenGL then this is when to initialize it, as the loader needs +a current context to load from. This example uses +[glad](https://github.com/Dav1dde/glad), but the same rule applies to all such +libraries. + +@code +gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); +@endcode + + +@subsection quick_window_close Checking the window close flag + +Each window has a flag indicating whether the window should be closed. + +When the user attempts to close the window, either by pressing the close widget +in the title bar or using a key combination like Alt+F4, this flag is set to 1. +Note that __the window isn't actually closed__, so you are expected to monitor +this flag and either destroy the window or give some kind of feedback to the +user. + +@code +while (!glfwWindowShouldClose(window)) +{ + // Keep running +} +@endcode + +You can be notified when the user is attempting to close the window by setting +a close callback with @ref glfwSetWindowCloseCallback. The callback will be +called immediately after the close flag has been set. + +You can also set it yourself with @ref glfwSetWindowShouldClose. This can be +useful if you want to interpret other kinds of input as closing the window, like +for example pressing the _Escape_ key. + + +@subsection quick_key_input Receiving input events + +Each window has a large number of callbacks that can be set to receive all the +various kinds of events. To receive key press and release events, create a key +callback function. + +@code +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} +@endcode + +The key callback, like other window related callbacks, are set per-window. + +@code +glfwSetKeyCallback(window, key_callback); +@endcode + +In order for event callbacks to be called when events occur, you need to process +events as described below. + + +@subsection quick_render Rendering with OpenGL + +Once you have a current OpenGL context, you can use OpenGL normally. In this +tutorial, a multi-colored rotating triangle will be rendered. The framebuffer +size needs to be retrieved for `glViewport`. + +@code +int width, height; +glfwGetFramebufferSize(window, &width, &height); +glViewport(0, 0, width, height); +@endcode + +You can also set a framebuffer size callback using @ref +glfwSetFramebufferSizeCallback and be notified when the size changes. + +Actual rendering with OpenGL is outside the scope of this tutorial, but there +are [many](https://open.gl/) [excellent](http://learnopengl.com/) +[tutorial](http://openglbook.com/) [sites](http://ogldev.atspace.co.uk/) that +teach modern OpenGL. Some of them use GLFW to create the context and window +while others use GLUT or SDL, but remember that OpenGL itself always works the +same. + + +@subsection quick_timer Reading the timer + +To create smooth animation, a time source is needed. GLFW provides a timer that +returns the number of seconds since initialization. The time source used is the +most accurate on each platform and generally has micro- or nanosecond +resolution. + +@code +double time = glfwGetTime(); +@endcode + + +@subsection quick_swap_buffers Swapping buffers + +GLFW windows by default use double buffering. That means that each window has +two rendering buffers; a front buffer and a back buffer. The front buffer is +the one being displayed and the back buffer the one you render to. + +When the entire frame has been rendered, the buffers need to be swapped with one +another, so the back buffer becomes the front buffer and vice versa. + +@code +glfwSwapBuffers(window); +@endcode + +The swap interval indicates how many frames to wait until swapping the buffers, +commonly known as _vsync_. By default, the swap interval is zero, meaning +buffer swapping will occur immediately. On fast machines, many of those frames +will never be seen, as the screen is still only updated typically 60-75 times +per second, so this wastes a lot of CPU and GPU cycles. + +Also, because the buffers will be swapped in the middle the screen update, +leading to [screen tearing](https://en.wikipedia.org/wiki/Screen_tearing). + +For these reasons, applications will typically want to set the swap interval to +one. It can be set to higher values, but this is usually not recommended, +because of the input latency it leads to. + +@code +glfwSwapInterval(1); +@endcode + +This function acts on the current context and will fail unless a context is +current. + + +@subsection quick_process_events Processing events + +GLFW needs to communicate regularly with the window system both in order to +receive events and to show that the application hasn't locked up. Event +processing must be done regularly while you have visible windows and is normally +done each frame after buffer swapping. + +There are two methods for processing pending events; polling and waiting. This +example will use event polling, which processes only those events that have +already been received and then returns immediately. + +@code +glfwPollEvents(); +@endcode + +This is the best choice when rendering continually, like most games do. If +instead you only need to update your rendering once you have received new input, +@ref glfwWaitEvents is a better choice. It waits until at least one event has +been received, putting the thread to sleep in the meantime, and then processes +all received events. This saves a great deal of CPU cycles and is useful for, +for example, many kinds of editing tools. + + +@section quick_example Putting it together + +Now that you know how to initialize GLFW, create a window and poll for +keyboard input, it's possible to create a simple program. + +This program creates a 640 by 480 windowed mode window and starts a loop that +clears the screen, renders a triangle and processes events until the user either +presses _Escape_ or closes the window. + +@snippet simple.c code + +The program above can be found in the +[source package](http://www.glfw.org/download.html) as `examples/simple.c` +and is compiled along with all other examples when you build GLFW. If you +built GLFW from the source package then already have this as `simple.exe` on +Windows, `simple` on Linux or `simple.app` on macOS. + +This tutorial used only a few of the many functions GLFW provides. There are +guides for each of the areas covered by GLFW. Each guide will introduce all the +functions for that category. + + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref monitor_guide + - @ref input_guide + +You can access reference documentation for any GLFW function by clicking it and +the reference for each function links to related functions and guide sections. + +The tutorial ends here. Once you have written a program that uses GLFW, you +will need to compile and link it. How to do that depends on the development +environment you are using and is best explained by the documentation for that +environment. To learn about the details that are specific to GLFW, see +@ref build_guide. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/spaces.svg b/apps/exampleViewer/common/glfw/docs/spaces.svg new file mode 100644 index 0000000000..562fa8be13 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/spaces.svg @@ -0,0 +1,872 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/exampleViewer/common/glfw/docs/vulkan.dox b/apps/exampleViewer/common/glfw/docs/vulkan.dox new file mode 100644 index 0000000000..e704222ac3 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/vulkan.dox @@ -0,0 +1,211 @@ +/*! + +@page vulkan_guide Vulkan guide + +@tableofcontents + +This guide is intended to fill the gaps between the [Vulkan +documentation](https://www.khronos.org/vulkan/) and the rest of the GLFW +documentation and is not a replacement for either. It assumes some familiarity +with Vulkan concepts like loaders, devices, queues and surfaces and leaves it to +the Vulkan documentation to explain the details of Vulkan functions. + +To develop for Vulkan you should install an SDK for your platform, for example +the [LunarG Vulkan SDK](https://vulkan.lunarg.com/). Apart from the headers and +libraries, it also provides the validation layers necessary for development. + +The GLFW library does not need the Vulkan SDK to enable support for Vulkan. +However, any Vulkan-specific test and example programs are built only if the +CMake files find a Vulkan SDK. + +For details on a specific function in this category, see the @ref vulkan. There +are also guides for the other areas of the GLFW API. + + - @ref intro_guide + - @ref window_guide + - @ref context_guide + - @ref monitor_guide + - @ref input_guide + + +@section vulkan_include Including the Vulkan and GLFW header files + +To include the Vulkan header, define [GLFW_INCLUDE_VULKAN](@ref build_macros) +before including the GLFW header. + +@code +#define GLFW_INCLUDE_VULKAN +#include +@endcode + +If you instead want to include the Vulkan header from a custom location or use +your own custom Vulkan header then do this before the GLFW header. + +@code +#include +#include +@endcode + +Unless a Vulkan header is included, either by the GLFW header or above it, any +GLFW functions that take or return Vulkan types will not be declared. + +The `VK_USE_PLATFORM_*_KHR` macros do not need to be defined for the Vulkan part +of GLFW to work. Define them only if you are using these extensions directly. + + +@section vulkan_support Querying for Vulkan support + +If you are linking directly against the Vulkan loader then you can skip this +section. The canonical desktop loader library exports all Vulkan core and +Khronos extension functions, allowing them to be called directly. + +If you are loading the Vulkan loader dynamically instead of linking directly +against it, you can check for the availability of a loader with @ref +glfwVulkanSupported. + +@code +if (glfwVulkanSupported()) +{ + // Vulkan is available, at least for compute +} +@endcode + +This function returns `GLFW_TRUE` if the Vulkan loader was found. This check is +performed by @ref glfwInit. + +If no loader was found, calling any other Vulkan related GLFW function will +generate a @ref GLFW_API_UNAVAILABLE error. + + +@subsection vulkan_proc Querying Vulkan function pointers + +To load any Vulkan core or extension function from the found loader, call @ref +glfwGetInstanceProcAddress. To load functions needed for instance creation, +pass `NULL` as the instance. + +@code +PFN_vkCreateInstance pfnCreateInstance = (PFN_vkCreateInstance) + glfwGetInstanceProcAddress(NULL, "vkCreateInstance"); +@endcode + +Once you have created an instance, you can load from it all other Vulkan core +functions and functions from any instance extensions you enabled. + +@code +PFN_vkCreateDevice pfnCreateDevice = (PFN_vkCreateDevice) + glfwGetInstanceProcAddress(instance, "vkCreateDevice"); +@endcode + +This function in turn calls `vkGetInstanceProcAddr`. If that fails, the +function falls back to a platform-specific query of the Vulkan loader (i.e. +`dlsym` or `GetProcAddress`). If that also fails, the function returns `NULL`. +For more information about `vkGetInstanceProcAddr`, see the Vulkan +documentation. + +Vulkan also provides `vkGetDeviceProcAddr` for loading device-specific versions +of Vulkan function. This function can be retrieved from an instance with @ref +glfwGetInstanceProcAddress. + +@code +PFN_vkGetDeviceProcAddr pfnGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr) + glfwGetInstanceProcAddress(instance, "vkGetDeviceProcAddr"); +@endcode + +Device-specific functions may execute a little bit faster, due to not having to +dispatch internally based on the device passed to them. For more information +about `vkGetDeviceProcAddr`, see the Vulkan documentation. + + +@section vulkan_ext Querying required Vulkan extensions + +To do anything useful with Vulkan you need to create an instance. If you want +to use Vulkan to render to a window, you must enable the instance extensions +GLFW requires to create Vulkan surfaces. + +To query the instance extensions required, call @ref +glfwGetRequiredInstanceExtensions. + +@code +uint32_t count; +const char** extensions = glfwGetRequiredInstanceExtensions(&count); +@endcode + +These extensions must all be enabled when creating instances that are going to +be passed to @ref glfwGetPhysicalDevicePresentationSupport and @ref +glfwCreateWindowSurface. The set of extensions will vary depending on platform +and may also vary depending on graphics drivers and other factors. + +If it fails it will return `NULL` and GLFW will not be able to create Vulkan +window surfaces. You can still use Vulkan for off-screen rendering and compute +work. + +The returned array will always contain `VK_KHR_surface`, so if you don't +require any additional extensions you can pass this list directly to the +`VkInstanceCreateInfo` struct. + +@code +VkInstanceCreateInfo ici; + +memset(&ici, 0, sizeof(ici)); +ici.enabledExtensionCount = count; +ici.ppEnabledExtensionNames = extensions; +... +@endcode + +Additional extensions may be required by future versions of GLFW. You should +check whether any extensions you wish to enable are already in the returned +array, as it is an error to specify an extension more than once in the +`VkInstanceCreateInfo` struct. + + +@section vulkan_present Querying for Vulkan presentation support + +Not every queue family of every Vulkan device can present images to surfaces. +To check whether a specific queue family of a physical device supports image +presentation without first having to create a window and surface, call @ref +glfwGetPhysicalDevicePresentationSupport. + +@code +if (glfwGetPhysicalDevicePresentationSupport(instance, physical_device, queue_family_index)) +{ + // Queue family supports image presentation +} +@endcode + +The `VK_KHR_surface` extension additionally provides the +`vkGetPhysicalDeviceSurfaceSupportKHR` function, which performs the same test on +an existing Vulkan surface. + + +@section vulkan_window Creating the window + +Unless you will be using OpenGL or OpenGL ES with the same window as Vulkan, +there is no need to create a context. You can disable context creation with the +[GLFW_CLIENT_API](@ref window_hints_ctx) hint. + +@code +glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); +GLFWwindow* window = glfwCreateWindow(640, 480, "Window Title", NULL, NULL); +@endcode + +See @ref context_less for more information. + + +@section vulkan_surface Creating a Vulkan window surface + +You can create a Vulkan surface (as defined by the `VK_KHR_surface` extension) +for a GLFW window with @ref glfwCreateWindowSurface. + +@code +VkSurfaceKHR surface; +VkResult err = glfwCreateWindowSurface(instance, window, NULL, &surface); +if (err) +{ + // Window surface creation failed +} +@endcode + +It is your responsibility to destroy the surface. GLFW does not destroy it for +you. Call `vkDestroySurfaceKHR` function from the same extension to destroy it. + +*/ diff --git a/apps/exampleViewer/common/glfw/docs/window.dox b/apps/exampleViewer/common/glfw/docs/window.dox new file mode 100644 index 0000000000..d725106a32 --- /dev/null +++ b/apps/exampleViewer/common/glfw/docs/window.dox @@ -0,0 +1,1057 @@ +/*! + +@page window_guide Window guide + +@tableofcontents + +This guide introduces the window related functions of GLFW. For details on +a specific function in this category, see the @ref window. There are also +guides for the other areas of GLFW. + + - @ref intro_guide + - @ref context_guide + - @ref vulkan_guide + - @ref monitor_guide + - @ref input_guide + + +@section window_object Window objects + +The @ref GLFWwindow object encapsulates both a window and a context. They are +created with @ref glfwCreateWindow and destroyed with @ref glfwDestroyWindow, or +@ref glfwTerminate, if any remain. As the window and context are inseparably +linked, the object pointer is used as both a context and window handle. + +To see the event stream provided to the various window related callbacks, run +the `events` test program. + + +@subsection window_creation Window creation + +A window and its OpenGL or OpenGL ES context are created with @ref +glfwCreateWindow, which returns a handle to the created window object. For +example, this creates a 640 by 480 windowed mode window: + +@code +GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL); +@endcode + +If window creation fails, `NULL` will be returned, so it is necessary to check +the return value. + +The window handle is passed to all window related functions and is provided to +along with all input events, so event handlers can tell which window received +the event. + + +@subsubsection window_full_screen Full screen windows + +To create a full screen window, you need to specify which monitor the window +should use. In most cases, the user's primary monitor is a good choice. +For more information about retrieving monitors, see @ref monitor_monitors. + +@code +GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", glfwGetPrimaryMonitor(), NULL); +@endcode + +Full screen windows cover the entire display area of a monitor, have no border +or decorations. + +Windowed mode windows can be made full screen by setting a monitor with @ref +glfwSetWindowMonitor, and full screen ones can be made windowed by unsetting it +with the same function. + +Each field of the @ref GLFWvidmode structure corresponds to a function parameter +or window hint and combine to form the _desired video mode_ for that window. +The supported video mode most closely matching the desired video mode will be +set for the chosen monitor as long as the window has input focus. For more +information about retrieving video modes, see @ref monitor_modes. + +Video mode field | Corresponds to +----------------------- | ------------------------ +GLFWvidmode.width | `width` parameter +GLFWvidmode.height | `height` parameter +GLFWvidmode.redBits | `GLFW_RED_BITS` hint +GLFWvidmode.greenBits | `GLFW_GREEN_BITS` hint +GLFWvidmode.blueBits | `GLFW_BLUE_BITS` hint +GLFWvidmode.refreshRate | `GLFW_REFRESH_RATE` hint + +Once you have a full screen window, you can change its resolution, refresh rate +and monitor with @ref glfwSetWindowMonitor. If you just need change its +resolution you can also call @ref glfwSetWindowSize. In all cases, the new +video mode will be selected the same way as the video mode chosen by @ref +glfwCreateWindow. If the window has an OpenGL or OpenGL ES context, it will be +unaffected. + +By default, the original video mode of the monitor will be restored and the +window iconified if it loses input focus, to allow the user to switch back to +the desktop. This behavior can be disabled with the `GLFW_AUTO_ICONIFY` window +hint, for example if you wish to simultaneously cover multiple windows with full +screen windows. + + +@subsubsection window_windowed_full_screen "Windowed full screen" windows + +If the closest match for the desired video mode is the current one, the video +mode will not be changed, making window creation faster and application +switching much smoother. This is sometimes called _windowed full screen_ or +_borderless full screen_ window and counts as a full screen window. To create +such a window, simply request the current video mode. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); + +glfwWindowHint(GLFW_RED_BITS, mode->redBits); +glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); +glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); +glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + +GLFWwindow* window = glfwCreateWindow(mode->width, mode->height, "My Title", monitor, NULL); +@endcode + +This also works for windowed mode windows that are made full screen. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); + +glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); +@endcode + +Note that @ref glfwGetVideoMode returns the _current_ video mode of a monitor, +so if you already have a full screen window on that monitor that you want to +make windowed full screen, you need to have saved the desktop resolution before. + + +@subsection window_destruction Window destruction + +When a window is no longer needed, destroy it with @ref glfwDestroyWindow. + +@code +glfwDestroyWindow(window); +@endcode + +Window destruction always succeeds. Before the actual destruction, all +callbacks are removed so no further events will be delivered for the window. +All windows remaining when @ref glfwTerminate is called are destroyed as well. + +When a full screen window is destroyed, the original video mode of its monitor +is restored, but the gamma ramp is left untouched. + + +@subsection window_hints Window creation hints + +There are a number of hints that can be set before the creation of a window and +context. Some affect the window itself, others affect the framebuffer or +context. These hints are set to their default values each time the library is +initialized with @ref glfwInit, can be set individually with @ref glfwWindowHint +and reset all at once to their defaults with @ref glfwDefaultWindowHints. + +Note that hints need to be set _before_ the creation of the window and context +you wish to have the specified attributes. + + +@subsubsection window_hints_hard Hard and soft constraints + +Some window hints are hard constraints. These must match the available +capabilities _exactly_ for window and context creation to succeed. Hints +that are not hard constraints are matched as closely as possible, but the +resulting context and framebuffer may differ from what these hints requested. + +The following hints are always hard constraints: +- `GLFW_STEREO` +- `GLFW_DOUBLEBUFFER` +- `GLFW_CLIENT_API` +- `GLFW_CONTEXT_CREATION_API` + +The following additional hints are hard constraints when requesting an OpenGL +context, but are ignored when requesting an OpenGL ES context: +- `GLFW_OPENGL_FORWARD_COMPAT` +- `GLFW_OPENGL_PROFILE` + + +@subsubsection window_hints_wnd Window related hints + +`GLFW_RESIZABLE` specifies whether the windowed mode window will be resizable +_by the user_. The window will still be resizable using the @ref +glfwSetWindowSize function. This hint is ignored for full screen windows. + +`GLFW_VISIBLE` specifies whether the windowed mode window will be initially +visible. This hint is ignored for full screen windows. + +`GLFW_DECORATED` specifies whether the windowed mode window will have window +decorations such as a border, a close widget, etc. An undecorated window may +still allow the user to generate close events on some platforms. This hint is +ignored for full screen windows. + +`GLFW_FOCUSED` specifies whether the windowed mode window will be given input +focus when created. This hint is ignored for full screen and initially hidden +windows. + +`GLFW_AUTO_ICONIFY` specifies whether the full screen window will +automatically iconify and restore the previous video mode on input focus loss. +This hint is ignored for windowed mode windows. + +`GLFW_FLOATING` specifies whether the windowed mode window will be floating +above other regular windows, also called topmost or always-on-top. This is +intended primarily for debugging purposes and cannot be used to implement proper +full screen windows. This hint is ignored for full screen windows. + +`GLFW_MAXIMIZED` specifies whether the windowed mode window will be maximized +when created. This hint is ignored for full screen windows. + + +@subsubsection window_hints_fb Framebuffer related hints + +`GLFW_RED_BITS`, `GLFW_GREEN_BITS`, `GLFW_BLUE_BITS`, `GLFW_ALPHA_BITS`, +`GLFW_DEPTH_BITS` and `GLFW_STENCIL_BITS` specify the desired bit depths of the +various components of the default framebuffer. `GLFW_DONT_CARE` means the +application has no preference. + +`GLFW_ACCUM_RED_BITS`, `GLFW_ACCUM_GREEN_BITS`, `GLFW_ACCUM_BLUE_BITS` and +`GLFW_ACCUM_ALPHA_BITS` specify the desired bit depths of the various components +of the accumulation buffer. `GLFW_DONT_CARE` means the application has no +preference. + +@par +Accumulation buffers are a legacy OpenGL feature and should not be used in new +code. + +`GLFW_AUX_BUFFERS` specifies the desired number of auxiliary buffers. +`GLFW_DONT_CARE` means the application has no preference. + +@par +Auxiliary buffers are a legacy OpenGL feature and should not be used in new +code. + +`GLFW_STEREO` specifies whether to use stereoscopic rendering. This is a hard +constraint. + +`GLFW_SAMPLES` specifies the desired number of samples to use for multisampling. +Zero disables multisampling. `GLFW_DONT_CARE` means the application has no +preference. + +`GLFW_SRGB_CAPABLE` specifies whether the framebuffer should be sRGB capable. +If supported, a created OpenGL context will support the `GL_FRAMEBUFFER_SRGB` +enable, also called `GL_FRAMEBUFFER_SRGB_EXT`) for controlling sRGB rendering +and a created OpenGL ES context will always have sRGB rendering enabled. + +`GLFW_DOUBLEBUFFER` specifies whether the framebuffer should be double buffered. +You nearly always want to use double buffering. This is a hard constraint. + + +@subsubsection window_hints_mtr Monitor related hints + +`GLFW_REFRESH_RATE` specifies the desired refresh rate for full screen windows. +If set to `GLFW_DONT_CARE`, the highest available refresh rate will be used. +This hint is ignored for windowed mode windows. + + +@subsubsection window_hints_ctx Context related hints + +`GLFW_CLIENT_API` specifies which client API to create the context for. +Possible values are `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` and `GLFW_NO_API`. +This is a hard constraint. + +`GLFW_CONTEXT_CREATION_API` specifies which context creation API to use to +create the context. Possible values are `GLFW_NATIVE_CONTEXT_API` and +`GLFW_EGL_CONTEXT_API`. This is a hard constraint. If no client API is +requested, this hint is ignored. + +@par +@macos The EGL API is not available on this platform and requests to use it +will fail. + +@par +__Wayland, Mir:__ The EGL API _is_ the native context creation API, so this hint +will have no effect. + +@note An OpenGL extension loader library that assumes it knows which context +creation API is used on a given platform may fail if you change this hint. This +can be resolved by having it load via @ref glfwGetProcAddress, which always uses +the selected API. + +@bug On some Linux systems, creating contexts via both the native and EGL APIs +in a single process will cause the application to segfault. Stick to one API or +the other on Linux for now. + +`GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` specify the client +API version that the created context must be compatible with. The exact +behavior of these hints depend on the requested client API. + +@par +__OpenGL:__ `GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` are +not hard constraints, but creation will fail if the OpenGL version of the +created context is less than the one requested. It is therefore perfectly safe +to use the default of version 1.0 for legacy code and you will still get +backwards-compatible contexts of version 3.0 and above when available. + +@par +While there is no way to ask the driver for a context of the highest supported +version, GLFW will attempt to provide this when you ask for a version 1.0 +context, which is the default for these hints. + +@par +__OpenGL ES:__ `GLFW_CONTEXT_VERSION_MAJOR` and `GLFW_CONTEXT_VERSION_MINOR` are +not hard constraints, but creation will fail if the OpenGL ES version of the +created context is less than the one requested. Additionally, OpenGL ES 1.x +cannot be returned if 2.0 or later was requested, and vice versa. This is +because OpenGL ES 3.x is backward compatible with 2.0, but OpenGL ES 2.0 is not +backward compatible with 1.x. + +`GLFW_OPENGL_FORWARD_COMPAT` specifies whether the OpenGL context should be +forward-compatible, i.e. one where all functionality deprecated in the requested +version of OpenGL is removed. This must only be used if the requested OpenGL +version is 3.0 or above. If OpenGL ES is requested, this hint is ignored. + +@par +Forward-compatibility is described in detail in the +[OpenGL Reference Manual](https://www.opengl.org/registry/). + +`GLFW_OPENGL_DEBUG_CONTEXT` specifies whether to create a debug OpenGL context, +which may have additional error and performance issue reporting functionality. +If OpenGL ES is requested, this hint is ignored. + +`GLFW_OPENGL_PROFILE` specifies which OpenGL profile to create the context for. +Possible values are one of `GLFW_OPENGL_CORE_PROFILE` or +`GLFW_OPENGL_COMPAT_PROFILE`, or `GLFW_OPENGL_ANY_PROFILE` to not request +a specific profile. If requesting an OpenGL version below 3.2, +`GLFW_OPENGL_ANY_PROFILE` must be used. If OpenGL ES is requested, +this hint is ignored. + +@par +OpenGL profiles are described in detail in the +[OpenGL Reference Manual](https://www.opengl.org/registry/). + +`GLFW_CONTEXT_ROBUSTNESS` specifies the robustness strategy to be used by the +context. This can be one of `GLFW_NO_RESET_NOTIFICATION` or +`GLFW_LOSE_CONTEXT_ON_RESET`, or `GLFW_NO_ROBUSTNESS` to not request +a robustness strategy. + +`GLFW_CONTEXT_RELEASE_BEHAVIOR` specifies the release behavior to be +used by the context. Possible values are one of `GLFW_ANY_RELEASE_BEHAVIOR`, +`GLFW_RELEASE_BEHAVIOR_FLUSH` or `GLFW_RELEASE_BEHAVIOR_NONE`. If the +behavior is `GLFW_ANY_RELEASE_BEHAVIOR`, the default behavior of the context +creation API will be used. If the behavior is `GLFW_RELEASE_BEHAVIOR_FLUSH`, +the pipeline will be flushed whenever the context is released from being the +current one. If the behavior is `GLFW_RELEASE_BEHAVIOR_NONE`, the pipeline will +not be flushed on release. + +@par +Context release behaviors are described in detail by the +[GL_KHR_context_flush_control](https://www.opengl.org/registry/specs/KHR/context_flush_control.txt) +extension. + +`GLFW_CONTEXT_NO_ERROR` specifies whether errors should be generated by the +context. If enabled, situations that would have generated errors instead cause +undefined behavior. + +@par +The no error mode for OpenGL and OpenGL ES is described in detail by the +[GL_KHR_no_error](https://www.opengl.org/registry/specs/KHR/no_error.txt) +extension. + +@note This hint is experimental in its current state. There are currently +(October 2015) no corresponding WGL or GLX extensions. That makes this hint +a [hard constraint](@ref window_hints_hard) for those backends, as creation will +fail if unsupported context flags are requested. Once the extensions are +available, they will be required and creation of `GL_KHR_no_error` contexts may +fail on early drivers where this flag is supported without those extensions +being listed. + + +@subsubsection window_hints_values Supported and default values + +Window hint | Default value | Supported values +------------------------------- | --------------------------- | ---------------- +`GLFW_RESIZABLE` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_VISIBLE` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_DECORATED` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_FOCUSED` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_AUTO_ICONIFY` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_FLOATING` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_MAXIMIZED` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_RED_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_GREEN_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_BLUE_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_ALPHA_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_DEPTH_BITS` | 24 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_STENCIL_BITS` | 8 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_ACCUM_RED_BITS` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_ACCUM_GREEN_BITS` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_ACCUM_BLUE_BITS` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_ACCUM_ALPHA_BITS` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_AUX_BUFFERS` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_SAMPLES` | 0 | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_REFRESH_RATE` | `GLFW_DONT_CARE` | 0 to `INT_MAX` or `GLFW_DONT_CARE` +`GLFW_STEREO` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_SRGB_CAPABLE` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_DOUBLEBUFFER` | `GLFW_TRUE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_CLIENT_API` | `GLFW_OPENGL_API` | `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API` +`GLFW_CONTEXT_CREATION_API` | `GLFW_NATIVE_CONTEXT_API` | `GLFW_NATIVE_CONTEXT_API` or `GLFW_EGL_CONTEXT_API` +`GLFW_CONTEXT_VERSION_MAJOR` | 1 | Any valid major version number of the chosen client API +`GLFW_CONTEXT_VERSION_MINOR` | 0 | Any valid minor version number of the chosen client API +`GLFW_CONTEXT_ROBUSTNESS` | `GLFW_NO_ROBUSTNESS` | `GLFW_NO_ROBUSTNESS`, `GLFW_NO_RESET_NOTIFICATION` or `GLFW_LOSE_CONTEXT_ON_RESET` +`GLFW_CONTEXT_RELEASE_BEHAVIOR` | `GLFW_ANY_RELEASE_BEHAVIOR` | `GLFW_ANY_RELEASE_BEHAVIOR`, `GLFW_RELEASE_BEHAVIOR_FLUSH` or `GLFW_RELEASE_BEHAVIOR_NONE` +`GLFW_OPENGL_FORWARD_COMPAT` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_OPENGL_DEBUG_CONTEXT` | `GLFW_FALSE` | `GLFW_TRUE` or `GLFW_FALSE` +`GLFW_OPENGL_PROFILE` | `GLFW_OPENGL_ANY_PROFILE` | `GLFW_OPENGL_ANY_PROFILE`, `GLFW_OPENGL_COMPAT_PROFILE` or `GLFW_OPENGL_CORE_PROFILE` + + +@section window_events Window event processing + +See @ref events. + + +@section window_properties Window properties and events + +@subsection window_userptr User pointer + +Each window has a user pointer that can be set with @ref +glfwSetWindowUserPointer and fetched with @ref glfwGetWindowUserPointer. This +can be used for any purpose you need and will not be modified by GLFW throughout +the life-time of the window. + +The initial value of the pointer is `NULL`. + + +@subsection window_close Window closing and close flag + +When the user attempts to close the window, for example by clicking the close +widget or using a key chord like Alt+F4, the _close flag_ of the window is set. +The window is however not actually destroyed and, unless you watch for this +state change, nothing further happens. + +The current state of the close flag is returned by @ref glfwWindowShouldClose +and can be set or cleared directly with @ref glfwSetWindowShouldClose. A common +pattern is to use the close flag as a main loop condition. + +@code +while (!glfwWindowShouldClose(window)) +{ + render(window); + + glfwSwapBuffers(window); + glfwPollEvents(); +} +@endcode + +If you wish to be notified when the user attempts to close a window, set a close +callback. + +@code +glfwSetWindowCloseCallback(window, window_close_callback); +@endcode + +The callback function is called directly _after_ the close flag has been set. +It can be used for example to filter close requests and clear the close flag +again unless certain conditions are met. + +@code +void window_close_callback(GLFWwindow* window) +{ + if (!time_to_close) + glfwSetWindowShouldClose(window, GLFW_FALSE); +} +@endcode + + +@subsection window_size Window size + +The size of a window can be changed with @ref glfwSetWindowSize. For windowed +mode windows, this sets the size, in +[screen coordinates](@ref coordinate_systems) of the _client area_ or _content +area_ of the window. The window system may impose limits on window size. + +@code +glfwSetWindowSize(window, 640, 480); +@endcode + +For full screen windows, the specified size becomes the new resolution of the +window's desired video mode. The video mode most closely matching the new +desired video mode is set immediately. The window is resized to fit the +resolution of the set video mode. + +If you wish to be notified when a window is resized, whether by the user or +the system, set a size callback. + +@code +glfwSetWindowSizeCallback(window, window_size_callback); +@endcode + +The callback function receives the new size, in screen coordinates, of the +client area of the window when it is resized. + +@code +void window_size_callback(GLFWwindow* window, int width, int height) +{ +} +@endcode + +There is also @ref glfwGetWindowSize for directly retrieving the current size of +a window. + +@code +int width, height; +glfwGetWindowSize(window, &width, &height); +@endcode + +@note Do not pass the window size to `glViewport` or other pixel-based OpenGL +calls. The window size is in screen coordinates, not pixels. Use the +[framebuffer size](@ref window_fbsize), which is in pixels, for pixel-based +calls. + +The above functions work with the size of the client area, but decorated windows +typically have title bars and window frames around this rectangle. You can +retrieve the extents of these with @ref glfwGetWindowFrameSize. + +@code +int left, top, right, bottom; +glfwGetWindowFrameSize(window, &left, &top, &right, &bottom); +@endcode + +The returned values are the distances, in screen coordinates, from the edges of +the client area to the corresponding edges of the full window. As they are +distances and not coordinates, they are always zero or positive. + + +@subsection window_fbsize Framebuffer size + +While the size of a window is measured in screen coordinates, OpenGL works with +pixels. The size you pass into `glViewport`, for example, should be in pixels. +On some machines screen coordinates and pixels are the same, but on others they +will not be. There is a second set of functions to retrieve the size, in +pixels, of the framebuffer of a window. + +If you wish to be notified when the framebuffer of a window is resized, whether +by the user or the system, set a size callback. + +@code +glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); +@endcode + +The callback function receives the new size of the framebuffer when it is +resized, which can for example be used to update the OpenGL viewport. + +@code +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} +@endcode + +There is also @ref glfwGetFramebufferSize for directly retrieving the current +size of the framebuffer of a window. + +@code +int width, height; +glfwGetFramebufferSize(window, &width, &height); +glViewport(0, 0, width, height); +@endcode + +The size of a framebuffer may change independently of the size of a window, for +example if the window is dragged between a regular monitor and a high-DPI one. + + +@subsection window_sizelimits Window size limits + +The minimum and maximum size of the client area of a windowed mode window can be +enforced with @ref glfwSetWindowSizeLimits. The user may resize the window to +any size and aspect ratio within the specified limits, unless the aspect ratio +is also set. + +@code +glfwSetWindowSizeLimits(window, 200, 200, 400, 400); +@endcode + +To specify only a minimum size or only a maximum one, set the other pair to +`GLFW_DONT_CARE`. + +@code +glfwSetWindowSizeLimits(window, 640, 480, GLFW_DONT_CARE, GLFW_DONT_CARE); +@endcode + +To disable size limits for a window, set them all to `GLFW_DONT_CARE`. + +The aspect ratio of the client area of a windowed mode window can be enforced +with @ref glfwSetWindowAspectRatio. The user may resize the window freely +unless size limits are also set, but the size will be constrained to maintain +the aspect ratio. + +@code +glfwSetWindowAspectRatio(window, 16, 9); +@endcode + +The aspect ratio is specified as a numerator and denominator, corresponding to +the width and height, respectively. If you want a window to maintain its +current aspect ratio, simply use its current size as the ratio. + +@code +int width, height; +glfwGetWindowSize(window, &width, &height); +glfwSetWindowAspectRatio(window, width, height); +@endcode + +To disable the aspect ratio limit for a window, set both terms to +`GLFW_DONT_CARE`. + +You can have both size limits and aspect ratio set for a window, but the results +are undefined if they conflict. + + +@subsection window_pos Window position + +The position of a windowed-mode window can be changed with @ref +glfwSetWindowPos. This moves the window so that the upper-left corner of its +client area has the specified [screen coordinates](@ref coordinate_systems). +The window system may put limitations on window placement. + +@code +glfwSetWindowPos(window, 100, 100); +@endcode + +If you wish to be notified when a window is moved, whether by the user, system +or your own code, set a position callback. + +@code +glfwSetWindowPosCallback(window, window_pos_callback); +@endcode + +The callback function receives the new position of the upper-left corner of the +client area when the window is moved. + +@code +void window_pos_callback(GLFWwindow* window, int xpos, int ypos) +{ +} +@endcode + +There is also @ref glfwGetWindowPos for directly retrieving the current position +of the client area of the window. + +@code +int xpos, ypos; +glfwGetWindowPos(window, &xpos, &ypos); +@endcode + + +@subsection window_title Window title + +All GLFW windows have a title, although undecorated or full screen windows may +not display it or only display it in a task bar or similar interface. You can +set a UTF-8 encoded window title with @ref glfwSetWindowTitle. + +@code +glfwSetWindowTitle(window, "My Window"); +@endcode + +The specified string is copied before the function returns, so there is no need +to keep it around. + +As long as your source file is encoded as UTF-8, you can use any Unicode +characters directly in the source. + +@code +glfwSetWindowTitle(window, "ラストエグザイル"); +@endcode + +If you are using C++11 or C11, you can use a UTF-8 string literal. + +@code +glfwSetWindowTitle(window, u8"This is always a UTF-8 string"); +@endcode + + +@subsection window_icon Window icon + +Decorated windows have icons on some platforms. You can set this icon by +specifying a list of candidate images with @ref glfwSetWindowIcon. + +@code +GLFWimage images[2]; +images[0] = load_icon("my_icon.png"); +images[1] = load_icon("my_icon_small.png"); + +glfwSetWindowIcon(window, 2, images); +@endcode + +To revert to the default window icon, pass in an empty image array. + +@code +glfwSetWindowIcon(window, 0, NULL); +@endcode + + +@subsection window_monitor Window monitor + +Full screen windows are associated with a specific monitor. You can get the +handle for this monitor with @ref glfwGetWindowMonitor. + +@code +GLFWmonitor* monitor = glfwGetWindowMonitor(window); +@endcode + +This monitor handle is one of those returned by @ref glfwGetMonitors. + +For windowed mode windows, this function returns `NULL`. This is how to tell +full screen windows from windowed mode windows. + +You can move windows between monitors or between full screen and windowed mode +with @ref glfwSetWindowMonitor. When making a window full screen on the same or +on a different monitor, specify the desired monitor, resolution and refresh +rate. The position arguments are ignored. + +@code +const GLFWvidmode* mode = glfwGetVideoMode(monitor); + +glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); +@endcode + +When making the window windowed, specify the desired position and size. The +refresh rate argument is ignored. + +@code +glfwSetWindowMonitor(window, NULL, xpos, ypos, width, height, 0); +@endcode + +This restores any previous window settings such as whether it is decorated, +floating, resizable, has size or aspect ratio limits, etc.. To restore a window +that was originally windowed to its original size and position, save these +before making it full screen and then pass them in as above. + + +@subsection window_iconify Window iconification + +Windows can be iconified (i.e. minimized) with @ref glfwIconifyWindow. + +@code +glfwIconifyWindow(window); +@endcode + +When a full screen window is iconified, the original video mode of its monitor +is restored until the user or application restores the window. + +Iconified windows can be restored with @ref glfwRestoreWindow. This function +also restores windows from maximization. + +@code +glfwRestoreWindow(window); +@endcode + +When a full screen window is restored, the desired video mode is restored to its +monitor as well. + +If you wish to be notified when a window is iconified or restored, whether by +the user, system or your own code, set a iconify callback. + +@code +glfwSetWindowIconifyCallback(window, window_iconify_callback); +@endcode + +The callback function receives changes in the iconification state of the window. + +@code +void window_iconify_callback(GLFWwindow* window, int iconified) +{ + if (iconified) + { + // The window was iconified + } + else + { + // The window was restored + } +} +@endcode + +You can also get the current iconification state with @ref glfwGetWindowAttrib. + +@code +int iconified = glfwGetWindowAttrib(window, GLFW_ICONIFIED); +@endcode + + +@subsection window_maximize Window maximization + +Windows can be maximized (i.e. zoomed) with @ref glfwMaximizeWindow. + +@code +glfwMaximizeWindow(window); +@endcode + +Full screen windows cannot be maximized and passing a full screen window to this +function does nothing. + +Maximized windows can be restored with @ref glfwRestoreWindow. This function +also restores windows from iconification. + +@code +glfwRestoreWindow(window); +@endcode + +If you wish to be notified when a window is maximized or restored, whether by +the user, system or your own code, set a maximize callback. + +@code +glfwSetWindowMaximizeCallback(window, window_maximize_callback); +@endcode + +The callback function receives changes in the maximization state of the window. + +@code +void window_maximize_callback(GLFWwindow* window, int maximized) +{ + if (maximized) + { + // The window was maximized + } + else + { + // The window was restored + } +} +@endcode + +You can also get the current maximization state with @ref glfwGetWindowAttrib. + +@code +int maximized = glfwGetWindowAttrib(window, GLFW_MAXIMIZED); +@endcode + + +@subsection window_hide Window visibility + +Windowed mode windows can be hidden with @ref glfwHideWindow. + +@code +glfwHideWindow(window); +@endcode + +This makes the window completely invisible to the user, including removing it +from the task bar, dock or window list. Full screen windows cannot be hidden +and calling @ref glfwHideWindow on a full screen window does nothing. + +Hidden windows can be shown with @ref glfwShowWindow. + +@code +glfwShowWindow(window); +@endcode + +Windowed mode windows can be created initially hidden with the `GLFW_VISIBLE` +[window hint](@ref window_hints_wnd). Windows created hidden are completely +invisible to the user until shown. This can be useful if you need to set up +your window further before showing it, for example moving it to a specific +location. + +You can also get the current visibility state with @ref glfwGetWindowAttrib. + +@code +int visible = glfwGetWindowAttrib(window, GLFW_VISIBLE); +@endcode + + +@subsection window_focus Window input focus + +Windows can be given input focus and brought to the front with @ref +glfwFocusWindow. + +@code +glfwFocusWindow(window); +@endcode + +If you wish to be notified when a window gains or loses input focus, whether by +the user, system or your own code, set a focus callback. + +@code +glfwSetWindowFocusCallback(window, window_focus_callback); +@endcode + +The callback function receives changes in the input focus state of the window. + +@code +void window_focus_callback(GLFWwindow* window, int focused) +{ + if (focused) + { + // The window gained input focus + } + else + { + // The window lost input focus + } +} +@endcode + +You can also get the current input focus state with @ref glfwGetWindowAttrib. + +@code +int focused = glfwGetWindowAttrib(window, GLFW_FOCUSED); +@endcode + + +@subsection window_refresh Window damage and refresh + +If you wish to be notified when the contents of a window is damaged and needs +to be refreshed, set a window refresh callback. + +@code +glfwSetWindowRefreshCallback(m_handle, window_refresh_callback); +@endcode + +The callback function is called when the contents of the window needs to be +refreshed. + +@code +void window_refresh_callback(GLFWwindow* window) +{ + draw_editor_ui(window); + glfwSwapBuffers(window); +} +@endcode + +@note On compositing window systems such as Aero, Compiz or Aqua, where the +window contents are saved off-screen, this callback might only be called when +the window or framebuffer is resized. + + +@subsection window_attribs Window attributes + +Windows have a number of attributes that can be returned using @ref +glfwGetWindowAttrib. Some reflect state that may change during the lifetime of +the window, while others reflect the corresponding hints and are fixed at the +time of creation. Some are related to the actual window and others to its +context. + +@code +if (glfwGetWindowAttrib(window, GLFW_FOCUSED)) +{ + // window has input focus +} +@endcode + + +@subsubsection window_attribs_wnd Window related attributes + +`GLFW_FOCUSED` indicates whether the specified window has input focus. Initial +input focus is controlled by the [window hint](@ref window_hints_wnd) with the +same name. + +`GLFW_ICONIFIED` indicates whether the specified window is iconified, whether by +the user or with @ref glfwIconifyWindow. + +`GLFW_MAXIMIZED` indicates whether the specified window is maximized, whether by +the user or with @ref glfwMaximizeWindow. + +`GLFW_VISIBLE` indicates whether the specified window is visible. Window +visibility can be controlled with @ref glfwShowWindow and @ref glfwHideWindow +and initial visibility is controlled by the [window hint](@ref window_hints_wnd) +with the same name. + +`GLFW_RESIZABLE` indicates whether the specified window is resizable _by the +user_. This is set on creation with the [window hint](@ref window_hints_wnd) + with the same name. + +`GLFW_DECORATED` indicates whether the specified window has decorations such as +a border, a close widget, etc. This is set on creation with the +[window hint](@ref window_hints_wnd) with the same name. + +`GLFW_FLOATING` indicates whether the specified window is floating, also called +topmost or always-on-top. This is controlled by the +[window hint](@ref window_hints_wnd) with the same name. + + +@subsubsection window_attribs_ctx Context related attributes + +`GLFW_CLIENT_API` indicates the client API provided by the window's context; +either `GLFW_OPENGL_API`, `GLFW_OPENGL_ES_API` or `GLFW_NO_API`. + +`GLFW_CONTEXT_CREATION_API` indicates the context creation API used to create +the window's context; either `GLFW_NATIVE_CONTEXT_API` or +`GLFW_EGL_CONTEXT_API`. + +`GLFW_CONTEXT_VERSION_MAJOR`, `GLFW_CONTEXT_VERSION_MINOR` and +`GLFW_CONTEXT_REVISION` indicate the client API version of the window's context. + +`GLFW_OPENGL_FORWARD_COMPAT` is `GLFW_TRUE` if the window's context is an OpenGL +forward-compatible one, or `GLFW_FALSE` otherwise. + +`GLFW_OPENGL_DEBUG_CONTEXT` is `GLFW_TRUE` if the window's context is an OpenGL +debug context, or `GLFW_FALSE` otherwise. + +`GLFW_OPENGL_PROFILE` indicates the OpenGL profile used by the context. This is +`GLFW_OPENGL_CORE_PROFILE` or `GLFW_OPENGL_COMPAT_PROFILE` if the context uses +a known profile, or `GLFW_OPENGL_ANY_PROFILE` if the OpenGL profile is unknown +or the context is an OpenGL ES context. Note that the returned profile may not +match the profile bits of the context flags, as GLFW will try other means of +detecting the profile when no bits are set. + +`GLFW_CONTEXT_ROBUSTNESS` indicates the robustness strategy used by the context. +This is `GLFW_LOSE_CONTEXT_ON_RESET` or `GLFW_NO_RESET_NOTIFICATION` if the +window's context supports robustness, or `GLFW_NO_ROBUSTNESS` otherwise. + + +@subsubsection window_attribs_fb Framebuffer related attributes + +GLFW does not expose attributes of the default framebuffer (i.e. the framebuffer +attached to the window) as these can be queried directly with either OpenGL, +OpenGL ES or Vulkan. + +If you are using version 3.0 or later of OpenGL or OpenGL ES, the +`glGetFramebufferAttachmentParameteriv` function can be used to retrieve the +number of bits for the red, green, blue, alpha, depth and stencil buffer +channels. Otherwise, the `glGetIntegerv` function can be used. + +The number of MSAA samples are always retrieved with `glGetIntegerv`. For +contexts supporting framebuffer objects, the number of samples of the currently +bound framebuffer is returned. + +Attribute | glGetIntegerv | glGetFramebufferAttachmentParameteriv +------------ | ----------------- | ------------------------------------- +Red bits | `GL_RED_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE` +Green bits | `GL_GREEN_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE` +Blue bits | `GL_BLUE_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE` +Alpha bits | `GL_ALPHA_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE` +Depth bits | `GL_DEPTH_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE` +Stencil bits | `GL_STENCIL_BITS` | `GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE` +MSAA samples | `GL_SAMPLES` | _Not provided by this function_ + +When calling `glGetFramebufferAttachmentParameteriv`, the red, green, blue and +alpha sizes are queried from the `GL_BACK_LEFT`, while the depth and stencil +sizes are queried from the `GL_DEPTH` and `GL_STENCIL` attachments, +respectively. + + +@section buffer_swap Buffer swapping + +GLFW windows are by default double buffered. That means that you have two +rendering buffers; a front buffer and a back buffer. The front buffer is +the one being displayed and the back buffer the one you render to. + +When the entire frame has been rendered, it is time to swap the back and the +front buffers in order to display what has been rendered and begin rendering +a new frame. This is done with @ref glfwSwapBuffers. + +@code +glfwSwapBuffers(window); +@endcode + +Sometimes it can be useful to select when the buffer swap will occur. With the +function @ref glfwSwapInterval it is possible to select the minimum number of +monitor refreshes the driver wait should from the time @ref glfwSwapBuffers was +called before swapping the buffers: + +@code +glfwSwapInterval(1); +@endcode + +If the interval is zero, the swap will take place immediately when @ref +glfwSwapBuffers is called without waiting for a refresh. Otherwise at least +interval retraces will pass between each buffer swap. Using a swap interval of +zero can be useful for benchmarking purposes, when it is not desirable to +measure the time it takes to wait for the vertical retrace. However, a swap +interval of one lets you avoid tearing. + +Note that this may not work on all machines, as some drivers have +user-controlled settings that override any swap interval the application +requests. + +*/ diff --git a/apps/exampleViewer/common/glfw/examples/CMakeLists.txt b/apps/exampleViewer/common/glfw/examples/CMakeLists.txt new file mode 100644 index 0000000000..138a781667 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/CMakeLists.txt @@ -0,0 +1,67 @@ + +link_libraries(glfw) + +include_directories(${glfw_INCLUDE_DIRS}) + +if (BUILD_SHARED_LIBS) + link_libraries("${MATH_LIBRARY}") +endif() + +if (MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories("${GLFW_SOURCE_DIR}/deps") + +if (WIN32) + set(ICON glfw.rc) +elseif (APPLE) + set(ICON glfw.icns) + set_source_files_properties(glfw.icns PROPERTIES + MAXOSX_PACKAGE_LOCATION "Resources") +endif() + +set(GLAD "${GLFW_SOURCE_DIR}/deps/glad/glad.h" + "${GLFW_SOURCE_DIR}/deps/glad.c") +set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h" + "${GLFW_SOURCE_DIR}/deps/getopt.c") +set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" + "${GLFW_SOURCE_DIR}/deps/tinycthread.c") + +add_executable(boing WIN32 MACOSX_BUNDLE boing.c ${ICON} ${GLAD}) +add_executable(gears WIN32 MACOSX_BUNDLE gears.c ${ICON} ${GLAD}) +add_executable(heightmap WIN32 MACOSX_BUNDLE heightmap.c ${ICON} ${GLAD}) +add_executable(particles WIN32 MACOSX_BUNDLE particles.c ${ICON} ${TINYCTHREAD} ${GETOPT} ${GLAD}) +add_executable(simple WIN32 MACOSX_BUNDLE simple.c ${ICON} ${GLAD}) +add_executable(splitview WIN32 MACOSX_BUNDLE splitview.c ${ICON} ${GLAD}) +add_executable(wave WIN32 MACOSX_BUNDLE wave.c ${ICON} ${GLAD}) + +target_link_libraries(particles "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") + +set(WINDOWS_BINARIES boing gears heightmap particles simple splitview wave) + +set_target_properties(${WINDOWS_BINARIES} PROPERTIES FOLDER "GLFW3/Examples") + +if (MSVC) + # Tell MSVC to use main instead of WinMain for Windows subsystem executables + set_target_properties(${WINDOWS_BINARIES} PROPERTIES + LINK_FLAGS "/ENTRY:mainCRTStartup") +endif() + +if (APPLE) + set_target_properties(boing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Boing") + set_target_properties(gears PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gears") + set_target_properties(heightmap PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Heightmap") + set_target_properties(particles PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Particles") + set_target_properties(simple PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Simple") + set_target_properties(splitview PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "SplitView") + set_target_properties(wave PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Wave") + + set_target_properties(${WINDOWS_BINARIES} PROPERTIES + RESOURCE glfw.icns + MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION} + MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION_FULL} + MACOSX_BUNDLE_ICON_FILE glfw.icns + MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in") +endif() + diff --git a/apps/exampleViewer/common/glfw/examples/boing.c b/apps/exampleViewer/common/glfw/examples/boing.c new file mode 100644 index 0000000000..45c867fd1b --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/boing.c @@ -0,0 +1,678 @@ +/***************************************************************************** + * Title: GLBoing + * Desc: Tribute to Amiga Boing. + * Author: Jim Brooks + * Original Amiga authors were R.J. Mical and Dale Luck. + * GLFW conversion by Marcus Geelnard + * Notes: - 360' = 2*PI [radian] + * + * - Distances between objects are created by doing a relative + * Z translations. + * + * - Although OpenGL enticingly supports alpha-blending, + * the shadow of the original Boing didn't affect the color + * of the grid. + * + * - [Marcus] Changed timing scheme from interval driven to frame- + * time based animation steps (which results in much smoother + * movement) + * + * History of Amiga Boing: + * + * Boing was demonstrated on the prototype Amiga (codenamed "Lorraine") in + * 1985. According to legend, it was written ad-hoc in one night by + * R. J. Mical and Dale Luck. Because the bouncing ball animation was so fast + * and smooth, attendees did not believe the Amiga prototype was really doing + * the rendering. Suspecting a trick, they began looking around the booth for + * a hidden computer or VCR. + *****************************************************************************/ + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include + +#include +#include + +#include + + +/***************************************************************************** + * Various declarations and macros + *****************************************************************************/ + +/* Prototypes */ +void init( void ); +void display( void ); +void reshape( GLFWwindow* window, int w, int h ); +void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ); +void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ); +void cursor_position_callback( GLFWwindow* window, double x, double y ); +void DrawBoingBall( void ); +void BounceBall( double dt ); +void DrawBoingBallBand( GLfloat long_lo, GLfloat long_hi ); +void DrawGrid( void ); + +#define RADIUS 70.f +#define STEP_LONGITUDE 22.5f /* 22.5 makes 8 bands like original Boing */ +#define STEP_LATITUDE 22.5f + +#define DIST_BALL (RADIUS * 2.f + RADIUS * 0.1f) + +#define VIEW_SCENE_DIST (DIST_BALL * 3.f + 200.f)/* distance from viewer to middle of boing area */ +#define GRID_SIZE (RADIUS * 4.5f) /* length (width) of grid */ +#define BOUNCE_HEIGHT (RADIUS * 2.1f) +#define BOUNCE_WIDTH (RADIUS * 2.1f) + +#define SHADOW_OFFSET_X -20.f +#define SHADOW_OFFSET_Y 10.f +#define SHADOW_OFFSET_Z 0.f + +#define WALL_L_OFFSET 0.f +#define WALL_R_OFFSET 5.f + +/* Animation speed (50.0 mimics the original GLUT demo speed) */ +#define ANIMATION_SPEED 50.f + +/* Maximum allowed delta time per physics iteration */ +#define MAX_DELTA_T 0.02f + +/* Draw ball, or its shadow */ +typedef enum { DRAW_BALL, DRAW_BALL_SHADOW } DRAW_BALL_ENUM; + +/* Vertex type */ +typedef struct {float x; float y; float z;} vertex_t; + +/* Global vars */ +int windowed_xpos, windowed_ypos, windowed_width, windowed_height; +int width, height; +GLfloat deg_rot_y = 0.f; +GLfloat deg_rot_y_inc = 2.f; +int override_pos = GLFW_FALSE; +GLfloat cursor_x = 0.f; +GLfloat cursor_y = 0.f; +GLfloat ball_x = -RADIUS; +GLfloat ball_y = -RADIUS; +GLfloat ball_x_inc = 1.f; +GLfloat ball_y_inc = 2.f; +DRAW_BALL_ENUM drawBallHow; +double t; +double t_old = 0.f; +double dt; + +/* Random number generator */ +#ifndef RAND_MAX + #define RAND_MAX 4095 +#endif + + +/***************************************************************************** + * Truncate a degree. + *****************************************************************************/ +GLfloat TruncateDeg( GLfloat deg ) +{ + if ( deg >= 360.f ) + return (deg - 360.f); + else + return deg; +} + +/***************************************************************************** + * Convert a degree (360-based) into a radian. + * 360' = 2 * PI + *****************************************************************************/ +double deg2rad( double deg ) +{ + return deg / 360 * (2 * M_PI); +} + +/***************************************************************************** + * 360' sin(). + *****************************************************************************/ +double sin_deg( double deg ) +{ + return sin( deg2rad( deg ) ); +} + +/***************************************************************************** + * 360' cos(). + *****************************************************************************/ +double cos_deg( double deg ) +{ + return cos( deg2rad( deg ) ); +} + +/***************************************************************************** + * Compute a cross product (for a normal vector). + * + * c = a x b + *****************************************************************************/ +void CrossProduct( vertex_t a, vertex_t b, vertex_t c, vertex_t *n ) +{ + GLfloat u1, u2, u3; + GLfloat v1, v2, v3; + + u1 = b.x - a.x; + u2 = b.y - a.y; + u3 = b.y - a.z; + + v1 = c.x - a.x; + v2 = c.y - a.y; + v3 = c.z - a.z; + + n->x = u2 * v3 - v2 * v3; + n->y = u3 * v1 - v3 * u1; + n->z = u1 * v2 - v1 * u2; +} + + +#define BOING_DEBUG 0 + + +/***************************************************************************** + * init() + *****************************************************************************/ +void init( void ) +{ + /* + * Clear background. + */ + glClearColor( 0.55f, 0.55f, 0.55f, 0.f ); + + glShadeModel( GL_FLAT ); +} + + +/***************************************************************************** + * display() + *****************************************************************************/ +void display(void) +{ + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + glPushMatrix(); + + drawBallHow = DRAW_BALL_SHADOW; + DrawBoingBall(); + + DrawGrid(); + + drawBallHow = DRAW_BALL; + DrawBoingBall(); + + glPopMatrix(); + glFlush(); +} + + +/***************************************************************************** + * reshape() + *****************************************************************************/ +void reshape( GLFWwindow* window, int w, int h ) +{ + mat4x4 projection, view; + + glViewport( 0, 0, (GLsizei)w, (GLsizei)h ); + + glMatrixMode( GL_PROJECTION ); + mat4x4_perspective( projection, + 2.f * (float) atan2( RADIUS, 200.f ), + (float)w / (float)h, + 1.f, VIEW_SCENE_DIST ); + glLoadMatrixf((const GLfloat*) projection); + + glMatrixMode( GL_MODELVIEW ); + { + vec3 eye = { 0.f, 0.f, VIEW_SCENE_DIST }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, -1.f, 0.f }; + mat4x4_look_at( view, eye, center, up ); + } + glLoadMatrixf((const GLfloat*) view); +} + +void key_callback( GLFWwindow* window, int key, int scancode, int action, int mods ) +{ + if (action != GLFW_PRESS) + return; + + if (key == GLFW_KEY_ESCAPE && mods == 0) + glfwSetWindowShouldClose(window, GLFW_TRUE); + if ((key == GLFW_KEY_ENTER && mods == GLFW_MOD_ALT) || + (key == GLFW_KEY_F11 && mods == GLFW_MOD_ALT)) + { + if (glfwGetWindowMonitor(window)) + { + glfwSetWindowMonitor(window, NULL, + windowed_xpos, windowed_ypos, + windowed_width, windowed_height, 0); + } + else + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos); + glfwGetWindowSize(window, &windowed_width, &windowed_height); + glfwSetWindowMonitor(window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); + } + } + } +} + +static void set_ball_pos ( GLfloat x, GLfloat y ) +{ + ball_x = (width / 2) - x; + ball_y = y - (height / 2); +} + +void mouse_button_callback( GLFWwindow* window, int button, int action, int mods ) +{ + if (button != GLFW_MOUSE_BUTTON_LEFT) + return; + + if (action == GLFW_PRESS) + { + override_pos = GLFW_TRUE; + set_ball_pos(cursor_x, cursor_y); + } + else + { + override_pos = GLFW_FALSE; + } +} + +void cursor_position_callback( GLFWwindow* window, double x, double y ) +{ + cursor_x = (float) x; + cursor_y = (float) y; + + if ( override_pos ) + set_ball_pos(cursor_x, cursor_y); +} + +/***************************************************************************** + * Draw the Boing ball. + * + * The Boing ball is sphere in which each facet is a rectangle. + * Facet colors alternate between red and white. + * The ball is built by stacking latitudinal circles. Each circle is composed + * of a widely-separated set of points, so that each facet is noticably large. + *****************************************************************************/ +void DrawBoingBall( void ) +{ + GLfloat lon_deg; /* degree of longitude */ + double dt_total, dt2; + + glPushMatrix(); + glMatrixMode( GL_MODELVIEW ); + + /* + * Another relative Z translation to separate objects. + */ + glTranslatef( 0.0, 0.0, DIST_BALL ); + + /* Update ball position and rotation (iterate if necessary) */ + dt_total = dt; + while( dt_total > 0.0 ) + { + dt2 = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total; + dt_total -= dt2; + BounceBall( dt2 ); + deg_rot_y = TruncateDeg( deg_rot_y + deg_rot_y_inc*((float)dt2*ANIMATION_SPEED) ); + } + + /* Set ball position */ + glTranslatef( ball_x, ball_y, 0.0 ); + + /* + * Offset the shadow. + */ + if ( drawBallHow == DRAW_BALL_SHADOW ) + { + glTranslatef( SHADOW_OFFSET_X, + SHADOW_OFFSET_Y, + SHADOW_OFFSET_Z ); + } + + /* + * Tilt the ball. + */ + glRotatef( -20.0, 0.0, 0.0, 1.0 ); + + /* + * Continually rotate ball around Y axis. + */ + glRotatef( deg_rot_y, 0.0, 1.0, 0.0 ); + + /* + * Set OpenGL state for Boing ball. + */ + glCullFace( GL_FRONT ); + glEnable( GL_CULL_FACE ); + glEnable( GL_NORMALIZE ); + + /* + * Build a faceted latitude slice of the Boing ball, + * stepping same-sized vertical bands of the sphere. + */ + for ( lon_deg = 0; + lon_deg < 180; + lon_deg += STEP_LONGITUDE ) + { + /* + * Draw a latitude circle at this longitude. + */ + DrawBoingBallBand( lon_deg, + lon_deg + STEP_LONGITUDE ); + } + + glPopMatrix(); + + return; +} + + +/***************************************************************************** + * Bounce the ball. + *****************************************************************************/ +void BounceBall( double delta_t ) +{ + GLfloat sign; + GLfloat deg; + + if ( override_pos ) + return; + + /* Bounce on walls */ + if ( ball_x > (BOUNCE_WIDTH/2 + WALL_R_OFFSET ) ) + { + ball_x_inc = -0.5f - 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX; + deg_rot_y_inc = -deg_rot_y_inc; + } + if ( ball_x < -(BOUNCE_HEIGHT/2 + WALL_L_OFFSET) ) + { + ball_x_inc = 0.5f + 0.75f * (GLfloat)rand() / (GLfloat)RAND_MAX; + deg_rot_y_inc = -deg_rot_y_inc; + } + + /* Bounce on floor / roof */ + if ( ball_y > BOUNCE_HEIGHT/2 ) + { + ball_y_inc = -0.75f - 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX; + } + if ( ball_y < -BOUNCE_HEIGHT/2*0.85 ) + { + ball_y_inc = 0.75f + 1.f * (GLfloat)rand() / (GLfloat)RAND_MAX; + } + + /* Update ball position */ + ball_x += ball_x_inc * ((float)delta_t*ANIMATION_SPEED); + ball_y += ball_y_inc * ((float)delta_t*ANIMATION_SPEED); + + /* + * Simulate the effects of gravity on Y movement. + */ + if ( ball_y_inc < 0 ) sign = -1.0; else sign = 1.0; + + deg = (ball_y + BOUNCE_HEIGHT/2) * 90 / BOUNCE_HEIGHT; + if ( deg > 80 ) deg = 80; + if ( deg < 10 ) deg = 10; + + ball_y_inc = sign * 4.f * (float) sin_deg( deg ); +} + + +/***************************************************************************** + * Draw a faceted latitude band of the Boing ball. + * + * Parms: long_lo, long_hi + * Low and high longitudes of slice, resp. + *****************************************************************************/ +void DrawBoingBallBand( GLfloat long_lo, + GLfloat long_hi ) +{ + vertex_t vert_ne; /* "ne" means south-east, so on */ + vertex_t vert_nw; + vertex_t vert_sw; + vertex_t vert_se; + vertex_t vert_norm; + GLfloat lat_deg; + static int colorToggle = 0; + + /* + * Iterate thru the points of a latitude circle. + * A latitude circle is a 2D set of X,Z points. + */ + for ( lat_deg = 0; + lat_deg <= (360 - STEP_LATITUDE); + lat_deg += STEP_LATITUDE ) + { + /* + * Color this polygon with red or white. + */ + if ( colorToggle ) + glColor3f( 0.8f, 0.1f, 0.1f ); + else + glColor3f( 0.95f, 0.95f, 0.95f ); +#if 0 + if ( lat_deg >= 180 ) + if ( colorToggle ) + glColor3f( 0.1f, 0.8f, 0.1f ); + else + glColor3f( 0.5f, 0.5f, 0.95f ); +#endif + colorToggle = ! colorToggle; + + /* + * Change color if drawing shadow. + */ + if ( drawBallHow == DRAW_BALL_SHADOW ) + glColor3f( 0.35f, 0.35f, 0.35f ); + + /* + * Assign each Y. + */ + vert_ne.y = vert_nw.y = (float) cos_deg(long_hi) * RADIUS; + vert_sw.y = vert_se.y = (float) cos_deg(long_lo) * RADIUS; + + /* + * Assign each X,Z with sin,cos values scaled by latitude radius indexed by longitude. + * Eg, long=0 and long=180 are at the poles, so zero scale is sin(longitude), + * while long=90 (sin(90)=1) is at equator. + */ + vert_ne.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_se.x = (float) cos_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo )); + vert_nw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_sw.x = (float) cos_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo )); + + vert_ne.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_se.z = (float) sin_deg( lat_deg ) * (RADIUS * (float) sin_deg( long_lo )); + vert_nw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo + STEP_LONGITUDE )); + vert_sw.z = (float) sin_deg( lat_deg + STEP_LATITUDE ) * (RADIUS * (float) sin_deg( long_lo )); + + /* + * Draw the facet. + */ + glBegin( GL_POLYGON ); + + CrossProduct( vert_ne, vert_nw, vert_sw, &vert_norm ); + glNormal3f( vert_norm.x, vert_norm.y, vert_norm.z ); + + glVertex3f( vert_ne.x, vert_ne.y, vert_ne.z ); + glVertex3f( vert_nw.x, vert_nw.y, vert_nw.z ); + glVertex3f( vert_sw.x, vert_sw.y, vert_sw.z ); + glVertex3f( vert_se.x, vert_se.y, vert_se.z ); + + glEnd(); + +#if BOING_DEBUG + printf( "----------------------------------------------------------- \n" ); + printf( "lat = %f long_lo = %f long_hi = %f \n", lat_deg, long_lo, long_hi ); + printf( "vert_ne x = %.8f y = %.8f z = %.8f \n", vert_ne.x, vert_ne.y, vert_ne.z ); + printf( "vert_nw x = %.8f y = %.8f z = %.8f \n", vert_nw.x, vert_nw.y, vert_nw.z ); + printf( "vert_se x = %.8f y = %.8f z = %.8f \n", vert_se.x, vert_se.y, vert_se.z ); + printf( "vert_sw x = %.8f y = %.8f z = %.8f \n", vert_sw.x, vert_sw.y, vert_sw.z ); +#endif + + } + + /* + * Toggle color so that next band will opposite red/white colors than this one. + */ + colorToggle = ! colorToggle; + + /* + * This circular band is done. + */ + return; +} + + +/***************************************************************************** + * Draw the purple grid of lines, behind the Boing ball. + * When the Workbench is dropped to the bottom, Boing shows 12 rows. + *****************************************************************************/ +void DrawGrid( void ) +{ + int row, col; + const int rowTotal = 12; /* must be divisible by 2 */ + const int colTotal = rowTotal; /* must be same as rowTotal */ + const GLfloat widthLine = 2.0; /* should be divisible by 2 */ + const GLfloat sizeCell = GRID_SIZE / rowTotal; + const GLfloat z_offset = -40.0; + GLfloat xl, xr; + GLfloat yt, yb; + + glPushMatrix(); + glDisable( GL_CULL_FACE ); + + /* + * Another relative Z translation to separate objects. + */ + glTranslatef( 0.0, 0.0, DIST_BALL ); + + /* + * Draw vertical lines (as skinny 3D rectangles). + */ + for ( col = 0; col <= colTotal; col++ ) + { + /* + * Compute co-ords of line. + */ + xl = -GRID_SIZE / 2 + col * sizeCell; + xr = xl + widthLine; + + yt = GRID_SIZE / 2; + yb = -GRID_SIZE / 2 - widthLine; + + glBegin( GL_POLYGON ); + + glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */ + + glVertex3f( xr, yt, z_offset ); /* NE */ + glVertex3f( xl, yt, z_offset ); /* NW */ + glVertex3f( xl, yb, z_offset ); /* SW */ + glVertex3f( xr, yb, z_offset ); /* SE */ + + glEnd(); + } + + /* + * Draw horizontal lines (as skinny 3D rectangles). + */ + for ( row = 0; row <= rowTotal; row++ ) + { + /* + * Compute co-ords of line. + */ + yt = GRID_SIZE / 2 - row * sizeCell; + yb = yt - widthLine; + + xl = -GRID_SIZE / 2; + xr = GRID_SIZE / 2 + widthLine; + + glBegin( GL_POLYGON ); + + glColor3f( 0.6f, 0.1f, 0.6f ); /* purple */ + + glVertex3f( xr, yt, z_offset ); /* NE */ + glVertex3f( xl, yt, z_offset ); /* NW */ + glVertex3f( xl, yb, z_offset ); /* SW */ + glVertex3f( xr, yb, z_offset ); /* SE */ + + glEnd(); + } + + glPopMatrix(); + + return; +} + + +/*======================================================================* + * main() + *======================================================================*/ + +int main( void ) +{ + GLFWwindow* window; + + /* Init GLFW */ + if( !glfwInit() ) + exit( EXIT_FAILURE ); + + window = glfwCreateWindow( 400, 400, "Boing (classic Amiga demo)", NULL, NULL ); + if (!window) + { + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + glfwSetWindowAspectRatio(window, 1, 1); + + glfwSetFramebufferSizeCallback(window, reshape); + glfwSetKeyCallback(window, key_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, cursor_position_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval( 1 ); + + glfwGetFramebufferSize(window, &width, &height); + reshape(window, width, height); + + glfwSetTime( 0.0 ); + + init(); + + /* Main loop */ + for (;;) + { + /* Timing */ + t = glfwGetTime(); + dt = t - t_old; + t_old = t; + + /* Draw one frame */ + display(); + + /* Swap buffers */ + glfwSwapBuffers(window); + glfwPollEvents(); + + /* Check if we are still running */ + if (glfwWindowShouldClose(window)) + break; + } + + glfwTerminate(); + exit( EXIT_SUCCESS ); +} + diff --git a/apps/exampleViewer/common/glfw/examples/gears.c b/apps/exampleViewer/common/glfw/examples/gears.c new file mode 100644 index 0000000000..29e63f58b2 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/gears.c @@ -0,0 +1,357 @@ +/* + * 3-D gear wheels. This program is in the public domain. + * + * Command line options: + * -info print GL implementation information + * -exit automatically exit after 30 seconds + * + * + * Brian Paul + * + * + * Marcus Geelnard: + * - Conversion to GLFW + * - Time based rendering (frame rate independent) + * - Slightly modified camera that should work better for stereo viewing + * + * + * Camilla Berglund: + * - Removed FPS counter (this is not a benchmark) + * - Added a few comments + * - Enabled vsync + */ + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include +#include + +#include +#include + +/** + + Draw a gear wheel. You'll probably want to call this function when + building a display list since we do a lot of trig here. + + Input: inner_radius - radius of hole at center + outer_radius - radius at center of teeth + width - width of gear teeth - number of teeth + tooth_depth - depth of tooth + + **/ + +static void +gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width, + GLint teeth, GLfloat tooth_depth) +{ + GLint i; + GLfloat r0, r1, r2; + GLfloat angle, da; + GLfloat u, v, len; + + r0 = inner_radius; + r1 = outer_radius - tooth_depth / 2.f; + r2 = outer_radius + tooth_depth / 2.f; + + da = 2.f * (float) M_PI / teeth / 4.f; + + glShadeModel(GL_FLAT); + + glNormal3f(0.f, 0.f, 1.f); + + /* draw front face */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + if (i < teeth) { + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + } + } + glEnd(); + + /* draw front sides of teeth */ + glBegin(GL_QUADS); + da = 2.f * (float) M_PI / teeth / 4.f; + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + } + glEnd(); + + glNormal3f(0.0, 0.0, -1.0); + + /* draw back face */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + if (i < teeth) { + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + } + } + glEnd(); + + /* draw back sides of teeth */ + glBegin(GL_QUADS); + da = 2.f * (float) M_PI / teeth / 4.f; + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + } + glEnd(); + + /* draw outward faces of teeth */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i < teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), width * 0.5f); + glVertex3f(r1 * (float) cos(angle), r1 * (float) sin(angle), -width * 0.5f); + u = r2 * (float) cos(angle + da) - r1 * (float) cos(angle); + v = r2 * (float) sin(angle + da) - r1 * (float) sin(angle); + len = (float) sqrt(u * u + v * v); + u /= len; + v /= len; + glNormal3f(v, -u, 0.0); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + da), r2 * (float) sin(angle + da), -width * 0.5f); + glNormal3f((float) cos(angle), (float) sin(angle), 0.f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), width * 0.5f); + glVertex3f(r2 * (float) cos(angle + 2 * da), r2 * (float) sin(angle + 2 * da), -width * 0.5f); + u = r1 * (float) cos(angle + 3 * da) - r2 * (float) cos(angle + 2 * da); + v = r1 * (float) sin(angle + 3 * da) - r2 * (float) sin(angle + 2 * da); + glNormal3f(v, -u, 0.f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), width * 0.5f); + glVertex3f(r1 * (float) cos(angle + 3 * da), r1 * (float) sin(angle + 3 * da), -width * 0.5f); + glNormal3f((float) cos(angle), (float) sin(angle), 0.f); + } + + glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), width * 0.5f); + glVertex3f(r1 * (float) cos(0), r1 * (float) sin(0), -width * 0.5f); + + glEnd(); + + glShadeModel(GL_SMOOTH); + + /* draw inside radius cylinder */ + glBegin(GL_QUAD_STRIP); + for (i = 0; i <= teeth; i++) { + angle = i * 2.f * (float) M_PI / teeth; + glNormal3f(-(float) cos(angle), -(float) sin(angle), 0.f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), -width * 0.5f); + glVertex3f(r0 * (float) cos(angle), r0 * (float) sin(angle), width * 0.5f); + } + glEnd(); + +} + + +static GLfloat view_rotx = 20.f, view_roty = 30.f, view_rotz = 0.f; +static GLint gear1, gear2, gear3; +static GLfloat angle = 0.f; + +/* OpenGL draw function & timing */ +static void draw(void) +{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glPushMatrix(); + glRotatef(view_rotx, 1.0, 0.0, 0.0); + glRotatef(view_roty, 0.0, 1.0, 0.0); + glRotatef(view_rotz, 0.0, 0.0, 1.0); + + glPushMatrix(); + glTranslatef(-3.0, -2.0, 0.0); + glRotatef(angle, 0.0, 0.0, 1.0); + glCallList(gear1); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(3.1f, -2.f, 0.f); + glRotatef(-2.f * angle - 9.f, 0.f, 0.f, 1.f); + glCallList(gear2); + glPopMatrix(); + + glPushMatrix(); + glTranslatef(-3.1f, 4.2f, 0.f); + glRotatef(-2.f * angle - 25.f, 0.f, 0.f, 1.f); + glCallList(gear3); + glPopMatrix(); + + glPopMatrix(); +} + + +/* update animation parameters */ +static void animate(void) +{ + angle = 100.f * (float) glfwGetTime(); +} + + +/* change view angle, exit upon ESC */ +void key( GLFWwindow* window, int k, int s, int action, int mods ) +{ + if( action != GLFW_PRESS ) return; + + switch (k) { + case GLFW_KEY_Z: + if( mods & GLFW_MOD_SHIFT ) + view_rotz -= 5.0; + else + view_rotz += 5.0; + break; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_UP: + view_rotx += 5.0; + break; + case GLFW_KEY_DOWN: + view_rotx -= 5.0; + break; + case GLFW_KEY_LEFT: + view_roty += 5.0; + break; + case GLFW_KEY_RIGHT: + view_roty -= 5.0; + break; + default: + return; + } +} + + +/* new window size */ +void reshape( GLFWwindow* window, int width, int height ) +{ + GLfloat h = (GLfloat) height / (GLfloat) width; + GLfloat xmax, znear, zfar; + + znear = 5.0f; + zfar = 30.0f; + xmax = znear * 0.5f; + + glViewport( 0, 0, (GLint) width, (GLint) height ); + glMatrixMode( GL_PROJECTION ); + glLoadIdentity(); + glFrustum( -xmax, xmax, -xmax*h, xmax*h, znear, zfar ); + glMatrixMode( GL_MODELVIEW ); + glLoadIdentity(); + glTranslatef( 0.0, 0.0, -20.0 ); +} + + +/* program & OpenGL initialization */ +static void init(void) +{ + static GLfloat pos[4] = {5.f, 5.f, 10.f, 0.f}; + static GLfloat red[4] = {0.8f, 0.1f, 0.f, 1.f}; + static GLfloat green[4] = {0.f, 0.8f, 0.2f, 1.f}; + static GLfloat blue[4] = {0.2f, 0.2f, 1.f, 1.f}; + + glLightfv(GL_LIGHT0, GL_POSITION, pos); + glEnable(GL_CULL_FACE); + glEnable(GL_LIGHTING); + glEnable(GL_LIGHT0); + glEnable(GL_DEPTH_TEST); + + /* make the gears */ + gear1 = glGenLists(1); + glNewList(gear1, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, red); + gear(1.f, 4.f, 1.f, 20, 0.7f); + glEndList(); + + gear2 = glGenLists(1); + glNewList(gear2, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, green); + gear(0.5f, 2.f, 2.f, 10, 0.7f); + glEndList(); + + gear3 = glGenLists(1); + glNewList(gear3, GL_COMPILE); + glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, blue); + gear(1.3f, 2.f, 0.5f, 10, 0.7f); + glEndList(); + + glEnable(GL_NORMALIZE); +} + + +/* program entry */ +int main(int argc, char *argv[]) +{ + GLFWwindow* window; + int width, height; + + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + exit( EXIT_FAILURE ); + } + + glfwWindowHint(GLFW_DEPTH_BITS, 16); + + window = glfwCreateWindow( 300, 300, "Gears", NULL, NULL ); + if (!window) + { + fprintf( stderr, "Failed to open GLFW window\n" ); + glfwTerminate(); + exit( EXIT_FAILURE ); + } + + // Set callback functions + glfwSetFramebufferSizeCallback(window, reshape); + glfwSetKeyCallback(window, key); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval( 1 ); + + glfwGetFramebufferSize(window, &width, &height); + reshape(window, width, height); + + // Parse command-line options + init(); + + // Main loop + while( !glfwWindowShouldClose(window) ) + { + // Draw gears + draw(); + + // Update animation + animate(); + + // Swap buffers + glfwSwapBuffers(window); + glfwPollEvents(); + } + + // Terminate GLFW + glfwTerminate(); + + // Exit program + exit( EXIT_SUCCESS ); +} + diff --git a/apps/exampleViewer/common/glfw/examples/glfw.icns b/apps/exampleViewer/common/glfw/examples/glfw.icns new file mode 100644 index 0000000000..ad98f39752 Binary files /dev/null and b/apps/exampleViewer/common/glfw/examples/glfw.icns differ diff --git a/apps/exampleViewer/common/glfw/examples/glfw.ico b/apps/exampleViewer/common/glfw/examples/glfw.ico new file mode 100644 index 0000000000..882a66051e Binary files /dev/null and b/apps/exampleViewer/common/glfw/examples/glfw.ico differ diff --git a/apps/exampleViewer/common/glfw/examples/glfw.rc b/apps/exampleViewer/common/glfw/examples/glfw.rc new file mode 100644 index 0000000000..ab88e4ea43 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/glfw.rc @@ -0,0 +1,3 @@ + +GLFW_ICON ICON "glfw.ico" + diff --git a/apps/exampleViewer/common/glfw/examples/heightmap.c b/apps/exampleViewer/common/glfw/examples/heightmap.c new file mode 100644 index 0000000000..b57815eb02 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/heightmap.c @@ -0,0 +1,511 @@ +//======================================================================== +// Heightmap example program using OpenGL 3 core profile +// Copyright (c) 2010 Olivier Delannoy +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include +#include +#include +#include +#include + +#include +#include + +/* Map height updates */ +#define MAX_CIRCLE_SIZE (5.0f) +#define MAX_DISPLACEMENT (1.0f) +#define DISPLACEMENT_SIGN_LIMIT (0.3f) +#define MAX_ITER (200) +#define NUM_ITER_AT_A_TIME (1) + +/* Map general information */ +#define MAP_SIZE (10.0f) +#define MAP_NUM_VERTICES (80) +#define MAP_NUM_TOTAL_VERTICES (MAP_NUM_VERTICES*MAP_NUM_VERTICES) +#define MAP_NUM_LINES (3* (MAP_NUM_VERTICES - 1) * (MAP_NUM_VERTICES - 1) + \ + 2 * (MAP_NUM_VERTICES - 1)) + + +/********************************************************************** + * Default shader programs + *********************************************************************/ + +static const char* vertex_shader_text = +"#version 150\n" +"uniform mat4 project;\n" +"uniform mat4 modelview;\n" +"in float x;\n" +"in float y;\n" +"in float z;\n" +"\n" +"void main()\n" +"{\n" +" gl_Position = project * modelview * vec4(x, y, z, 1.0);\n" +"}\n"; + +static const char* fragment_shader_text = +"#version 150\n" +"out vec4 color;\n" +"void main()\n" +"{\n" +" color = vec4(0.2, 1.0, 0.2, 1.0); \n" +"}\n"; + +/********************************************************************** + * Values for shader uniforms + *********************************************************************/ + +/* Frustum configuration */ +static GLfloat view_angle = 45.0f; +static GLfloat aspect_ratio = 4.0f/3.0f; +static GLfloat z_near = 1.0f; +static GLfloat z_far = 100.f; + +/* Projection matrix */ +static GLfloat projection_matrix[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f +}; + +/* Model view matrix */ +static GLfloat modelview_matrix[16] = { + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f +}; + +/********************************************************************** + * Heightmap vertex and index data + *********************************************************************/ + +static GLfloat map_vertices[3][MAP_NUM_TOTAL_VERTICES]; +static GLuint map_line_indices[2*MAP_NUM_LINES]; + +/* Store uniform location for the shaders + * Those values are setup as part of the process of creating + * the shader program. They should not be used before creating + * the program. + */ +static GLuint mesh; +static GLuint mesh_vbo[4]; + +/********************************************************************** + * OpenGL helper functions + *********************************************************************/ + +/* Creates a shader object of the specified type using the specified text + */ +static GLuint make_shader(GLenum type, const char* text) +{ + GLuint shader; + GLint shader_ok; + GLsizei log_length; + char info_log[8192]; + + shader = glCreateShader(type); + if (shader != 0) + { + glShaderSource(shader, 1, (const GLchar**)&text, NULL); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_ok); + if (shader_ok != GL_TRUE) + { + fprintf(stderr, "ERROR: Failed to compile %s shader\n", (type == GL_FRAGMENT_SHADER) ? "fragment" : "vertex" ); + glGetShaderInfoLog(shader, 8192, &log_length,info_log); + fprintf(stderr, "ERROR: \n%s\n\n", info_log); + glDeleteShader(shader); + shader = 0; + } + } + return shader; +} + +/* Creates a program object using the specified vertex and fragment text + */ +static GLuint make_shader_program(const char* vs_text, const char* fs_text) +{ + GLuint program = 0u; + GLint program_ok; + GLuint vertex_shader = 0u; + GLuint fragment_shader = 0u; + GLsizei log_length; + char info_log[8192]; + + vertex_shader = make_shader(GL_VERTEX_SHADER, vs_text); + if (vertex_shader != 0u) + { + fragment_shader = make_shader(GL_FRAGMENT_SHADER, fs_text); + if (fragment_shader != 0u) + { + /* make the program that connect the two shader and link it */ + program = glCreateProgram(); + if (program != 0u) + { + /* attach both shader and link */ + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + glGetProgramiv(program, GL_LINK_STATUS, &program_ok); + + if (program_ok != GL_TRUE) + { + fprintf(stderr, "ERROR, failed to link shader program\n"); + glGetProgramInfoLog(program, 8192, &log_length, info_log); + fprintf(stderr, "ERROR: \n%s\n\n", info_log); + glDeleteProgram(program); + glDeleteShader(fragment_shader); + glDeleteShader(vertex_shader); + program = 0u; + } + } + } + else + { + fprintf(stderr, "ERROR: Unable to load fragment shader\n"); + glDeleteShader(vertex_shader); + } + } + else + { + fprintf(stderr, "ERROR: Unable to load vertex shader\n"); + } + return program; +} + +/********************************************************************** + * Geometry creation functions + *********************************************************************/ + +/* Generate vertices and indices for the heightmap + */ +static void init_map(void) +{ + int i; + int j; + int k; + GLfloat step = MAP_SIZE / (MAP_NUM_VERTICES - 1); + GLfloat x = 0.0f; + GLfloat z = 0.0f; + /* Create a flat grid */ + k = 0; + for (i = 0 ; i < MAP_NUM_VERTICES ; ++i) + { + for (j = 0 ; j < MAP_NUM_VERTICES ; ++j) + { + map_vertices[0][k] = x; + map_vertices[1][k] = 0.0f; + map_vertices[2][k] = z; + z += step; + ++k; + } + x += step; + z = 0.0f; + } +#if DEBUG_ENABLED + for (i = 0 ; i < MAP_NUM_TOTAL_VERTICES ; ++i) + { + printf ("Vertice %d (%f, %f, %f)\n", + i, map_vertices[0][i], map_vertices[1][i], map_vertices[2][i]); + + } +#endif + /* create indices */ + /* line fan based on i + * i+1 + * | / i + n + 1 + * | / + * |/ + * i --- i + n + */ + + /* close the top of the square */ + k = 0; + for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i) + { + map_line_indices[k++] = (i + 1) * MAP_NUM_VERTICES -1; + map_line_indices[k++] = (i + 2) * MAP_NUM_VERTICES -1; + } + /* close the right of the square */ + for (i = 0 ; i < MAP_NUM_VERTICES -1 ; ++i) + { + map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i; + map_line_indices[k++] = (MAP_NUM_VERTICES - 1) * MAP_NUM_VERTICES + i + 1; + } + + for (i = 0 ; i < (MAP_NUM_VERTICES - 1) ; ++i) + { + for (j = 0 ; j < (MAP_NUM_VERTICES - 1) ; ++j) + { + int ref = i * (MAP_NUM_VERTICES) + j; + map_line_indices[k++] = ref; + map_line_indices[k++] = ref + 1; + + map_line_indices[k++] = ref; + map_line_indices[k++] = ref + MAP_NUM_VERTICES; + + map_line_indices[k++] = ref; + map_line_indices[k++] = ref + MAP_NUM_VERTICES + 1; + } + } + +#ifdef DEBUG_ENABLED + for (k = 0 ; k < 2 * MAP_NUM_LINES ; k += 2) + { + int beg, end; + beg = map_line_indices[k]; + end = map_line_indices[k+1]; + printf ("Line %d: %d -> %d (%f, %f, %f) -> (%f, %f, %f)\n", + k / 2, beg, end, + map_vertices[0][beg], map_vertices[1][beg], map_vertices[2][beg], + map_vertices[0][end], map_vertices[1][end], map_vertices[2][end]); + } +#endif +} + +static void generate_heightmap__circle(float* center_x, float* center_y, + float* size, float* displacement) +{ + float sign; + /* random value for element in between [0-1.0] */ + *center_x = (MAP_SIZE * rand()) / (1.0f * RAND_MAX); + *center_y = (MAP_SIZE * rand()) / (1.0f * RAND_MAX); + *size = (MAX_CIRCLE_SIZE * rand()) / (1.0f * RAND_MAX); + sign = (1.0f * rand()) / (1.0f * RAND_MAX); + sign = (sign < DISPLACEMENT_SIGN_LIMIT) ? -1.0f : 1.0f; + *displacement = (sign * (MAX_DISPLACEMENT * rand())) / (1.0f * RAND_MAX); +} + +/* Run the specified number of iterations of the generation process for the + * heightmap + */ +static void update_map(int num_iter) +{ + assert(num_iter > 0); + while(num_iter) + { + /* center of the circle */ + float center_x; + float center_z; + float circle_size; + float disp; + size_t ii; + generate_heightmap__circle(¢er_x, ¢er_z, &circle_size, &disp); + disp = disp / 2.0f; + for (ii = 0u ; ii < MAP_NUM_TOTAL_VERTICES ; ++ii) + { + GLfloat dx = center_x - map_vertices[0][ii]; + GLfloat dz = center_z - map_vertices[2][ii]; + GLfloat pd = (2.0f * (float) sqrt((dx * dx) + (dz * dz))) / circle_size; + if (fabs(pd) <= 1.0f) + { + /* tx,tz is within the circle */ + GLfloat new_height = disp + (float) (cos(pd*3.14f)*disp); + map_vertices[1][ii] += new_height; + } + } + --num_iter; + } +} + +/********************************************************************** + * OpenGL helper functions + *********************************************************************/ + +/* Create VBO, IBO and VAO objects for the heightmap geometry and bind them to + * the specified program object + */ +static void make_mesh(GLuint program) +{ + GLuint attrloc; + + glGenVertexArrays(1, &mesh); + glGenBuffers(4, mesh_vbo); + glBindVertexArray(mesh); + /* Prepare the data for drawing through a buffer inidices */ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_vbo[3]); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* MAP_NUM_LINES * 2, map_line_indices, GL_STATIC_DRAW); + + /* Prepare the attributes for rendering */ + attrloc = glGetAttribLocation(program, "x"); + glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[0]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[0][0], GL_STATIC_DRAW); + glEnableVertexAttribArray(attrloc); + glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); + + attrloc = glGetAttribLocation(program, "z"); + glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[2]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[2][0], GL_STATIC_DRAW); + glEnableVertexAttribArray(attrloc); + glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); + + attrloc = glGetAttribLocation(program, "y"); + glBindBuffer(GL_ARRAY_BUFFER, mesh_vbo[1]); + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0], GL_DYNAMIC_DRAW); + glEnableVertexAttribArray(attrloc); + glVertexAttribPointer(attrloc, 1, GL_FLOAT, GL_FALSE, 0, 0); +} + +/* Update VBO vertices from source data + */ +static void update_mesh(void) +{ + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * MAP_NUM_TOTAL_VERTICES, &map_vertices[1][0]); +} + +/********************************************************************** + * GLFW callback functions + *********************************************************************/ + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + switch(key) + { + case GLFW_KEY_ESCAPE: + /* Exit program on Escape */ + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + } +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +int main(int argc, char** argv) +{ + GLFWwindow* window; + int iter; + double dt; + double last_update_time; + int frame; + float f; + GLint uloc_modelview; + GLint uloc_project; + int width, height; + + GLuint shader_program; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + + window = glfwCreateWindow(800, 600, "GLFW OpenGL3 Heightmap demo", NULL, NULL); + if (! window ) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + /* Register events callback */ + glfwSetKeyCallback(window, key_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + /* Prepare opengl resources for rendering */ + shader_program = make_shader_program(vertex_shader_text, fragment_shader_text); + + if (shader_program == 0u) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glUseProgram(shader_program); + uloc_project = glGetUniformLocation(shader_program, "project"); + uloc_modelview = glGetUniformLocation(shader_program, "modelview"); + + /* Compute the projection matrix */ + f = 1.0f / tanf(view_angle / 2.0f); + projection_matrix[0] = f / aspect_ratio; + projection_matrix[5] = f; + projection_matrix[10] = (z_far + z_near)/ (z_near - z_far); + projection_matrix[11] = -1.0f; + projection_matrix[14] = 2.0f * (z_far * z_near) / (z_near - z_far); + glUniformMatrix4fv(uloc_project, 1, GL_FALSE, projection_matrix); + + /* Set the camera position */ + modelview_matrix[12] = -5.0f; + modelview_matrix[13] = -5.0f; + modelview_matrix[14] = -20.0f; + glUniformMatrix4fv(uloc_modelview, 1, GL_FALSE, modelview_matrix); + + /* Create mesh data */ + init_map(); + make_mesh(shader_program); + + /* Create vao + vbo to store the mesh */ + /* Create the vbo to store all the information for the grid and the height */ + + /* setup the scene ready for rendering */ + glfwGetFramebufferSize(window, &width, &height); + glViewport(0, 0, width, height); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + /* main loop */ + frame = 0; + iter = 0; + last_update_time = glfwGetTime(); + + while (!glfwWindowShouldClose(window)) + { + ++frame; + /* render the next frame */ + glClear(GL_COLOR_BUFFER_BIT); + glDrawElements(GL_LINES, 2* MAP_NUM_LINES , GL_UNSIGNED_INT, 0); + + /* display and process events through callbacks */ + glfwSwapBuffers(window); + glfwPollEvents(); + /* Check the frame rate and update the heightmap if needed */ + dt = glfwGetTime(); + if ((dt - last_update_time) > 0.2) + { + /* generate the next iteration of the heightmap */ + if (iter < MAX_ITER) + { + update_map(NUM_ITER_AT_A_TIME); + update_mesh(); + iter += NUM_ITER_AT_A_TIME; + } + last_update_time = dt; + frame = 0; + } + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/examples/particles.c b/apps/exampleViewer/common/glfw/examples/particles.c new file mode 100644 index 0000000000..4a65ab124b --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/particles.c @@ -0,0 +1,1072 @@ +//======================================================================== +// A simple particle engine with threaded physics +// Copyright (c) Marcus Geelnard +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +// Define tokens for GL_EXT_separate_specular_color if not already defined +#ifndef GL_EXT_separate_specular_color +#define GL_LIGHT_MODEL_COLOR_CONTROL_EXT 0x81F8 +#define GL_SINGLE_COLOR_EXT 0x81F9 +#define GL_SEPARATE_SPECULAR_COLOR_EXT 0x81FA +#endif // GL_EXT_separate_specular_color + + +//======================================================================== +// Type definitions +//======================================================================== + +typedef struct +{ + float x, y, z; +} Vec3; + +// This structure is used for interleaved vertex arrays (see the +// draw_particles function) +// +// NOTE: This structure SHOULD be packed on most systems. It uses 32-bit fields +// on 32-bit boundaries, and is a multiple of 64 bits in total (6x32=3x64). If +// it does not work, try using pragmas or whatever to force the structure to be +// packed. +typedef struct +{ + GLfloat s, t; // Texture coordinates + GLuint rgba; // Color (four ubytes packed into an uint) + GLfloat x, y, z; // Vertex coordinates +} Vertex; + + +//======================================================================== +// Program control global variables +//======================================================================== + +// Window dimensions +float aspect_ratio; + +// "wireframe" flag (true if we use wireframe view) +int wireframe; + +// Thread synchronization +struct { + double t; // Time (s) + float dt; // Time since last frame (s) + int p_frame; // Particle physics frame number + int d_frame; // Particle draw frame number + cnd_t p_done; // Condition: particle physics done + cnd_t d_done; // Condition: particle draw done + mtx_t particles_lock; // Particles data sharing mutex +} thread_sync; + + +//======================================================================== +// Texture declarations (we hard-code them into the source code, since +// they are so simple) +//======================================================================== + +#define P_TEX_WIDTH 8 // Particle texture dimensions +#define P_TEX_HEIGHT 8 +#define F_TEX_WIDTH 16 // Floor texture dimensions +#define F_TEX_HEIGHT 16 + +// Texture object IDs +GLuint particle_tex_id, floor_tex_id; + +// Particle texture (a simple spot) +const unsigned char particle_texture[ P_TEX_WIDTH * P_TEX_HEIGHT ] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x11, 0x22, 0x22, 0x11, 0x00, 0x00, + 0x00, 0x11, 0x33, 0x88, 0x77, 0x33, 0x11, 0x00, + 0x00, 0x22, 0x88, 0xff, 0xee, 0x77, 0x22, 0x00, + 0x00, 0x22, 0x77, 0xee, 0xff, 0x88, 0x22, 0x00, + 0x00, 0x11, 0x33, 0x77, 0x88, 0x33, 0x11, 0x00, + 0x00, 0x00, 0x11, 0x33, 0x22, 0x11, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +// Floor texture (your basic checkered floor) +const unsigned char floor_texture[ F_TEX_WIDTH * F_TEX_HEIGHT ] = { + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xff, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xcc, 0xee, 0xff, 0xf0, 0xf0, 0xf0, 0xf0, 0x30, 0x66, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xee, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xcc, 0xf0, 0xf0, 0xf0, 0x30, 0x30, 0x55, 0x30, 0x30, 0x44, 0x30, 0x30, + 0xf0, 0xdd, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x60, 0x30, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0x33, 0x33, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x33, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x20, 0x30, 0x30, 0xf0, 0xff, 0xf0, 0xf0, 0xdd, 0xf0, 0xf0, 0xff, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x55, 0x33, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xf0, + 0x30, 0x44, 0x66, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xaa, 0xf0, 0xf0, 0xcc, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xff, 0xf0, 0xf0, 0xf0, 0xff, 0xf0, 0xdd, 0xf0, + 0x30, 0x30, 0x30, 0x77, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, +}; + + +//======================================================================== +// These are fixed constants that control the particle engine. In a +// modular world, these values should be variables... +//======================================================================== + +// Maximum number of particles +#define MAX_PARTICLES 3000 + +// Life span of a particle (in seconds) +#define LIFE_SPAN 8.f + +// A new particle is born every [BIRTH_INTERVAL] second +#define BIRTH_INTERVAL (LIFE_SPAN/(float)MAX_PARTICLES) + +// Particle size (meters) +#define PARTICLE_SIZE 0.7f + +// Gravitational constant (m/s^2) +#define GRAVITY 9.8f + +// Base initial velocity (m/s) +#define VELOCITY 8.f + +// Bounce friction (1.0 = no friction, 0.0 = maximum friction) +#define FRICTION 0.75f + +// "Fountain" height (m) +#define FOUNTAIN_HEIGHT 3.f + +// Fountain radius (m) +#define FOUNTAIN_RADIUS 1.6f + +// Minimum delta-time for particle phisics (s) +#define MIN_DELTA_T (BIRTH_INTERVAL * 0.5f) + + +//======================================================================== +// Particle system global variables +//======================================================================== + +// This structure holds all state for a single particle +typedef struct { + float x,y,z; // Position in space + float vx,vy,vz; // Velocity vector + float r,g,b; // Color of particle + float life; // Life of particle (1.0 = newborn, < 0.0 = dead) + int active; // Tells if this particle is active +} PARTICLE; + +// Global vectors holding all particles. We use two vectors for double +// buffering. +static PARTICLE particles[MAX_PARTICLES]; + +// Global variable holding the age of the youngest particle +static float min_age; + +// Color of latest born particle (used for fountain lighting) +static float glow_color[4]; + +// Position of latest born particle (used for fountain lighting) +static float glow_pos[4]; + + +//======================================================================== +// Object material and fog configuration constants +//======================================================================== + +const GLfloat fountain_diffuse[4] = { 0.7f, 1.f, 1.f, 1.f }; +const GLfloat fountain_specular[4] = { 1.f, 1.f, 1.f, 1.f }; +const GLfloat fountain_shininess = 12.f; +const GLfloat floor_diffuse[4] = { 1.f, 0.6f, 0.6f, 1.f }; +const GLfloat floor_specular[4] = { 0.6f, 0.6f, 0.6f, 1.f }; +const GLfloat floor_shininess = 18.f; +const GLfloat fog_color[4] = { 0.1f, 0.1f, 0.1f, 1.f }; + + +//======================================================================== +// Print usage information +//======================================================================== + +static void usage(void) +{ + printf("Usage: particles [-bfhs]\n"); + printf("Options:\n"); + printf(" -f Run in full screen\n"); + printf(" -h Display this help\n"); + printf(" -s Run program as single thread (default is to use two threads)\n"); + printf("\n"); + printf("Program runtime controls:\n"); + printf(" W Toggle wireframe mode\n"); + printf(" Esc Exit program\n"); +} + + +//======================================================================== +// Initialize a new particle +//======================================================================== + +static void init_particle(PARTICLE *p, double t) +{ + float xy_angle, velocity; + + // Start position of particle is at the fountain blow-out + p->x = 0.f; + p->y = 0.f; + p->z = FOUNTAIN_HEIGHT; + + // Start velocity is up (Z)... + p->vz = 0.7f + (0.3f / 4096.f) * (float) (rand() & 4095); + + // ...and a randomly chosen X/Y direction + xy_angle = (2.f * (float) M_PI / 4096.f) * (float) (rand() & 4095); + p->vx = 0.4f * (float) cos(xy_angle); + p->vy = 0.4f * (float) sin(xy_angle); + + // Scale velocity vector according to a time-varying velocity + velocity = VELOCITY * (0.8f + 0.1f * (float) (sin(0.5 * t) + sin(1.31 * t))); + p->vx *= velocity; + p->vy *= velocity; + p->vz *= velocity; + + // Color is time-varying + p->r = 0.7f + 0.3f * (float) sin(0.34 * t + 0.1); + p->g = 0.6f + 0.4f * (float) sin(0.63 * t + 1.1); + p->b = 0.6f + 0.4f * (float) sin(0.91 * t + 2.1); + + // Store settings for fountain glow lighting + glow_pos[0] = 0.4f * (float) sin(1.34 * t); + glow_pos[1] = 0.4f * (float) sin(3.11 * t); + glow_pos[2] = FOUNTAIN_HEIGHT + 1.f; + glow_pos[3] = 1.f; + glow_color[0] = p->r; + glow_color[1] = p->g; + glow_color[2] = p->b; + glow_color[3] = 1.f; + + // The particle is new-born and active + p->life = 1.f; + p->active = 1; +} + + +//======================================================================== +// Update a particle +//======================================================================== + +#define FOUNTAIN_R2 (FOUNTAIN_RADIUS+PARTICLE_SIZE/2)*(FOUNTAIN_RADIUS+PARTICLE_SIZE/2) + +static void update_particle(PARTICLE *p, float dt) +{ + // If the particle is not active, we need not do anything + if (!p->active) + return; + + // The particle is getting older... + p->life -= dt * (1.f / LIFE_SPAN); + + // Did the particle die? + if (p->life <= 0.f) + { + p->active = 0; + return; + } + + // Apply gravity + p->vz = p->vz - GRAVITY * dt; + + // Update particle position + p->x = p->x + p->vx * dt; + p->y = p->y + p->vy * dt; + p->z = p->z + p->vz * dt; + + // Simple collision detection + response + if (p->vz < 0.f) + { + // Particles should bounce on the fountain (with friction) + if ((p->x * p->x + p->y * p->y) < FOUNTAIN_R2 && + p->z < (FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2)) + { + p->vz = -FRICTION * p->vz; + p->z = FOUNTAIN_HEIGHT + PARTICLE_SIZE / 2 + + FRICTION * (FOUNTAIN_HEIGHT + + PARTICLE_SIZE / 2 - p->z); + } + + // Particles should bounce on the floor (with friction) + else if (p->z < PARTICLE_SIZE / 2) + { + p->vz = -FRICTION * p->vz; + p->z = PARTICLE_SIZE / 2 + + FRICTION * (PARTICLE_SIZE / 2 - p->z); + } + } +} + + +//======================================================================== +// The main frame for the particle engine. Called once per frame. +//======================================================================== + +static void particle_engine(double t, float dt) +{ + int i; + float dt2; + + // Update particles (iterated several times per frame if dt is too large) + while (dt > 0.f) + { + // Calculate delta time for this iteration + dt2 = dt < MIN_DELTA_T ? dt : MIN_DELTA_T; + + for (i = 0; i < MAX_PARTICLES; i++) + update_particle(&particles[i], dt2); + + min_age += dt2; + + // Should we create any new particle(s)? + while (min_age >= BIRTH_INTERVAL) + { + min_age -= BIRTH_INTERVAL; + + // Find a dead particle to replace with a new one + for (i = 0; i < MAX_PARTICLES; i++) + { + if (!particles[i].active) + { + init_particle(&particles[i], t + min_age); + update_particle(&particles[i], min_age); + break; + } + } + } + + dt -= dt2; + } +} + + +//======================================================================== +// Draw all active particles. We use OpenGL 1.1 vertex +// arrays for this in order to accelerate the drawing. +//======================================================================== + +#define BATCH_PARTICLES 70 // Number of particles to draw in each batch + // (70 corresponds to 7.5 KB = will not blow + // the L1 data cache on most CPUs) +#define PARTICLE_VERTS 4 // Number of vertices per particle + +static void draw_particles(GLFWwindow* window, double t, float dt) +{ + int i, particle_count; + Vertex vertex_array[BATCH_PARTICLES * PARTICLE_VERTS]; + Vertex* vptr; + float alpha; + GLuint rgba; + Vec3 quad_lower_left, quad_lower_right; + GLfloat mat[16]; + PARTICLE* pptr; + + // Here comes the real trick with flat single primitive objects (s.c. + // "billboards"): We must rotate the textured primitive so that it + // always faces the viewer (is coplanar with the view-plane). + // We: + // 1) Create the primitive around origo (0,0,0) + // 2) Rotate it so that it is coplanar with the view plane + // 3) Translate it according to the particle position + // Note that 1) and 2) is the same for all particles (done only once). + + // Get modelview matrix. We will only use the upper left 3x3 part of + // the matrix, which represents the rotation. + glGetFloatv(GL_MODELVIEW_MATRIX, mat); + + // 1) & 2) We do it in one swift step: + // Although not obvious, the following six lines represent two matrix/ + // vector multiplications. The matrix is the inverse 3x3 rotation + // matrix (i.e. the transpose of the same matrix), and the two vectors + // represent the lower left corner of the quad, PARTICLE_SIZE/2 * + // (-1,-1,0), and the lower right corner, PARTICLE_SIZE/2 * (1,-1,0). + // The upper left/right corners of the quad is always the negative of + // the opposite corners (regardless of rotation). + quad_lower_left.x = (-PARTICLE_SIZE / 2) * (mat[0] + mat[1]); + quad_lower_left.y = (-PARTICLE_SIZE / 2) * (mat[4] + mat[5]); + quad_lower_left.z = (-PARTICLE_SIZE / 2) * (mat[8] + mat[9]); + quad_lower_right.x = (PARTICLE_SIZE / 2) * (mat[0] - mat[1]); + quad_lower_right.y = (PARTICLE_SIZE / 2) * (mat[4] - mat[5]); + quad_lower_right.z = (PARTICLE_SIZE / 2) * (mat[8] - mat[9]); + + // Don't update z-buffer, since all particles are transparent! + glDepthMask(GL_FALSE); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + + // Select particle texture + if (!wireframe) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, particle_tex_id); + } + + // Set up vertex arrays. We use interleaved arrays, which is easier to + // handle (in most situations) and it gives a linear memeory access + // access pattern (which may give better performance in some + // situations). GL_T2F_C4UB_V3F means: 2 floats for texture coords, + // 4 ubytes for color and 3 floats for vertex coord (in that order). + // Most OpenGL cards / drivers are optimized for this format. + glInterleavedArrays(GL_T2F_C4UB_V3F, 0, vertex_array); + + // Wait for particle physics thread to be done + mtx_lock(&thread_sync.particles_lock); + while (!glfwWindowShouldClose(window) && + thread_sync.p_frame <= thread_sync.d_frame) + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 100 * 1000 * 1000; + ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); + ts.tv_nsec %= 1000 * 1000 * 1000; + cnd_timedwait(&thread_sync.p_done, &thread_sync.particles_lock, &ts); + } + + // Store the frame time and delta time for the physics thread + thread_sync.t = t; + thread_sync.dt = dt; + + // Update frame counter + thread_sync.d_frame++; + + // Loop through all particles and build vertex arrays. + particle_count = 0; + vptr = vertex_array; + pptr = particles; + + for (i = 0; i < MAX_PARTICLES; i++) + { + if (pptr->active) + { + // Calculate particle intensity (we set it to max during 75% + // of its life, then it fades out) + alpha = 4.f * pptr->life; + if (alpha > 1.f) + alpha = 1.f; + + // Convert color from float to 8-bit (store it in a 32-bit + // integer using endian independent type casting) + ((GLubyte*) &rgba)[0] = (GLubyte)(pptr->r * 255.f); + ((GLubyte*) &rgba)[1] = (GLubyte)(pptr->g * 255.f); + ((GLubyte*) &rgba)[2] = (GLubyte)(pptr->b * 255.f); + ((GLubyte*) &rgba)[3] = (GLubyte)(alpha * 255.f); + + // 3) Translate the quad to the correct position in modelview + // space and store its parameters in vertex arrays (we also + // store texture coord and color information for each vertex). + + // Lower left corner + vptr->s = 0.f; + vptr->t = 0.f; + vptr->rgba = rgba; + vptr->x = pptr->x + quad_lower_left.x; + vptr->y = pptr->y + quad_lower_left.y; + vptr->z = pptr->z + quad_lower_left.z; + vptr ++; + + // Lower right corner + vptr->s = 1.f; + vptr->t = 0.f; + vptr->rgba = rgba; + vptr->x = pptr->x + quad_lower_right.x; + vptr->y = pptr->y + quad_lower_right.y; + vptr->z = pptr->z + quad_lower_right.z; + vptr ++; + + // Upper right corner + vptr->s = 1.f; + vptr->t = 1.f; + vptr->rgba = rgba; + vptr->x = pptr->x - quad_lower_left.x; + vptr->y = pptr->y - quad_lower_left.y; + vptr->z = pptr->z - quad_lower_left.z; + vptr ++; + + // Upper left corner + vptr->s = 0.f; + vptr->t = 1.f; + vptr->rgba = rgba; + vptr->x = pptr->x - quad_lower_right.x; + vptr->y = pptr->y - quad_lower_right.y; + vptr->z = pptr->z - quad_lower_right.z; + vptr ++; + + // Increase count of drawable particles + particle_count ++; + } + + // If we have filled up one batch of particles, draw it as a set + // of quads using glDrawArrays. + if (particle_count >= BATCH_PARTICLES) + { + // The first argument tells which primitive type we use (QUAD) + // The second argument tells the index of the first vertex (0) + // The last argument is the vertex count + glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count); + particle_count = 0; + vptr = vertex_array; + } + + // Next particle + pptr++; + } + + // We are done with the particle data + mtx_unlock(&thread_sync.particles_lock); + cnd_signal(&thread_sync.d_done); + + // Draw final batch of particles (if any) + glDrawArrays(GL_QUADS, 0, PARTICLE_VERTS * particle_count); + + // Disable vertex arrays (Note: glInterleavedArrays implicitly called + // glEnableClientState for vertex, texture coord and color arrays) + glDisableClientState(GL_VERTEX_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_COLOR_ARRAY); + + glDisable(GL_TEXTURE_2D); + glDisable(GL_BLEND); + + glDepthMask(GL_TRUE); +} + + +//======================================================================== +// Fountain geometry specification +//======================================================================== + +#define FOUNTAIN_SIDE_POINTS 14 +#define FOUNTAIN_SWEEP_STEPS 32 + +static const float fountain_side[FOUNTAIN_SIDE_POINTS * 2] = +{ + 1.2f, 0.f, 1.f, 0.2f, 0.41f, 0.3f, 0.4f, 0.35f, + 0.4f, 1.95f, 0.41f, 2.f, 0.8f, 2.2f, 1.2f, 2.4f, + 1.5f, 2.7f, 1.55f,2.95f, 1.6f, 3.f, 1.f, 3.f, + 0.5f, 3.f, 0.f, 3.f +}; + +static const float fountain_normal[FOUNTAIN_SIDE_POINTS * 2] = +{ + 1.0000f, 0.0000f, 0.6428f, 0.7660f, 0.3420f, 0.9397f, 1.0000f, 0.0000f, + 1.0000f, 0.0000f, 0.3420f,-0.9397f, 0.4226f,-0.9063f, 0.5000f,-0.8660f, + 0.7660f,-0.6428f, 0.9063f,-0.4226f, 0.0000f,1.00000f, 0.0000f,1.00000f, + 0.0000f,1.00000f, 0.0000f,1.00000f +}; + + +//======================================================================== +// Draw a fountain +//======================================================================== + +static void draw_fountain(void) +{ + static GLuint fountain_list = 0; + double angle; + float x, y; + int m, n; + + // The first time, we build the fountain display list + if (!fountain_list) + { + fountain_list = glGenLists(1); + glNewList(fountain_list, GL_COMPILE_AND_EXECUTE); + + glMaterialfv(GL_FRONT, GL_DIFFUSE, fountain_diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, fountain_specular); + glMaterialf(GL_FRONT, GL_SHININESS, fountain_shininess); + + // Build fountain using triangle strips + for (n = 0; n < FOUNTAIN_SIDE_POINTS - 1; n++) + { + glBegin(GL_TRIANGLE_STRIP); + for (m = 0; m <= FOUNTAIN_SWEEP_STEPS; m++) + { + angle = (double) m * (2.0 * M_PI / (double) FOUNTAIN_SWEEP_STEPS); + x = (float) cos(angle); + y = (float) sin(angle); + + // Draw triangle strip + glNormal3f(x * fountain_normal[n * 2 + 2], + y * fountain_normal[n * 2 + 2], + fountain_normal[n * 2 + 3]); + glVertex3f(x * fountain_side[n * 2 + 2], + y * fountain_side[n * 2 + 2], + fountain_side[n * 2 +3 ]); + glNormal3f(x * fountain_normal[n * 2], + y * fountain_normal[n * 2], + fountain_normal[n * 2 + 1]); + glVertex3f(x * fountain_side[n * 2], + y * fountain_side[n * 2], + fountain_side[n * 2 + 1]); + } + + glEnd(); + } + + glEndList(); + } + else + glCallList(fountain_list); +} + + +//======================================================================== +// Recursive function for building variable tesselated floor +//======================================================================== + +static void tessellate_floor(float x1, float y1, float x2, float y2, int depth) +{ + float delta, x, y; + + // Last recursion? + if (depth >= 5) + delta = 999999.f; + else + { + x = (float) (fabs(x1) < fabs(x2) ? fabs(x1) : fabs(x2)); + y = (float) (fabs(y1) < fabs(y2) ? fabs(y1) : fabs(y2)); + delta = x*x + y*y; + } + + // Recurse further? + if (delta < 0.1f) + { + x = (x1 + x2) * 0.5f; + y = (y1 + y2) * 0.5f; + tessellate_floor(x1, y1, x, y, depth + 1); + tessellate_floor(x, y1, x2, y, depth + 1); + tessellate_floor(x1, y, x, y2, depth + 1); + tessellate_floor(x, y, x2, y2, depth + 1); + } + else + { + glTexCoord2f(x1 * 30.f, y1 * 30.f); + glVertex3f( x1 * 80.f, y1 * 80.f, 0.f); + glTexCoord2f(x2 * 30.f, y1 * 30.f); + glVertex3f( x2 * 80.f, y1 * 80.f, 0.f); + glTexCoord2f(x2 * 30.f, y2 * 30.f); + glVertex3f( x2 * 80.f, y2 * 80.f, 0.f); + glTexCoord2f(x1 * 30.f, y2 * 30.f); + glVertex3f( x1 * 80.f, y2 * 80.f, 0.f); + } +} + + +//======================================================================== +// Draw floor. We build the floor recursively and let the tessellation in the +// center (near x,y=0,0) be high, while the tessellation around the edges be +// low. +//======================================================================== + +static void draw_floor(void) +{ + static GLuint floor_list = 0; + + if (!wireframe) + { + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, floor_tex_id); + } + + // The first time, we build the floor display list + if (!floor_list) + { + floor_list = glGenLists(1); + glNewList(floor_list, GL_COMPILE_AND_EXECUTE); + + glMaterialfv(GL_FRONT, GL_DIFFUSE, floor_diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, floor_specular); + glMaterialf(GL_FRONT, GL_SHININESS, floor_shininess); + + // Draw floor as a bunch of triangle strips (high tesselation + // improves lighting) + glNormal3f(0.f, 0.f, 1.f); + glBegin(GL_QUADS); + tessellate_floor(-1.f, -1.f, 0.f, 0.f, 0); + tessellate_floor( 0.f, -1.f, 1.f, 0.f, 0); + tessellate_floor( 0.f, 0.f, 1.f, 1.f, 0); + tessellate_floor(-1.f, 0.f, 0.f, 1.f, 0); + glEnd(); + + glEndList(); + } + else + glCallList(floor_list); + + glDisable(GL_TEXTURE_2D); + +} + + +//======================================================================== +// Position and configure light sources +//======================================================================== + +static void setup_lights(void) +{ + float l1pos[4], l1amb[4], l1dif[4], l1spec[4]; + float l2pos[4], l2amb[4], l2dif[4], l2spec[4]; + + // Set light source 1 parameters + l1pos[0] = 0.f; l1pos[1] = -9.f; l1pos[2] = 8.f; l1pos[3] = 1.f; + l1amb[0] = 0.2f; l1amb[1] = 0.2f; l1amb[2] = 0.2f; l1amb[3] = 1.f; + l1dif[0] = 0.8f; l1dif[1] = 0.4f; l1dif[2] = 0.2f; l1dif[3] = 1.f; + l1spec[0] = 1.f; l1spec[1] = 0.6f; l1spec[2] = 0.2f; l1spec[3] = 0.f; + + // Set light source 2 parameters + l2pos[0] = -15.f; l2pos[1] = 12.f; l2pos[2] = 1.5f; l2pos[3] = 1.f; + l2amb[0] = 0.f; l2amb[1] = 0.f; l2amb[2] = 0.f; l2amb[3] = 1.f; + l2dif[0] = 0.2f; l2dif[1] = 0.4f; l2dif[2] = 0.8f; l2dif[3] = 1.f; + l2spec[0] = 0.2f; l2spec[1] = 0.6f; l2spec[2] = 1.f; l2spec[3] = 0.f; + + glLightfv(GL_LIGHT1, GL_POSITION, l1pos); + glLightfv(GL_LIGHT1, GL_AMBIENT, l1amb); + glLightfv(GL_LIGHT1, GL_DIFFUSE, l1dif); + glLightfv(GL_LIGHT1, GL_SPECULAR, l1spec); + glLightfv(GL_LIGHT2, GL_POSITION, l2pos); + glLightfv(GL_LIGHT2, GL_AMBIENT, l2amb); + glLightfv(GL_LIGHT2, GL_DIFFUSE, l2dif); + glLightfv(GL_LIGHT2, GL_SPECULAR, l2spec); + glLightfv(GL_LIGHT3, GL_POSITION, glow_pos); + glLightfv(GL_LIGHT3, GL_DIFFUSE, glow_color); + glLightfv(GL_LIGHT3, GL_SPECULAR, glow_color); + + glEnable(GL_LIGHT1); + glEnable(GL_LIGHT2); + glEnable(GL_LIGHT3); +} + + +//======================================================================== +// Main rendering function +//======================================================================== + +static void draw_scene(GLFWwindow* window, double t) +{ + double xpos, ypos, zpos, angle_x, angle_y, angle_z; + static double t_old = 0.0; + float dt; + mat4x4 projection; + + // Calculate frame-to-frame delta time + dt = (float) (t - t_old); + t_old = t; + + mat4x4_perspective(projection, + 65.f * (float) M_PI / 180.f, + aspect_ratio, + 1.0, 60.0); + + glClearColor(0.1f, 0.1f, 0.1f, 1.f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadMatrixf((const GLfloat*) projection); + + // Setup camera + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Rotate camera + angle_x = 90.0 - 10.0; + angle_y = 10.0 * sin(0.3 * t); + angle_z = 10.0 * t; + glRotated(-angle_x, 1.0, 0.0, 0.0); + glRotated(-angle_y, 0.0, 1.0, 0.0); + glRotated(-angle_z, 0.0, 0.0, 1.0); + + // Translate camera + xpos = 15.0 * sin((M_PI / 180.0) * angle_z) + + 2.0 * sin((M_PI / 180.0) * 3.1 * t); + ypos = -15.0 * cos((M_PI / 180.0) * angle_z) + + 2.0 * cos((M_PI / 180.0) * 2.9 * t); + zpos = 4.0 + 2.0 * cos((M_PI / 180.0) * 4.9 * t); + glTranslated(-xpos, -ypos, -zpos); + + glFrontFace(GL_CCW); + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); + + setup_lights(); + glEnable(GL_LIGHTING); + + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_EXP); + glFogf(GL_FOG_DENSITY, 0.05f); + glFogfv(GL_FOG_COLOR, fog_color); + + draw_floor(); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + glDepthMask(GL_TRUE); + + draw_fountain(); + + glDisable(GL_LIGHTING); + glDisable(GL_FOG); + + // Particles must be drawn after all solid objects have been drawn + draw_particles(window, t, dt); + + // Z-buffer not needed anymore + glDisable(GL_DEPTH_TEST); +} + + +//======================================================================== +// Window resize callback function +//======================================================================== + +static void resize_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); + aspect_ratio = height ? width / (float) height : 1.f; +} + + +//======================================================================== +// Key callback functions +//======================================================================== + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action == GLFW_PRESS) + { + switch (key) + { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_W: + wireframe = !wireframe; + glPolygonMode(GL_FRONT_AND_BACK, + wireframe ? GL_LINE : GL_FILL); + break; + default: + break; + } + } +} + + +//======================================================================== +// Thread for updating particle physics +//======================================================================== + +static int physics_thread_main(void* arg) +{ + GLFWwindow* window = arg; + + for (;;) + { + mtx_lock(&thread_sync.particles_lock); + + // Wait for particle drawing to be done + while (!glfwWindowShouldClose(window) && + thread_sync.p_frame > thread_sync.d_frame) + { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + ts.tv_nsec += 100 * 1000 * 1000; + ts.tv_sec += ts.tv_nsec / (1000 * 1000 * 1000); + ts.tv_nsec %= 1000 * 1000 * 1000; + cnd_timedwait(&thread_sync.d_done, &thread_sync.particles_lock, &ts); + } + + if (glfwWindowShouldClose(window)) + break; + + // Update particles + particle_engine(thread_sync.t, thread_sync.dt); + + // Update frame counter + thread_sync.p_frame++; + + // Unlock mutex and signal drawing thread + mtx_unlock(&thread_sync.particles_lock); + cnd_signal(&thread_sync.p_done); + } + + return 0; +} + + +//======================================================================== +// main +//======================================================================== + +int main(int argc, char** argv) +{ + int ch, width, height; + thrd_t physics_thread = 0; + GLFWwindow* window; + GLFWmonitor* monitor = NULL; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + while ((ch = getopt(argc, argv, "fh")) != -1) + { + switch (ch) + { + case 'f': + monitor = glfwGetPrimaryMonitor(); + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + } + } + + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + + width = mode->width; + height = mode->height; + } + else + { + width = 640; + height = 480; + } + + window = glfwCreateWindow(width, height, "Particle Engine", monitor, NULL); + if (!window) + { + fprintf(stderr, "Failed to create GLFW window\n"); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + if (monitor) + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glfwSetFramebufferSizeCallback(window, resize_callback); + glfwSetKeyCallback(window, key_callback); + + // Set initial aspect ratio + glfwGetFramebufferSize(window, &width, &height); + resize_callback(window, width, height); + + // Upload particle texture + glGenTextures(1, &particle_tex_id); + glBindTexture(GL_TEXTURE_2D, particle_tex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, P_TEX_WIDTH, P_TEX_HEIGHT, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, particle_texture); + + // Upload floor texture + glGenTextures(1, &floor_tex_id); + glBindTexture(GL_TEXTURE_2D, floor_tex_id); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, F_TEX_WIDTH, F_TEX_HEIGHT, + 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, floor_texture); + + if (glfwExtensionSupported("GL_EXT_separate_specular_color")) + { + glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, + GL_SEPARATE_SPECULAR_COLOR_EXT); + } + + // Set filled polygon mode as default (not wireframe) + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + wireframe = 0; + + // Set initial times + thread_sync.t = 0.0; + thread_sync.dt = 0.001f; + thread_sync.p_frame = 0; + thread_sync.d_frame = 0; + + mtx_init(&thread_sync.particles_lock, mtx_timed); + cnd_init(&thread_sync.p_done); + cnd_init(&thread_sync.d_done); + + if (thrd_create(&physics_thread, physics_thread_main, window) != thrd_success) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetTime(0.0); + + while (!glfwWindowShouldClose(window)) + { + draw_scene(window, glfwGetTime()); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + thrd_join(physics_thread, NULL); + + glfwDestroyWindow(window); + glfwTerminate(); + + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/examples/simple.c b/apps/exampleViewer/common/glfw/examples/simple.c new file mode 100644 index 0000000000..a961dab979 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/simple.c @@ -0,0 +1,163 @@ +//======================================================================== +// Simple GLFW example +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +//! [code] + +#include +#include + +#include "linmath.h" + +#include +#include + +static const struct +{ + float x, y; + float r, g, b; +} vertices[3] = +{ + { -0.6f, -0.4f, 1.f, 0.f, 0.f }, + { 0.6f, -0.4f, 0.f, 1.f, 0.f }, + { 0.f, 0.6f, 0.f, 0.f, 1.f } +}; + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec3 vCol;\n" +"attribute vec2 vPos;\n" +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +" color = vCol;\n" +"}\n"; + +static const char* fragment_shader_text = +"varying vec3 color;\n" +"void main()\n" +"{\n" +" gl_FragColor = vec4(color, 1.0);\n" +"}\n"; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +int main(void) +{ + GLFWwindow* window; + GLuint vertex_buffer, vertex_shader, fragment_shader, program; + GLint mvp_location, vpos_location, vcol_location; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(640, 480, "Simple example", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(window, key_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + // NOTE: OpenGL error checks have been omitted for brevity + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + vcol_location = glGetAttribLocation(program, "vCol"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + glEnableVertexAttribArray(vcol_location); + glVertexAttribPointer(vcol_location, 3, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) (sizeof(float) * 2)); + + while (!glfwWindowShouldClose(window)) + { + float ratio; + int width, height; + mat4x4 m, p, mvp; + + glfwGetFramebufferSize(window, &width, &height); + ratio = width / (float) height; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + mat4x4_identity(m); + mat4x4_rotate_Z(m, m, (float) glfwGetTime()); + mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 1.f, -1.f); + mat4x4_mul(mvp, p, m); + + glUseProgram(program); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDrawArrays(GL_TRIANGLES, 0, 3); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + +//! [code] diff --git a/apps/exampleViewer/common/glfw/examples/splitview.c b/apps/exampleViewer/common/glfw/examples/splitview.c new file mode 100644 index 0000000000..12f837d324 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/splitview.c @@ -0,0 +1,545 @@ +//======================================================================== +// This is an example program for the GLFW library +// +// The program uses a "split window" view, rendering four views of the +// same scene in one window (e.g. uesful for 3D modelling software). This +// demo uses scissors to separete the four different rendering areas from +// each other. +// +// (If the code seems a little bit strange here and there, it may be +// because I am not a friend of orthogonal projections) +//======================================================================== + +#include +#include + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include + +#include + + +//======================================================================== +// Global variables +//======================================================================== + +// Mouse position +static double xpos = 0, ypos = 0; + +// Window size +static int width, height; + +// Active view: 0 = none, 1 = upper left, 2 = upper right, 3 = lower left, +// 4 = lower right +static int active_view = 0; + +// Rotation around each axis +static int rot_x = 0, rot_y = 0, rot_z = 0; + +// Do redraw? +static int do_redraw = 1; + + +//======================================================================== +// Draw a solid torus (use a display list for the model) +//======================================================================== + +#define TORUS_MAJOR 1.5 +#define TORUS_MINOR 0.5 +#define TORUS_MAJOR_RES 32 +#define TORUS_MINOR_RES 32 + +static void drawTorus(void) +{ + static GLuint torus_list = 0; + int i, j, k; + double s, t, x, y, z, nx, ny, nz, scale, twopi; + + if (!torus_list) + { + // Start recording displaylist + torus_list = glGenLists(1); + glNewList(torus_list, GL_COMPILE_AND_EXECUTE); + + // Draw torus + twopi = 2.0 * M_PI; + for (i = 0; i < TORUS_MINOR_RES; i++) + { + glBegin(GL_QUAD_STRIP); + for (j = 0; j <= TORUS_MAJOR_RES; j++) + { + for (k = 1; k >= 0; k--) + { + s = (i + k) % TORUS_MINOR_RES + 0.5; + t = j % TORUS_MAJOR_RES; + + // Calculate point on surface + x = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * cos(t * twopi / TORUS_MAJOR_RES); + y = TORUS_MINOR * sin(s * twopi / TORUS_MINOR_RES); + z = (TORUS_MAJOR + TORUS_MINOR * cos(s * twopi / TORUS_MINOR_RES)) * sin(t * twopi / TORUS_MAJOR_RES); + + // Calculate surface normal + nx = x - TORUS_MAJOR * cos(t * twopi / TORUS_MAJOR_RES); + ny = y; + nz = z - TORUS_MAJOR * sin(t * twopi / TORUS_MAJOR_RES); + scale = 1.0 / sqrt(nx*nx + ny*ny + nz*nz); + nx *= scale; + ny *= scale; + nz *= scale; + + glNormal3f((float) nx, (float) ny, (float) nz); + glVertex3f((float) x, (float) y, (float) z); + } + } + + glEnd(); + } + + // Stop recording displaylist + glEndList(); + } + else + { + // Playback displaylist + glCallList(torus_list); + } +} + + +//======================================================================== +// Draw the scene (a rotating torus) +//======================================================================== + +static void drawScene(void) +{ + const GLfloat model_diffuse[4] = {1.0f, 0.8f, 0.8f, 1.0f}; + const GLfloat model_specular[4] = {0.6f, 0.6f, 0.6f, 1.0f}; + const GLfloat model_shininess = 20.0f; + + glPushMatrix(); + + // Rotate the object + glRotatef((GLfloat) rot_x * 0.5f, 1.0f, 0.0f, 0.0f); + glRotatef((GLfloat) rot_y * 0.5f, 0.0f, 1.0f, 0.0f); + glRotatef((GLfloat) rot_z * 0.5f, 0.0f, 0.0f, 1.0f); + + // Set model color (used for orthogonal views, lighting disabled) + glColor4fv(model_diffuse); + + // Set model material (used for perspective view, lighting enabled) + glMaterialfv(GL_FRONT, GL_DIFFUSE, model_diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, model_specular); + glMaterialf(GL_FRONT, GL_SHININESS, model_shininess); + + // Draw torus + drawTorus(); + + glPopMatrix(); +} + + +//======================================================================== +// Draw a 2D grid (used for orthogonal views) +//======================================================================== + +static void drawGrid(float scale, int steps) +{ + int i; + float x, y; + mat4x4 view; + + glPushMatrix(); + + // Set background to some dark bluish grey + glClearColor(0.05f, 0.05f, 0.2f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + // Setup modelview matrix (flat XY view) + { + vec3 eye = { 0.f, 0.f, 1.f }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, 1.f, 0.f }; + mat4x4_look_at(view, eye, center, up); + } + glLoadMatrixf((const GLfloat*) view); + + // We don't want to update the Z-buffer + glDepthMask(GL_FALSE); + + // Set grid color + glColor3f(0.0f, 0.5f, 0.5f); + + glBegin(GL_LINES); + + // Horizontal lines + x = scale * 0.5f * (float) (steps - 1); + y = -scale * 0.5f * (float) (steps - 1); + for (i = 0; i < steps; i++) + { + glVertex3f(-x, y, 0.0f); + glVertex3f(x, y, 0.0f); + y += scale; + } + + // Vertical lines + x = -scale * 0.5f * (float) (steps - 1); + y = scale * 0.5f * (float) (steps - 1); + for (i = 0; i < steps; i++) + { + glVertex3f(x, -y, 0.0f); + glVertex3f(x, y, 0.0f); + x += scale; + } + + glEnd(); + + // Enable Z-buffer writing again + glDepthMask(GL_TRUE); + + glPopMatrix(); +} + + +//======================================================================== +// Draw all views +//======================================================================== + +static void drawAllViews(void) +{ + const GLfloat light_position[4] = {0.0f, 8.0f, 8.0f, 1.0f}; + const GLfloat light_diffuse[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const GLfloat light_specular[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + const GLfloat light_ambient[4] = {0.2f, 0.2f, 0.3f, 1.0f}; + float aspect; + mat4x4 view, projection; + + // Calculate aspect of window + if (height > 0) + aspect = (float) width / (float) height; + else + aspect = 1.f; + + // Clear screen + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Enable scissor test + glEnable(GL_SCISSOR_TEST); + + // Enable depth test + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LEQUAL); + + // ** ORTHOGONAL VIEWS ** + + // For orthogonal views, use wireframe rendering + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + + // Enable line anti-aliasing + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + // Setup orthogonal projection matrix + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(-3.0 * aspect, 3.0 * aspect, -3.0, 3.0, 1.0, 50.0); + + // Upper left view (TOP VIEW) + glViewport(0, height / 2, width / 2, height / 2); + glScissor(0, height / 2, width / 2, height / 2); + glMatrixMode(GL_MODELVIEW); + { + vec3 eye = { 0.f, 10.f, 1e-3f }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, 1.f, 0.f }; + mat4x4_look_at( view, eye, center, up ); + } + glLoadMatrixf((const GLfloat*) view); + drawGrid(0.5, 12); + drawScene(); + + // Lower left view (FRONT VIEW) + glViewport(0, 0, width / 2, height / 2); + glScissor(0, 0, width / 2, height / 2); + glMatrixMode(GL_MODELVIEW); + { + vec3 eye = { 0.f, 0.f, 10.f }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, 1.f, 0.f }; + mat4x4_look_at( view, eye, center, up ); + } + glLoadMatrixf((const GLfloat*) view); + drawGrid(0.5, 12); + drawScene(); + + // Lower right view (SIDE VIEW) + glViewport(width / 2, 0, width / 2, height / 2); + glScissor(width / 2, 0, width / 2, height / 2); + glMatrixMode(GL_MODELVIEW); + { + vec3 eye = { 10.f, 0.f, 0.f }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, 1.f, 0.f }; + mat4x4_look_at( view, eye, center, up ); + } + glLoadMatrixf((const GLfloat*) view); + drawGrid(0.5, 12); + drawScene(); + + // Disable line anti-aliasing + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + + // ** PERSPECTIVE VIEW ** + + // For perspective view, use solid rendering + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + + // Enable face culling (faster rendering) + glEnable(GL_CULL_FACE); + glCullFace(GL_BACK); + glFrontFace(GL_CW); + + // Setup perspective projection matrix + glMatrixMode(GL_PROJECTION); + mat4x4_perspective(projection, + 65.f * (float) M_PI / 180.f, + aspect, + 1.f, 50.f); + glLoadMatrixf((const GLfloat*) projection); + + // Upper right view (PERSPECTIVE VIEW) + glViewport(width / 2, height / 2, width / 2, height / 2); + glScissor(width / 2, height / 2, width / 2, height / 2); + glMatrixMode(GL_MODELVIEW); + { + vec3 eye = { 3.f, 1.5f, 3.f }; + vec3 center = { 0.f, 0.f, 0.f }; + vec3 up = { 0.f, 1.f, 0.f }; + mat4x4_look_at( view, eye, center, up ); + } + glLoadMatrixf((const GLfloat*) view); + + // Configure and enable light source 1 + glLightfv(GL_LIGHT1, GL_POSITION, light_position); + glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient); + glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT1, GL_SPECULAR, light_specular); + glEnable(GL_LIGHT1); + glEnable(GL_LIGHTING); + + // Draw scene + drawScene(); + + // Disable lighting + glDisable(GL_LIGHTING); + + // Disable face culling + glDisable(GL_CULL_FACE); + + // Disable depth test + glDisable(GL_DEPTH_TEST); + + // Disable scissor test + glDisable(GL_SCISSOR_TEST); + + // Draw a border around the active view + if (active_view > 0 && active_view != 2) + { + glViewport(0, 0, width, height); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, 2.0, 0.0, 2.0, 0.0, 1.0); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glTranslatef((GLfloat) ((active_view - 1) & 1), (GLfloat) (1 - (active_view - 1) / 2), 0.0f); + + glColor3f(1.0f, 1.0f, 0.6f); + + glBegin(GL_LINE_STRIP); + glVertex2i(0, 0); + glVertex2i(1, 0); + glVertex2i(1, 1); + glVertex2i(0, 1); + glVertex2i(0, 0); + glEnd(); + } +} + + +//======================================================================== +// Framebuffer size callback function +//======================================================================== + +static void framebufferSizeFun(GLFWwindow* window, int w, int h) +{ + width = w; + height = h > 0 ? h : 1; + do_redraw = 1; +} + + +//======================================================================== +// Window refresh callback function +//======================================================================== + +static void windowRefreshFun(GLFWwindow* window) +{ + drawAllViews(); + glfwSwapBuffers(window); + do_redraw = 0; +} + + +//======================================================================== +// Mouse position callback function +//======================================================================== + +static void cursorPosFun(GLFWwindow* window, double x, double y) +{ + int wnd_width, wnd_height, fb_width, fb_height; + double scale; + + glfwGetWindowSize(window, &wnd_width, &wnd_height); + glfwGetFramebufferSize(window, &fb_width, &fb_height); + + scale = (double) fb_width / (double) wnd_width; + + x *= scale; + y *= scale; + + // Depending on which view was selected, rotate around different axes + switch (active_view) + { + case 1: + rot_x += (int) (y - ypos); + rot_z += (int) (x - xpos); + do_redraw = 1; + break; + case 3: + rot_x += (int) (y - ypos); + rot_y += (int) (x - xpos); + do_redraw = 1; + break; + case 4: + rot_y += (int) (x - xpos); + rot_z += (int) (y - ypos); + do_redraw = 1; + break; + default: + // Do nothing for perspective view, or if no view is selected + break; + } + + // Remember cursor position + xpos = x; + ypos = y; +} + + +//======================================================================== +// Mouse button callback function +//======================================================================== + +static void mouseButtonFun(GLFWwindow* window, int button, int action, int mods) +{ + if ((button == GLFW_MOUSE_BUTTON_LEFT) && action == GLFW_PRESS) + { + // Detect which of the four views was clicked + active_view = 1; + if (xpos >= width / 2) + active_view += 1; + if (ypos >= height / 2) + active_view += 2; + } + else if (button == GLFW_MOUSE_BUTTON_LEFT) + { + // Deselect any previously selected view + active_view = 0; + } + + do_redraw = 1; +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + + +//======================================================================== +// main +//======================================================================== + +int main(void) +{ + GLFWwindow* window; + + // Initialise GLFW + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + glfwWindowHint(GLFW_SAMPLES, 4); + + // Open OpenGL window + window = glfwCreateWindow(500, 500, "Split view demo", NULL, NULL); + if (!window) + { + fprintf(stderr, "Failed to open GLFW window\n"); + + glfwTerminate(); + exit(EXIT_FAILURE); + } + + // Set callback functions + glfwSetFramebufferSizeCallback(window, framebufferSizeFun); + glfwSetWindowRefreshCallback(window, windowRefreshFun); + glfwSetCursorPosCallback(window, cursorPosFun); + glfwSetMouseButtonCallback(window, mouseButtonFun); + glfwSetKeyCallback(window, key_callback); + + // Enable vsync + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + if (GLAD_GL_ARB_multisample || GLAD_GL_VERSION_1_3) + glEnable(GL_MULTISAMPLE_ARB); + + glfwGetFramebufferSize(window, &width, &height); + framebufferSizeFun(window, width, height); + + // Main loop + for (;;) + { + // Only redraw if we need to + if (do_redraw) + windowRefreshFun(window); + + // Wait for new events + glfwWaitEvents(); + + // Check if the window should be closed + if (glfwWindowShouldClose(window)) + break; + } + + // Close OpenGL window and terminate GLFW + glfwTerminate(); + + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/examples/wave.c b/apps/exampleViewer/common/glfw/examples/wave.c new file mode 100644 index 0000000000..c4c4d91849 --- /dev/null +++ b/apps/exampleViewer/common/glfw/examples/wave.c @@ -0,0 +1,460 @@ +/***************************************************************************** + * Wave Simulation in OpenGL + * (C) 2002 Jakob Thomsen + * http://home.in.tum.de/~thomsen + * Modified for GLFW by Sylvain Hellegouarch - sh@programmationworld.com + * Modified for variable frame rate by Marcus Geelnard + * 2003-Jan-31: Minor cleanups and speedups / MG + * 2010-10-24: Formatting and cleanup - Camilla Berglund + *****************************************************************************/ + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include + +#include +#include + +#include + +// Maximum delta T to allow for differential calculations +#define MAX_DELTA_T 0.01 + +// Animation speed (10.0 looks good) +#define ANIMATION_SPEED 10.0 + +GLfloat alpha = 210.f, beta = -70.f; +GLfloat zoom = 2.f; + +double cursorX; +double cursorY; + +struct Vertex +{ + GLfloat x, y, z; + GLfloat r, g, b; +}; + +#define GRIDW 50 +#define GRIDH 50 +#define VERTEXNUM (GRIDW*GRIDH) + +#define QUADW (GRIDW - 1) +#define QUADH (GRIDH - 1) +#define QUADNUM (QUADW*QUADH) + +GLuint quad[4 * QUADNUM]; +struct Vertex vertex[VERTEXNUM]; + +/* The grid will look like this: + * + * 3 4 5 + * *---*---* + * | | | + * | 0 | 1 | + * | | | + * *---*---* + * 0 1 2 + */ + +//======================================================================== +// Initialize grid geometry +//======================================================================== + +void init_vertices(void) +{ + int x, y, p; + + // Place the vertices in a grid + for (y = 0; y < GRIDH; y++) + { + for (x = 0; x < GRIDW; x++) + { + p = y * GRIDW + x; + + vertex[p].x = (GLfloat) (x - GRIDW / 2) / (GLfloat) (GRIDW / 2); + vertex[p].y = (GLfloat) (y - GRIDH / 2) / (GLfloat) (GRIDH / 2); + vertex[p].z = 0; + + if ((x % 4 < 2) ^ (y % 4 < 2)) + vertex[p].r = 0.0; + else + vertex[p].r = 1.0; + + vertex[p].g = (GLfloat) y / (GLfloat) GRIDH; + vertex[p].b = 1.f - ((GLfloat) x / (GLfloat) GRIDW + (GLfloat) y / (GLfloat) GRIDH) / 2.f; + } + } + + for (y = 0; y < QUADH; y++) + { + for (x = 0; x < QUADW; x++) + { + p = 4 * (y * QUADW + x); + + quad[p + 0] = y * GRIDW + x; // Some point + quad[p + 1] = y * GRIDW + x + 1; // Neighbor at the right side + quad[p + 2] = (y + 1) * GRIDW + x + 1; // Upper right neighbor + quad[p + 3] = (y + 1) * GRIDW + x; // Upper neighbor + } + } +} + +double dt; +double p[GRIDW][GRIDH]; +double vx[GRIDW][GRIDH], vy[GRIDW][GRIDH]; +double ax[GRIDW][GRIDH], ay[GRIDW][GRIDH]; + +//======================================================================== +// Initialize grid +//======================================================================== + +void init_grid(void) +{ + int x, y; + double dx, dy, d; + + for (y = 0; y < GRIDH; y++) + { + for (x = 0; x < GRIDW; x++) + { + dx = (double) (x - GRIDW / 2); + dy = (double) (y - GRIDH / 2); + d = sqrt(dx * dx + dy * dy); + if (d < 0.1 * (double) (GRIDW / 2)) + { + d = d * 10.0; + p[x][y] = -cos(d * (M_PI / (double)(GRIDW * 4))) * 100.0; + } + else + p[x][y] = 0.0; + + vx[x][y] = 0.0; + vy[x][y] = 0.0; + } + } +} + + +//======================================================================== +// Draw scene +//======================================================================== + +void draw_scene(GLFWwindow* window) +{ + // Clear the color and depth buffers + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // We don't want to modify the projection matrix + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + // Move back + glTranslatef(0.0, 0.0, -zoom); + // Rotate the view + glRotatef(beta, 1.0, 0.0, 0.0); + glRotatef(alpha, 0.0, 0.0, 1.0); + + glDrawElements(GL_QUADS, 4 * QUADNUM, GL_UNSIGNED_INT, quad); + + glfwSwapBuffers(window); +} + + +//======================================================================== +// Initialize Miscellaneous OpenGL state +//======================================================================== + +void init_opengl(void) +{ + // Use Gouraud (smooth) shading + glShadeModel(GL_SMOOTH); + + // Switch on the z-buffer + glEnable(GL_DEPTH_TEST); + + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glVertexPointer(3, GL_FLOAT, sizeof(struct Vertex), vertex); + glColorPointer(3, GL_FLOAT, sizeof(struct Vertex), &vertex[0].r); // Pointer to the first color + + glPointSize(2.0); + + // Background color is black + glClearColor(0, 0, 0, 0); +} + + +//======================================================================== +// Modify the height of each vertex according to the pressure +//======================================================================== + +void adjust_grid(void) +{ + int pos; + int x, y; + + for (y = 0; y < GRIDH; y++) + { + for (x = 0; x < GRIDW; x++) + { + pos = y * GRIDW + x; + vertex[pos].z = (float) (p[x][y] * (1.0 / 50.0)); + } + } +} + + +//======================================================================== +// Calculate wave propagation +//======================================================================== + +void calc_grid(void) +{ + int x, y, x2, y2; + double time_step = dt * ANIMATION_SPEED; + + // Compute accelerations + for (x = 0; x < GRIDW; x++) + { + x2 = (x + 1) % GRIDW; + for(y = 0; y < GRIDH; y++) + ax[x][y] = p[x][y] - p[x2][y]; + } + + for (y = 0; y < GRIDH; y++) + { + y2 = (y + 1) % GRIDH; + for(x = 0; x < GRIDW; x++) + ay[x][y] = p[x][y] - p[x][y2]; + } + + // Compute speeds + for (x = 0; x < GRIDW; x++) + { + for (y = 0; y < GRIDH; y++) + { + vx[x][y] = vx[x][y] + ax[x][y] * time_step; + vy[x][y] = vy[x][y] + ay[x][y] * time_step; + } + } + + // Compute pressure + for (x = 1; x < GRIDW; x++) + { + x2 = x - 1; + for (y = 1; y < GRIDH; y++) + { + y2 = y - 1; + p[x][y] = p[x][y] + (vx[x2][y] - vx[x][y] + vy[x][y2] - vy[x][y]) * time_step; + } + } +} + + +//======================================================================== +// Print errors +//======================================================================== + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + + +//======================================================================== +// Handle key strokes +//======================================================================== + +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_SPACE: + init_grid(); + break; + case GLFW_KEY_LEFT: + alpha += 5; + break; + case GLFW_KEY_RIGHT: + alpha -= 5; + break; + case GLFW_KEY_UP: + beta -= 5; + break; + case GLFW_KEY_DOWN: + beta += 5; + break; + case GLFW_KEY_PAGE_UP: + zoom -= 0.25f; + if (zoom < 0.f) + zoom = 0.f; + break; + case GLFW_KEY_PAGE_DOWN: + zoom += 0.25f; + break; + default: + break; + } +} + + +//======================================================================== +// Callback function for mouse button events +//======================================================================== + +void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + if (button != GLFW_MOUSE_BUTTON_LEFT) + return; + + if (action == GLFW_PRESS) + { + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + glfwGetCursorPos(window, &cursorX, &cursorY); + } + else + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); +} + + +//======================================================================== +// Callback function for cursor motion events +//======================================================================== + +void cursor_position_callback(GLFWwindow* window, double x, double y) +{ + if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) + { + alpha += (GLfloat) (x - cursorX) / 10.f; + beta += (GLfloat) (y - cursorY) / 10.f; + + cursorX = x; + cursorY = y; + } +} + + +//======================================================================== +// Callback function for scroll events +//======================================================================== + +void scroll_callback(GLFWwindow* window, double x, double y) +{ + zoom += (float) y / 4.f; + if (zoom < 0) + zoom = 0; +} + + +//======================================================================== +// Callback function for framebuffer resize events +//======================================================================== + +void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + float ratio = 1.f; + mat4x4 projection; + + if (height > 0) + ratio = (float) width / (float) height; + + // Setup viewport + glViewport(0, 0, width, height); + + // Change to the projection matrix and set our viewing volume + glMatrixMode(GL_PROJECTION); + mat4x4_perspective(projection, + 60.f * (float) M_PI / 180.f, + ratio, + 1.f, 1024.f); + glLoadMatrixf((const GLfloat*) projection); +} + + +//======================================================================== +// main +//======================================================================== + +int main(int argc, char* argv[]) +{ + GLFWwindow* window; + double t, dt_total, t_old; + int width, height; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = glfwCreateWindow(640, 480, "Wave Simulation", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(window, key_callback); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetMouseButtonCallback(window, mouse_button_callback); + glfwSetCursorPosCallback(window, cursor_position_callback); + glfwSetScrollCallback(window, scroll_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glfwGetFramebufferSize(window, &width, &height); + framebuffer_size_callback(window, width, height); + + // Initialize OpenGL + init_opengl(); + + // Initialize simulation + init_vertices(); + init_grid(); + adjust_grid(); + + // Initialize timer + t_old = glfwGetTime() - 0.01; + + while (!glfwWindowShouldClose(window)) + { + t = glfwGetTime(); + dt_total = t - t_old; + t_old = t; + + // Safety - iterate if dt_total is too large + while (dt_total > 0.f) + { + // Select iteration time step + dt = dt_total > MAX_DELTA_T ? MAX_DELTA_T : dt_total; + dt_total -= dt; + + // Calculate wave propagation + calc_grid(); + } + + // Compute height of each vertex + adjust_grid(); + + // Draw wave grid to OpenGL display + draw_scene(window); + + glfwPollEvents(); + } + + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/include/GLFW/glfw3.h b/apps/exampleViewer/common/glfw/include/GLFW/glfw3.h new file mode 100644 index 0000000000..73ebedd2cb --- /dev/null +++ b/apps/exampleViewer/common/glfw/include/GLFW/glfw3.h @@ -0,0 +1,4392 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2016 Camilla Berglund + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_h_ +#define _glfw3_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3.h + * @brief The header of the GLFW 3 API. + * + * This is the header file of the GLFW 3 API. It defines all its types and + * declares all its functions. + * + * For more information about how to use this file, see @ref build_include. + */ +/*! @defgroup context Context reference + * + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. + */ +/*! @defgroup vulkan Vulkan reference + * + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. + */ +/*! @defgroup init Initialization, version and error reference + * + * This is the reference documentation for initialization and termination of + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. + */ +/*! @defgroup input Input reference + * + * This is the reference documentation for input related functions and types. + * For more task-oriented information, see the @ref input_guide. + */ +/*! @defgroup monitor Monitor reference + * + * This is the reference documentation for monitor related functions and types. + * For more task-oriented information, see the @ref monitor_guide. + */ +/*! @defgroup window Window reference + * + * This is the reference documentation for window related functions and types, + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. + */ + + +/************************************************************************* + * Compiler- and platform-specific preprocessor work + *************************************************************************/ + +/* If we are we on Windows, we want a single define for it. + */ +#if !defined(_WIN32) && (defined(__WIN32__) || defined(WIN32) || defined(__MINGW32__)) + #define _WIN32 +#endif /* _WIN32 */ + +/* It is customary to use APIENTRY for OpenGL function pointer declarations on + * all platforms. Additionally, the Windows OpenGL header needs APIENTRY. + */ +#ifndef APIENTRY + #ifdef _WIN32 + #define APIENTRY __stdcall + #else + #define APIENTRY + #endif +#endif /* APIENTRY */ + +/* Some Windows OpenGL headers need this. + */ +#if !defined(WINGDIAPI) && defined(_WIN32) + #define WINGDIAPI __declspec(dllimport) + #define GLFW_WINGDIAPI_DEFINED +#endif /* WINGDIAPI */ + +/* Some Windows GLU headers need this. + */ +#if !defined(CALLBACK) && defined(_WIN32) + #define CALLBACK __stdcall + #define GLFW_CALLBACK_DEFINED +#endif /* CALLBACK */ + +/* Include because most Windows GLU headers need wchar_t and + * the macOS OpenGL header blocks the definition of ptrdiff_t by glext.h. + * Include it unconditionally to avoid surprising side-effects. + */ +#include + +/* Include because it is needed by Vulkan and related functions. + */ +#include + +/* Include the chosen client API headers. + */ +#if defined(__APPLE__) + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #elif !defined(GLFW_INCLUDE_NONE) + #if !defined(GLFW_INCLUDE_GLEXT) + #define GL_GLEXT_LEGACY + #endif + #include + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#else + #if defined(GLFW_INCLUDE_GLCOREARB) + #include + #elif defined(GLFW_INCLUDE_ES1) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #elif defined(GLFW_INCLUDE_ES2) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #elif defined(GLFW_INCLUDE_ES3) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #elif defined(GLFW_INCLUDE_ES31) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #elif !defined(GLFW_INCLUDE_NONE) + #include + #if defined(GLFW_INCLUDE_GLEXT) + #include + #endif + #endif + #if defined(GLFW_INCLUDE_GLU) + #include + #endif +#endif +#if defined(GLFW_INCLUDE_VULKAN) + #include +#endif + +#if defined(GLFW_DLL) && defined(_GLFW_BUILD_DLL) + /* GLFW_DLL must be defined by applications that are linking against the DLL + * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW + * configuration header when compiling the DLL version of the library. + */ + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" +#endif + +/* GLFWAPI is used to declare public API functions for export + * from the DLL / shared library / dynamic library. + */ +#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllexport) +#elif defined(_WIN32) && defined(GLFW_DLL) + /* We are calling GLFW as a Win32 DLL */ + #define GLFWAPI __declspec(dllimport) +#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) + /* We are building GLFW as a shared / dynamic library */ + #define GLFWAPI __attribute__((visibility("default"))) +#else + /* We are building or calling GLFW as a static library */ + #define GLFWAPI +#endif + + +/************************************************************************* + * GLFW API tokens + *************************************************************************/ + +/*! @name GLFW version macros + * @{ */ +/*! @brief The major version number of the GLFW library. + * + * This is incremented when the API is changed in non-compatible ways. + * @ingroup init + */ +#define GLFW_VERSION_MAJOR 3 +/*! @brief The minor version number of the GLFW library. + * + * This is incremented when features are added to the API but it remains + * backward-compatible. + * @ingroup init + */ +#define GLFW_VERSION_MINOR 3 +/*! @brief The revision number of the GLFW library. + * + * This is incremented when a bug fix release is made that does not contain any + * API changes. + * @ingroup init + */ +#define GLFW_VERSION_REVISION 0 +/*! @} */ + +/*! @name Boolean values + * @{ */ +/*! @brief One. + * + * One. Seriously. You don't _need_ to use this symbol in your code. It's + * just semantic sugar for the number 1. You can use `1` or `true` or `_True` + * or `GL_TRUE` or whatever you want. + */ +#define GLFW_TRUE 1 +/*! @brief Zero. + * + * Zero. Seriously. You don't _need_ to use this symbol in your code. It's + * just just semantic sugar for the number 0. You can use `0` or `false` or + * `_False` or `GL_FALSE` or whatever you want. + */ +#define GLFW_FALSE 0 +/*! @} */ + +/*! @name Key and button actions + * @{ */ +/*! @brief The key or mouse button was released. + * + * The key or mouse button was released. + * + * @ingroup input + */ +#define GLFW_RELEASE 0 +/*! @brief The key or mouse button was pressed. + * + * The key or mouse button was pressed. + * + * @ingroup input + */ +#define GLFW_PRESS 1 +/*! @brief The key was held down until it repeated. + * + * The key was held down until it repeated. + * + * @ingroup input + */ +#define GLFW_REPEAT 2 +/*! @} */ + +/*! @defgroup keys Keyboard keys + * + * See [key input](@ref input_key) for how these are used. + * + * These key codes are inspired by the _USB HID Usage Tables v1.12_ (p. 53-60), + * but re-arranged to map to 7-bit ASCII for printable keys (function keys are + * put in the 256+ range). + * + * The naming of the key codes follow these rules: + * - The US keyboard layout is used + * - Names of printable alpha-numeric characters are used (e.g. "A", "R", + * "3", etc.) + * - For non-alphanumeric characters, Unicode:ish names are used (e.g. + * "COMMA", "LEFT_SQUARE_BRACKET", etc.). Note that some names do not + * correspond to the Unicode standard (usually for brevity) + * - Keys that lack a clear US mapping are named "WORLD_x" + * - For non-printable keys, custom names are used (e.g. "F4", + * "BACKSPACE", etc.) + * + * @ingroup input + * @{ + */ + +/* The unknown key */ +#define GLFW_KEY_UNKNOWN -1 + +/* Printable keys */ +#define GLFW_KEY_SPACE 32 +#define GLFW_KEY_APOSTROPHE 39 /* ' */ +#define GLFW_KEY_COMMA 44 /* , */ +#define GLFW_KEY_MINUS 45 /* - */ +#define GLFW_KEY_PERIOD 46 /* . */ +#define GLFW_KEY_SLASH 47 /* / */ +#define GLFW_KEY_0 48 +#define GLFW_KEY_1 49 +#define GLFW_KEY_2 50 +#define GLFW_KEY_3 51 +#define GLFW_KEY_4 52 +#define GLFW_KEY_5 53 +#define GLFW_KEY_6 54 +#define GLFW_KEY_7 55 +#define GLFW_KEY_8 56 +#define GLFW_KEY_9 57 +#define GLFW_KEY_SEMICOLON 59 /* ; */ +#define GLFW_KEY_EQUAL 61 /* = */ +#define GLFW_KEY_A 65 +#define GLFW_KEY_B 66 +#define GLFW_KEY_C 67 +#define GLFW_KEY_D 68 +#define GLFW_KEY_E 69 +#define GLFW_KEY_F 70 +#define GLFW_KEY_G 71 +#define GLFW_KEY_H 72 +#define GLFW_KEY_I 73 +#define GLFW_KEY_J 74 +#define GLFW_KEY_K 75 +#define GLFW_KEY_L 76 +#define GLFW_KEY_M 77 +#define GLFW_KEY_N 78 +#define GLFW_KEY_O 79 +#define GLFW_KEY_P 80 +#define GLFW_KEY_Q 81 +#define GLFW_KEY_R 82 +#define GLFW_KEY_S 83 +#define GLFW_KEY_T 84 +#define GLFW_KEY_U 85 +#define GLFW_KEY_V 86 +#define GLFW_KEY_W 87 +#define GLFW_KEY_X 88 +#define GLFW_KEY_Y 89 +#define GLFW_KEY_Z 90 +#define GLFW_KEY_LEFT_BRACKET 91 /* [ */ +#define GLFW_KEY_BACKSLASH 92 /* \ */ +#define GLFW_KEY_RIGHT_BRACKET 93 /* ] */ +#define GLFW_KEY_GRAVE_ACCENT 96 /* ` */ +#define GLFW_KEY_WORLD_1 161 /* non-US #1 */ +#define GLFW_KEY_WORLD_2 162 /* non-US #2 */ + +/* Function keys */ +#define GLFW_KEY_ESCAPE 256 +#define GLFW_KEY_ENTER 257 +#define GLFW_KEY_TAB 258 +#define GLFW_KEY_BACKSPACE 259 +#define GLFW_KEY_INSERT 260 +#define GLFW_KEY_DELETE 261 +#define GLFW_KEY_RIGHT 262 +#define GLFW_KEY_LEFT 263 +#define GLFW_KEY_DOWN 264 +#define GLFW_KEY_UP 265 +#define GLFW_KEY_PAGE_UP 266 +#define GLFW_KEY_PAGE_DOWN 267 +#define GLFW_KEY_HOME 268 +#define GLFW_KEY_END 269 +#define GLFW_KEY_CAPS_LOCK 280 +#define GLFW_KEY_SCROLL_LOCK 281 +#define GLFW_KEY_NUM_LOCK 282 +#define GLFW_KEY_PRINT_SCREEN 283 +#define GLFW_KEY_PAUSE 284 +#define GLFW_KEY_F1 290 +#define GLFW_KEY_F2 291 +#define GLFW_KEY_F3 292 +#define GLFW_KEY_F4 293 +#define GLFW_KEY_F5 294 +#define GLFW_KEY_F6 295 +#define GLFW_KEY_F7 296 +#define GLFW_KEY_F8 297 +#define GLFW_KEY_F9 298 +#define GLFW_KEY_F10 299 +#define GLFW_KEY_F11 300 +#define GLFW_KEY_F12 301 +#define GLFW_KEY_F13 302 +#define GLFW_KEY_F14 303 +#define GLFW_KEY_F15 304 +#define GLFW_KEY_F16 305 +#define GLFW_KEY_F17 306 +#define GLFW_KEY_F18 307 +#define GLFW_KEY_F19 308 +#define GLFW_KEY_F20 309 +#define GLFW_KEY_F21 310 +#define GLFW_KEY_F22 311 +#define GLFW_KEY_F23 312 +#define GLFW_KEY_F24 313 +#define GLFW_KEY_F25 314 +#define GLFW_KEY_KP_0 320 +#define GLFW_KEY_KP_1 321 +#define GLFW_KEY_KP_2 322 +#define GLFW_KEY_KP_3 323 +#define GLFW_KEY_KP_4 324 +#define GLFW_KEY_KP_5 325 +#define GLFW_KEY_KP_6 326 +#define GLFW_KEY_KP_7 327 +#define GLFW_KEY_KP_8 328 +#define GLFW_KEY_KP_9 329 +#define GLFW_KEY_KP_DECIMAL 330 +#define GLFW_KEY_KP_DIVIDE 331 +#define GLFW_KEY_KP_MULTIPLY 332 +#define GLFW_KEY_KP_SUBTRACT 333 +#define GLFW_KEY_KP_ADD 334 +#define GLFW_KEY_KP_ENTER 335 +#define GLFW_KEY_KP_EQUAL 336 +#define GLFW_KEY_LEFT_SHIFT 340 +#define GLFW_KEY_LEFT_CONTROL 341 +#define GLFW_KEY_LEFT_ALT 342 +#define GLFW_KEY_LEFT_SUPER 343 +#define GLFW_KEY_RIGHT_SHIFT 344 +#define GLFW_KEY_RIGHT_CONTROL 345 +#define GLFW_KEY_RIGHT_ALT 346 +#define GLFW_KEY_RIGHT_SUPER 347 +#define GLFW_KEY_MENU 348 + +#define GLFW_KEY_LAST GLFW_KEY_MENU + +/*! @} */ + +/*! @defgroup mods Modifier key flags + * + * See [key input](@ref input_key) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief If this bit is set one or more Shift keys were held down. + */ +#define GLFW_MOD_SHIFT 0x0001 +/*! @brief If this bit is set one or more Control keys were held down. + */ +#define GLFW_MOD_CONTROL 0x0002 +/*! @brief If this bit is set one or more Alt keys were held down. + */ +#define GLFW_MOD_ALT 0x0004 +/*! @brief If this bit is set one or more Super keys were held down. + */ +#define GLFW_MOD_SUPER 0x0008 + +/*! @} */ + +/*! @defgroup buttons Mouse buttons + * + * See [mouse button input](@ref input_mouse_button) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_MOUSE_BUTTON_1 0 +#define GLFW_MOUSE_BUTTON_2 1 +#define GLFW_MOUSE_BUTTON_3 2 +#define GLFW_MOUSE_BUTTON_4 3 +#define GLFW_MOUSE_BUTTON_5 4 +#define GLFW_MOUSE_BUTTON_6 5 +#define GLFW_MOUSE_BUTTON_7 6 +#define GLFW_MOUSE_BUTTON_8 7 +#define GLFW_MOUSE_BUTTON_LAST GLFW_MOUSE_BUTTON_8 +#define GLFW_MOUSE_BUTTON_LEFT GLFW_MOUSE_BUTTON_1 +#define GLFW_MOUSE_BUTTON_RIGHT GLFW_MOUSE_BUTTON_2 +#define GLFW_MOUSE_BUTTON_MIDDLE GLFW_MOUSE_BUTTON_3 +/*! @} */ + +/*! @defgroup joysticks Joysticks + * + * See [joystick input](@ref joystick) for how these are used. + * + * @ingroup input + * @{ */ +#define GLFW_JOYSTICK_1 0 +#define GLFW_JOYSTICK_2 1 +#define GLFW_JOYSTICK_3 2 +#define GLFW_JOYSTICK_4 3 +#define GLFW_JOYSTICK_5 4 +#define GLFW_JOYSTICK_6 5 +#define GLFW_JOYSTICK_7 6 +#define GLFW_JOYSTICK_8 7 +#define GLFW_JOYSTICK_9 8 +#define GLFW_JOYSTICK_10 9 +#define GLFW_JOYSTICK_11 10 +#define GLFW_JOYSTICK_12 11 +#define GLFW_JOYSTICK_13 12 +#define GLFW_JOYSTICK_14 13 +#define GLFW_JOYSTICK_15 14 +#define GLFW_JOYSTICK_16 15 +#define GLFW_JOYSTICK_LAST GLFW_JOYSTICK_16 +/*! @} */ + +/*! @defgroup errors Error codes + * + * See [error handling](@ref error_handling) for how these are used. + * + * @ingroup init + * @{ */ +/*! @brief GLFW has not been initialized. + * + * This occurs if a GLFW function was called that must not be called unless the + * library is [initialized](@ref intro_init). + * + * @analysis Application programmer error. Initialize GLFW before calling any + * function that requires initialization. + */ +#define GLFW_NOT_INITIALIZED 0x00010001 +/*! @brief No context is current for this thread. + * + * This occurs if a GLFW function was called that needs and operates on the + * current OpenGL or OpenGL ES context but no context is current on the calling + * thread. One such function is @ref glfwSwapInterval. + * + * @analysis Application programmer error. Ensure a context is current before + * calling functions that require a current context. + */ +#define GLFW_NO_CURRENT_CONTEXT 0x00010002 +/*! @brief One of the arguments to the function was an invalid enum value. + * + * One of the arguments to the function was an invalid enum value, for example + * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref + * glfwGetWindowAttrib. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_ENUM 0x00010003 +/*! @brief One of the arguments to the function was an invalid value. + * + * One of the arguments to the function was an invalid value, for example + * requesting a non-existent OpenGL or OpenGL ES version like 2.7. + * + * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead + * result in a @ref GLFW_VERSION_UNAVAILABLE error. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_INVALID_VALUE 0x00010004 +/*! @brief A memory allocation failed. + * + * A memory allocation failed. + * + * @analysis A bug in GLFW or the underlying operating system. Report the bug + * to our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_OUT_OF_MEMORY 0x00010005 +/*! @brief GLFW could not find support for the requested API on the system. + * + * GLFW could not find support for the requested API on the system. + * + * @analysis The installed graphics driver does not support the requested + * API, or does not support it via the chosen context creation backend. + * Below are a few examples. + * + * @par + * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only + * supports OpenGL ES via EGL, while Nvidia and Intel only support it via + * a WGL or GLX extension. macOS does not provide OpenGL ES at all. The Mesa + * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary + * driver. Older graphics drivers do not support Vulkan. + */ +#define GLFW_API_UNAVAILABLE 0x00010006 +/*! @brief The requested OpenGL or OpenGL ES version is not available. + * + * The requested OpenGL or OpenGL ES version (including any requested context + * or framebuffer hints) is not available on this machine. + * + * @analysis The machine does not support your requirements. If your + * application is sufficiently flexible, downgrade your requirements and try + * again. Otherwise, inform the user that their machine does not match your + * requirements. + * + * @par + * Future invalid OpenGL and OpenGL ES versions, for example OpenGL 4.8 if 5.0 + * comes out before the 4.x series gets that far, also fail with this error and + * not @ref GLFW_INVALID_VALUE, because GLFW cannot know what future versions + * will exist. + */ +#define GLFW_VERSION_UNAVAILABLE 0x00010007 +/*! @brief A platform-specific error occurred that does not match any of the + * more specific categories. + * + * A platform-specific error occurred that does not match any of the more + * specific categories. + * + * @analysis A bug or configuration error in GLFW, the underlying operating + * system or its drivers, or a lack of required resources. Report the issue to + * our [issue tracker](https://github.com/glfw/glfw/issues). + */ +#define GLFW_PLATFORM_ERROR 0x00010008 +/*! @brief The requested format is not supported or available. + * + * If emitted during window creation, the requested pixel format is not + * supported. + * + * If emitted when querying the clipboard, the contents of the clipboard could + * not be converted to the requested format. + * + * @analysis If emitted during window creation, one or more + * [hard constraints](@ref window_hints_hard) did not match any of the + * available pixel formats. If your application is sufficiently flexible, + * downgrade your requirements and try again. Otherwise, inform the user that + * their machine does not match your requirements. + * + * @par + * If emitted when querying the clipboard, ignore the error or report it to + * the user, as appropriate. + */ +#define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @brief The specified window does not have an OpenGL or OpenGL ES context. + * + * A window that does not have an OpenGL or OpenGL ES context was passed to + * a function that requires it to have one. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_NO_WINDOW_CONTEXT 0x0001000A +/*! @} */ + +#define GLFW_FOCUSED 0x00020001 +#define GLFW_ICONIFIED 0x00020002 +#define GLFW_RESIZABLE 0x00020003 +#define GLFW_VISIBLE 0x00020004 +#define GLFW_DECORATED 0x00020005 +#define GLFW_AUTO_ICONIFY 0x00020006 +#define GLFW_FLOATING 0x00020007 +#define GLFW_MAXIMIZED 0x00020008 + +#define GLFW_RED_BITS 0x00021001 +#define GLFW_GREEN_BITS 0x00021002 +#define GLFW_BLUE_BITS 0x00021003 +#define GLFW_ALPHA_BITS 0x00021004 +#define GLFW_DEPTH_BITS 0x00021005 +#define GLFW_STENCIL_BITS 0x00021006 +#define GLFW_ACCUM_RED_BITS 0x00021007 +#define GLFW_ACCUM_GREEN_BITS 0x00021008 +#define GLFW_ACCUM_BLUE_BITS 0x00021009 +#define GLFW_ACCUM_ALPHA_BITS 0x0002100A +#define GLFW_AUX_BUFFERS 0x0002100B +#define GLFW_STEREO 0x0002100C +#define GLFW_SAMPLES 0x0002100D +#define GLFW_SRGB_CAPABLE 0x0002100E +#define GLFW_REFRESH_RATE 0x0002100F +#define GLFW_DOUBLEBUFFER 0x00021010 + +#define GLFW_CLIENT_API 0x00022001 +#define GLFW_CONTEXT_VERSION_MAJOR 0x00022002 +#define GLFW_CONTEXT_VERSION_MINOR 0x00022003 +#define GLFW_CONTEXT_REVISION 0x00022004 +#define GLFW_CONTEXT_ROBUSTNESS 0x00022005 +#define GLFW_OPENGL_FORWARD_COMPAT 0x00022006 +#define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 +#define GLFW_OPENGL_PROFILE 0x00022008 +#define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +#define GLFW_CONTEXT_NO_ERROR 0x0002200A +#define GLFW_CONTEXT_CREATION_API 0x0002200B + +#define GLFW_NO_API 0 +#define GLFW_OPENGL_API 0x00030001 +#define GLFW_OPENGL_ES_API 0x00030002 + +#define GLFW_NO_ROBUSTNESS 0 +#define GLFW_NO_RESET_NOTIFICATION 0x00031001 +#define GLFW_LOSE_CONTEXT_ON_RESET 0x00031002 + +#define GLFW_OPENGL_ANY_PROFILE 0 +#define GLFW_OPENGL_CORE_PROFILE 0x00032001 +#define GLFW_OPENGL_COMPAT_PROFILE 0x00032002 + +#define GLFW_CURSOR 0x00033001 +#define GLFW_STICKY_KEYS 0x00033002 +#define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 + +#define GLFW_CURSOR_NORMAL 0x00034001 +#define GLFW_CURSOR_HIDDEN 0x00034002 +#define GLFW_CURSOR_DISABLED 0x00034003 + +#define GLFW_ANY_RELEASE_BEHAVIOR 0 +#define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 +#define GLFW_RELEASE_BEHAVIOR_NONE 0x00035002 + +#define GLFW_NATIVE_CONTEXT_API 0x00036001 +#define GLFW_EGL_CONTEXT_API 0x00036002 + +/*! @defgroup shapes Standard cursor shapes + * + * See [standard cursor creation](@ref cursor_standard) for how these are used. + * + * @ingroup input + * @{ */ + +/*! @brief The regular arrow cursor shape. + * + * The regular arrow cursor. + */ +#define GLFW_ARROW_CURSOR 0x00036001 +/*! @brief The text input I-beam cursor shape. + * + * The text input I-beam cursor shape. + */ +#define GLFW_IBEAM_CURSOR 0x00036002 +/*! @brief The crosshair shape. + * + * The crosshair shape. + */ +#define GLFW_CROSSHAIR_CURSOR 0x00036003 +/*! @brief The hand shape. + * + * The hand shape. + */ +#define GLFW_HAND_CURSOR 0x00036004 +/*! @brief The horizontal resize arrow shape. + * + * The horizontal resize arrow shape. + */ +#define GLFW_HRESIZE_CURSOR 0x00036005 +/*! @brief The vertical resize arrow shape. + * + * The vertical resize arrow shape. + */ +#define GLFW_VRESIZE_CURSOR 0x00036006 +/*! @} */ + +#define GLFW_CONNECTED 0x00040001 +#define GLFW_DISCONNECTED 0x00040002 + +#define GLFW_DONT_CARE -1 + + +/************************************************************************* + * GLFW API types + *************************************************************************/ + +/*! @brief Client API function pointer type. + * + * Generic function pointer used for returning client API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 3.0. + + * @ingroup context + */ +typedef void (*GLFWglproc)(void); + +/*! @brief Vulkan API function pointer type. + * + * Generic function pointer used for returning Vulkan API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref vulkan_proc + * @sa @ref glfwGetInstanceProcAddress + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +typedef void (*GLFWvkproc)(void); + +/*! @brief Opaque monitor object. + * + * Opaque monitor object. + * + * @see @ref monitor_object + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWmonitor GLFWmonitor; + +/*! @brief Opaque window object. + * + * Opaque window object. + * + * @see @ref window_object + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef struct GLFWwindow GLFWwindow; + +/*! @brief Opaque cursor object. + * + * Opaque cursor object. + * + * @see @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup cursor + */ +typedef struct GLFWcursor GLFWcursor; + +/*! @brief The function signature for error callbacks. + * + * This is the function signature for error callback functions. + * + * @param[in] error An [error code](@ref errors). + * @param[in] description A UTF-8 encoded string describing the error. + * + * @sa @ref error_handling + * @sa @ref glfwSetErrorCallback + * + * @since Added in version 3.0. + * + * @ingroup init + */ +typedef void (* GLFWerrorfun)(int,const char*); + +/*! @brief The function signature for window position callbacks. + * + * This is the function signature for window position callback functions. + * + * @param[in] window The window that was moved. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the + * upper-left corner of the client area of the window. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPosCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window resize callbacks. + * + * This is the function signature for window size callback functions. + * + * @param[in] window The window that was resized. + * @param[in] width The new width, in screen coordinates, of the window. + * @param[in] height The new height, in screen coordinates, of the window. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSizeCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for window close callbacks. + * + * This is the function signature for window close callback functions. + * + * @param[in] window The window that the user attempted to close. + * + * @sa @ref window_close + * @sa @ref glfwSetWindowCloseCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowclosefun)(GLFWwindow*); + +/*! @brief The function signature for window content refresh callbacks. + * + * This is the function signature for window refresh callback functions. + * + * @param[in] window The window whose content needs to be refreshed. + * + * @sa @ref window_refresh + * @sa @ref glfwSetWindowRefreshCallback + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); + +/*! @brief The function signature for window focus/defocus callbacks. + * + * This is the function signature for window focus callback functions. + * + * @param[in] window The window that gained or lost input focus. + * @param[in] focused `GLFW_TRUE` if the window was given input focus, or + * `GLFW_FALSE` if it lost it. + * + * @sa @ref window_focus + * @sa @ref glfwSetWindowFocusCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); + +/*! @brief The function signature for window iconify/restore callbacks. + * + * This is the function signature for window iconify/restore callback + * functions. + * + * @param[in] window The window that was iconified or restored. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_iconify + * @sa @ref glfwSetWindowIconifyCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); + +/*! @brief The function signature for window maximize/restore callbacks. + * + * This is the function signature for window maximize/restore callback + * functions. + * + * @param[in] window The window that was maximized or restored. + * @param[in] iconified `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * + * @sa @ref window_maximize + * @sa glfwSetWindowMaximizeCallback + * + * @since Added in version 3.3. + * + * @ingroup window + */ +typedef void (* GLFWwindowmaximizefun)(GLFWwindow*,int); + +/*! @brief The function signature for framebuffer resize callbacks. + * + * This is the function signature for framebuffer resize callback + * functions. + * + * @param[in] window The window whose framebuffer was resized. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); + +/*! @brief The function signature for mouse button callbacks. + * + * This is the function signature for mouse button callback functions. + * + * @param[in] window The window that received the event. + * @param[in] button The [mouse button](@ref buttons) that was pressed or + * released. + * @param[in] action One of `GLFW_PRESS` or `GLFW_RELEASE`. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_mouse_button + * @sa @ref glfwSetMouseButtonCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); + +/*! @brief The function signature for cursor position callbacks. + * + * This is the function signature for cursor position callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the client area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * client area. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPosCallback + * + * @since Added in version 3.0. Replaces `GLFWmouseposfun`. + * + * @ingroup input + */ +typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for cursor enter/leave callbacks. + * + * This is the function signature for cursor enter/leave callback functions. + * + * @param[in] window The window that received the event. + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * area, or `GLFW_FALSE` if it left it. + * + * @sa @ref cursor_enter + * @sa @ref glfwSetCursorEnterCallback + * + * @since Added in version 3.0. + * + * @ingroup input + */ +typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); + +/*! @brief The function signature for scroll callbacks. + * + * This is the function signature for scroll callback functions. + * + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * + * @sa @ref scrolling + * @sa @ref glfwSetScrollCallback + * + * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. + * + * @ingroup input + */ +typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); + +/*! @brief The function signature for keyboard key callbacks. + * + * This is the function signature for keyboard key callback functions. + * + * @param[in] window The window that received the event. + * @param[in] key The [keyboard key](@ref keys) that was pressed or released. + * @param[in] scancode The system-specific scancode of the key. + * @param[in] action `GLFW_PRESS`, `GLFW_RELEASE` or `GLFW_REPEAT`. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_key + * @sa @ref glfwSetKeyCallback + * + * @since Added in version 1.0. + * @glfw3 Added window handle, scancode and modifier mask parameters. + * + * @ingroup input + */ +typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); + +/*! @brief The function signature for Unicode character callbacks. + * + * This is the function signature for Unicode character callback functions. + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * + * @sa @ref input_char + * @sa @ref glfwSetCharCallback + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); + +/*! @brief The function signature for Unicode character with modifiers + * callbacks. + * + * This is the function signature for Unicode character with modifiers callback + * functions. It is called for each input character, regardless of what + * modifier keys are held down. + * + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the character. + * @param[in] mods Bit field describing which [modifier keys](@ref mods) were + * held down. + * + * @sa @ref input_char + * @sa @ref glfwSetCharModsCallback + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); + +/*! @brief The function signature for file drop callbacks. + * + * This is the function signature for file drop callbacks. + * + * @param[in] window The window that received the event. + * @param[in] count The number of dropped files. + * @param[in] paths The UTF-8 encoded file and/or directory path names. + * + * @sa @ref path_drop + * @sa @ref glfwSetDropCallback + * + * @since Added in version 3.1. + * + * @ingroup input + */ +typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); + +/*! @brief The function signature for monitor configuration callbacks. + * + * This is the function signature for monitor configuration callback functions. + * + * @param[in] monitor The monitor that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * + * @sa @ref monitor_event + * @sa @ref glfwSetMonitorCallback + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); + +/*! @brief The function signature for joystick configuration callbacks. + * + * This is the function signature for joystick configuration callback + * functions. + * + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * + * @sa @ref joystick_event + * @sa @ref glfwSetJoystickCallback + * + * @since Added in version 3.2. + * + * @ingroup input + */ +typedef void (* GLFWjoystickfun)(int,int); + +/*! @brief Video mode type. + * + * This describes a single video mode. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * @sa @ref glfwGetVideoModes + * + * @since Added in version 1.0. + * @glfw3 Added refresh rate member. + * + * @ingroup monitor + */ +typedef struct GLFWvidmode +{ + /*! The width, in screen coordinates, of the video mode. + */ + int width; + /*! The height, in screen coordinates, of the video mode. + */ + int height; + /*! The bit depth of the red channel of the video mode. + */ + int redBits; + /*! The bit depth of the green channel of the video mode. + */ + int greenBits; + /*! The bit depth of the blue channel of the video mode. + */ + int blueBits; + /*! The refresh rate, in Hz, of the video mode. + */ + int refreshRate; +} GLFWvidmode; + +/*! @brief Gamma ramp. + * + * This describes the gamma ramp for a monitor. + * + * @sa @ref monitor_gamma + * @sa @ref glfwGetGammaRamp + * @sa @ref glfwSetGammaRamp + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +typedef struct GLFWgammaramp +{ + /*! An array of value describing the response of the red channel. + */ + unsigned short* red; + /*! An array of value describing the response of the green channel. + */ + unsigned short* green; + /*! An array of value describing the response of the blue channel. + */ + unsigned short* blue; + /*! The number of elements in each array. + */ + unsigned int size; +} GLFWgammaramp; + +/*! @brief Image data. + * + * @sa @ref cursor_custom + * @sa @ref window_icon + * + * @since Added in version 2.1. + * @glfw3 Removed format and bytes-per-pixel members. + */ +typedef struct GLFWimage +{ + /*! The width, in pixels, of this image. + */ + int width; + /*! The height, in pixels, of this image. + */ + int height; + /*! The pixel data of this image, arranged left-to-right, top-to-bottom. + */ + unsigned char* pixels; +} GLFWimage; + + +/************************************************************************* + * GLFW API functions + *************************************************************************/ + +/*! @brief Initializes the GLFW library. + * + * This function initializes the GLFW library. Before most GLFW functions can + * be used, GLFW must be initialized, and before an application terminates GLFW + * should be terminated in order to free any resources allocated during or + * after initialization. + * + * If this function fails, it calls @ref glfwTerminate before returning. If it + * succeeds, you should call @ref glfwTerminate before the application exits. + * + * Additional calls to this function after successful initialization but before + * termination will return `GLFW_TRUE` immediately. + * + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark @macos This function will change the current directory of the + * application to the `Contents/Resources` subdirectory of the application's + * bundle, if present. This can be disabled with a + * [compile-time option](@ref compile_options_osx). + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwTerminate + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI int glfwInit(void); + +/*! @brief Terminates the GLFW library. + * + * This function destroys all remaining windows and cursors, restores any + * modified gamma ramps and frees any other allocated resources. Once this + * function is called, you must again call @ref glfwInit successfully before + * you will be able to use most GLFW functions. + * + * If GLFW has been successfully initialized, this function should be called + * before the application exits. If initialization fails, there is no need to + * call this function, as it is called by @ref glfwInit before it returns + * failure. + * + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark This function may be called before @ref glfwInit. + * + * @warning The contexts of any remaining windows must not be current on any + * other thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref intro_init + * @sa @ref glfwInit + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwTerminate(void); + +/*! @brief Retrieves the version of the GLFW library. + * + * This function retrieves the major, minor and revision numbers of the GLFW + * library. It is intended for when you are using GLFW as a shared library and + * want to ensure that you are using the minimum required version. + * + * Any or all of the version arguments may be `NULL`. + * + * @param[out] major Where to store the major version number, or `NULL`. + * @param[out] minor Where to store the minor version number, or `NULL`. + * @param[out] rev Where to store the revision number, or `NULL`. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersionString + * + * @since Added in version 1.0. + * + * @ingroup init + */ +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); + +/*! @brief Returns a string describing the compile-time configuration. + * + * This function returns the compile-time generated + * [version string](@ref intro_version_string) of the GLFW library binary. It + * describes the version, platform, compiler and any platform-specific + * compile-time options. It should not be confused with the OpenGL or OpenGL + * ES version string, queried with `glGetString`. + * + * __Do not use the version string__ to parse the GLFW library version. The + * @ref glfwGetVersion function provides the version of the running library + * binary in numerical format. + * + * @return The ASCII encoded GLFW version string. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @pointer_lifetime The returned string is static and compile-time generated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref intro_version + * @sa @ref glfwGetVersion + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI const char* glfwGetVersionString(void); + +/*! @brief Sets the error callback. + * + * This function sets the error callback, which is called with an error code + * and a human-readable description each time a GLFW error occurs. + * + * The error callback is called on the thread where the error occurred. If you + * are using GLFW from multiple threads, your error callback needs to be + * written accordingly. + * + * Because the description string may have been generated specifically for that + * error, it is not guaranteed to be valid after the callback has returned. If + * you wish to use it after the callback returns, you need to make a copy. + * + * Once set, the error callback remains set even after the library has been + * terminated. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set. + * + * @errors None. + * + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref error_handling + * + * @since Added in version 3.0. + * + * @ingroup init + */ +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); + +/*! @brief Returns the currently connected monitors. + * + * This function returns an array of handles for all currently connected + * monitors. The primary monitor is always first in the returned array. If no + * monitors were found, this function returns `NULL`. + * + * @param[out] count Where to store the number of monitors in the returned + * array. This is set to zero if an error occurred. + * @return An array of monitor handles, or `NULL` if no monitors were found or + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * monitor configuration changes or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_monitors + * @sa @ref monitor_event + * @sa @ref glfwGetPrimaryMonitor + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); + +/*! @brief Returns the primary monitor. + * + * This function returns the primary monitor. This is usually the monitor + * where elements like the task bar or global menu bar are located. + * + * @return The primary monitor, or `NULL` if no monitors were found or if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @remark The primary monitor is always first in the array returned by @ref + * glfwGetMonitors. + * + * @sa @ref monitor_monitors + * @sa @ref glfwGetMonitors + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); + +/*! @brief Returns the position of the monitor's viewport on the virtual screen. + * + * This function returns the position, in screen coordinates, of the upper-left + * corner of the specified monitor. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. + * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @brief Returns the physical size of the monitor. + * + * This function returns the size, in millimetres, of the display area of the + * specified monitor. + * + * Some systems do not provide accurate monitor size information, either + * because the monitor + * [EDID](https://en.wikipedia.org/wiki/Extended_display_identification_data) + * data is incorrect or because the driver does not report it accurately. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] monitor The monitor to query. + * @param[out] widthMM Where to store the width, in millimetres, of the + * monitor's display area, or `NULL`. + * @param[out] heightMM Where to store the height, in millimetres, of the + * monitor's display area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @win32 calculates the returned physical size from the + * current resolution and system DPI instead of querying the monitor EDID data. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* heightMM); + +/*! @brief Returns the name of the specified monitor. + * + * This function returns a human-readable name, encoded as UTF-8, of the + * specified monitor. The name typically reflects the make and model of the + * monitor and is not guaranteed to be unique among the connected monitors. + * + * @param[in] monitor The monitor to query. + * @return The UTF-8 encoded name of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_properties + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); + +/*! @brief Sets the monitor configuration callback. + * + * This function sets the monitor configuration callback, or removes the + * currently set callback. This is called when a monitor is connected to or + * disconnected from the system. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_event + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); + +/*! @brief Returns the available video modes for the specified monitor. + * + * This function returns an array of all video modes supported by the specified + * monitor. The returned array is sorted in ascending order, first by color + * bit depth (the sum of all channel depths) and then by resolution area (the + * product of width and height). + * + * @param[in] monitor The monitor to query. + * @param[out] count Where to store the number of video modes in the returned + * array. This is set to zero if an error occurred. + * @return An array of video modes, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected, this function is called again for that monitor or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoMode + * + * @since Added in version 1.0. + * @glfw3 Changed to return an array of modes for a specific monitor. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); + +/*! @brief Returns the current mode of the specified monitor. + * + * This function returns the current video mode of the specified monitor. If + * you have created a full screen window for that monitor, the return value + * will depend on whether that window is iconified. + * + * @param[in] monitor The monitor to query. + * @return The current mode of the monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_modes + * @sa @ref glfwGetVideoModes + * + * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. + * + * @ingroup monitor + */ +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); + +/*! @brief Generates a gamma ramp and sets it for the specified monitor. + * + * This function generates a 256-element gamma ramp from the specified exponent + * and then calls @ref glfwSetGammaRamp with it. The value must be a finite + * number greater than zero. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] gamma The desired exponent. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); + +/*! @brief Returns the current gamma ramp for the specified monitor. + * + * This function returns the current gamma ramp of the specified monitor. + * + * @param[in] monitor The monitor to query. + * @return The current gamma ramp, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always return `NULL` and emit @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned structure and its arrays are allocated and + * freed by GLFW. You should not free them yourself. They are valid until the + * specified monitor is disconnected, this function is called again for that + * monitor or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); + +/*! @brief Sets the current gamma ramp for the specified monitor. + * + * This function sets the current gamma ramp for the specified monitor. The + * original gamma ramp for that monitor is saved by GLFW the first time this + * function is called and is restored by @ref glfwTerminate. + * + * @param[in] monitor The monitor whose gamma ramp to set. + * @param[in] ramp The gamma ramp to use. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark Gamma ramp sizes other than 256 are not supported by all platforms + * or graphics hardware. + * + * @remark @win32 The gamma ramp size must be 256. + * + * @remark @wayland Gamma handling is currently unavailable, this function will + * always emit @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified gamma ramp is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref monitor_gamma + * + * @since Added in version 3.0. + * + * @ingroup monitor + */ +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @brief Resets all window hints to their default values. + * + * This function resets all window hints to their + * [default values](@ref window_hints_values). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwWindowHint + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwDefaultWindowHints(void); + +/*! @brief Sets the specified window hint to the desired value. + * + * This function sets hints for the next call to @ref glfwCreateWindow. The + * hints, once set, retain their values until changed by a call to @ref + * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is + * terminated. + * + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. + * + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hints + * @sa @ref glfwDefaultWindowHints + * + * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. + * + * @ingroup window + */ +GLFWAPI void glfwWindowHint(int hint, int value); + +/*! @brief Creates a window and its associated context. + * + * This function creates a window and its associated OpenGL or OpenGL ES + * context. Most of the options controlling how the window and its context + * should be created are specified with [window hints](@ref window_hints). + * + * Successful creation does not change which context is current. Before you + * can use the newly created context, you need to + * [make it current](@ref context_current). For information about the `share` + * parameter, see @ref context_sharing. + * + * The created window, framebuffer and context may differ from what you + * requested, as not all parameters and hints are + * [hard constraints](@ref window_hints_hard). This includes the size of the + * window, especially for full screen windows. To query the actual attributes + * of the created window, framebuffer and context, see @ref + * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. + * + * To create a full screen window, you need to specify the monitor the window + * will cover. If no monitor is specified, the window will be windowed mode. + * Unless you have a way for the user to choose a specific monitor, it is + * recommended that you pick the primary monitor. For more information on how + * to query connected monitors, see @ref monitor_monitors. + * + * For full screen windows, the specified size becomes the resolution of the + * window's _desired video mode_. As long as a full screen window is not + * iconified, the supported video mode most closely matching the desired video + * mode is set for the specified monitor. For more information about full + * screen windows, including the creation of so called _windowed full screen_ + * or _borderless full screen_ windows, see @ref window_windowed_full_screen. + * + * Once you have created the window, you can switch it between windowed and + * full screen mode with @ref glfwSetWindowMonitor. If the window has an + * OpenGL or OpenGL ES context, it will be unaffected. + * + * By default, newly created windows use the placement recommended by the + * window system. To create the window at a specific position, make it + * initially invisible using the [GLFW_VISIBLE](@ref window_hints_wnd) window + * hint, set its [position](@ref window_pos) and then [show](@ref window_hide) + * it. + * + * As long as at least one full screen window is not iconified, the screensaver + * is prohibited from starting. + * + * Window systems put limits on window sizes. Very large or very small window + * dimensions may be overridden by the window system on creation. Check the + * actual [size](@ref window_size) after creation. + * + * The [swap interval](@ref buffer_swap) is not set during window creation and + * the initial value may vary depending on driver settings and defaults. + * + * @param[in] width The desired width, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] height The desired height, in screen coordinates, of the window. + * This must be greater than zero. + * @param[in] title The initial, UTF-8 encoded window title. + * @param[in] monitor The monitor to use for full screen mode, or `NULL` for + * windowed mode. + * @param[in] share The window whose context to share resources with, or `NULL` + * to not share resources. + * @return The handle of the created window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @win32 Window creation will fail if the Microsoft GDI software + * OpenGL implementation is the only one available. + * + * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` it + * will be set as the initial icon for the window. If no such icon is present, + * the `IDI_WINLOGO` icon will be used instead. To set a different icon, see + * @ref glfwSetWindowIcon. + * + * @remark @win32 The context to share resources with must not be current on + * any other thread. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, but the dock icon will be the same as the application bundle's icon. + * For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @macos The first time a window is created the menu bar is populated + * with common commands like Hide, Quit and About. The About entry opens + * a minimal about dialog with information from the application's bundle. The + * menu bar can be disabled with a + * [compile-time option](@ref compile_options_osx). + * + * @remark @macos On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the `NSHighResolutionCapable` + * key is enabled in the application bundle's `Info.plist`. For more + * information, see + * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) + * in the Mac Developer Library. The GLFW test and example programs use + * a custom `Info.plist` template for this, which can be found as + * `CMake/MacOSXBundleInfo.plist.in` in the source tree. + * + * @remark @x11 Some window managers will not respect the placement of + * initially hidden windows. + * + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. + * + * @remark @wayland The window frame is currently unimplemented, as if + * `GLFW_DECORATED` was always set to `GLFW_FALSE`. A compositor can still + * emit close, resize or maximize events, using for example a keybind + * mechanism. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size or refresh rate. + * + * @remark @wayland The wl_shell protocol does not support window + * icons, the window will inherit the one defined in the application's + * desktop file, so this function emits @ref GLFW_PLATFORM_ERROR. + * + * @remark @wayland Screensaver inhibition is currently unimplemented. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwDestroyWindow + * + * @since Added in version 3.0. Replaces `glfwOpenWindow`. + * + * @ingroup window + */ +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share); + +/*! @brief Destroys the specified window and its context. + * + * This function destroys the specified window and its context. On calling + * this function, no further callbacks will be called for that window. + * + * If the context of the specified window is current on the main thread, it is + * detached before being destroyed. + * + * @param[in] window The window to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @note The context of the specified window must not be current on any other + * thread when this function is called. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_creation + * @sa @ref glfwCreateWindow + * + * @since Added in version 3.0. Replaces `glfwCloseWindow`. + * + * @ingroup window + */ +GLFWAPI void glfwDestroyWindow(GLFWwindow* window); + +/*! @brief Checks the close flag of the specified window. + * + * This function returns the value of the close flag of the specified window. + * + * @param[in] window The window to query. + * @return The value of the close flag. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); + +/*! @brief Sets the close flag of the specified window. + * + * This function sets the value of the close flag of the specified window. + * This can be used to override the user's attempt to close the window, or + * to signal that it should be closed. + * + * @param[in] window The window whose flag to change. + * @param[in] value The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_close + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); + +/*! @brief Sets the title of the specified window. + * + * This function sets the window title, encoded as UTF-8, of the specified + * window. + * + * @param[in] window The window whose title to change. + * @param[in] title The UTF-8 encoded window title. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @macos The window title will not be updated until the next time you + * process events. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_title + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); + +/*! @brief Sets the icon for the specified window. + * + * This function sets the icon of the specified window. If passed an array of + * candidate images, those of or closest to the sizes desired by the system are + * selected. If no images are specified, the window reverts to its default + * icon. + * + * The desired image sizes varies depending on platform and system settings. + * The selected images will be rescaled as needed. Good sizes include 16x16, + * 32x32 and 48x48. + * + * @param[in] window The window whose icon to set. + * @param[in] count The number of images in the specified array, or zero to + * revert to the default window icon. + * @param[in] images The images to create the icon from. This is ignored if + * count is zero. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @remark @macos The GLFW window has no icon, as it is not a document + * window, so this function does nothing. The dock icon will be the same as + * the application bundle's icon. For more information on bundles, see the + * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) + * in the Mac Developer Library. + * + * @remark @wayland The wl_shell protocol does not support icons, the window + * will inherit the one defined in the application's desktop file, so this + * function emits @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_icon + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowIcon(GLFWwindow* window, int count, const GLFWimage* images); + +/*! @brief Retrieves the position of the client area of the specified window. + * + * This function retrieves the position, in screen coordinates, of the + * upper-left corner of the client area of the specified window. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The window to query. + * @param[out] xpos Where to store the x-coordinate of the upper-left corner of + * the client area, or `NULL`. + * @param[out] ypos Where to store the y-coordinate of the upper-left corner of + * the client area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to retrieve the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwSetWindowPos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); + +/*! @brief Sets the position of the client area of the specified window. + * + * This function sets the position, in screen coordinates, of the upper-left + * corner of the client area of the specified windowed mode window. If the + * window is a full screen window, this function does nothing. + * + * __Do not use this function__ to move an already visible window unless you + * have very good reasons for doing so, as it will confuse and annoy the user. + * + * The window manager may put limits on what positions are allowed. GLFW + * cannot and should not override these limits. + * + * @param[in] window The window to query. + * @param[in] xpos The x-coordinate of the upper-left corner of the client area. + * @param[in] ypos The y-coordinate of the upper-left corner of the client area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no way for an application to set the global + * position of its windows, this function will always emit @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * @sa @ref glfwGetWindowPos + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); + +/*! @brief Retrieves the size of the client area of the specified window. + * + * This function retrieves the size, in screen coordinates, of the client area + * of the specified window. If you wish to retrieve the size of the + * framebuffer of the window in pixels, see @ref glfwGetFramebufferSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose size to retrieve. + * @param[out] width Where to store the width, in screen coordinates, of the + * client area, or `NULL`. + * @param[out] height Where to store the height, in screen coordinates, of the + * client area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwSetWindowSize + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Sets the size limits of the specified window. + * + * This function sets the size limits of the client area of the specified + * window. If the window is full screen, the size limits only take effect + * once it is made windowed. If the window is not resizable, this function + * does nothing. + * + * The size limits are applied immediately to a windowed mode window and may + * cause it to be resized. + * + * The maximum dimensions must be greater than or equal to the minimum + * dimensions and all must be greater than or equal to zero. + * + * @param[in] window The window to set limits for. + * @param[in] minwidth The minimum width, in screen coordinates, of the client + * area, or `GLFW_DONT_CARE`. + * @param[in] minheight The minimum height, in screen coordinates, of the + * client area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * area, or `GLFW_DONT_CARE`. + * @param[in] maxheight The maximum height, in screen coordinates, of the + * client area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The size limits will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowAspectRatio + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @brief Sets the aspect ratio of the specified window. + * + * This function sets the required aspect ratio of the client area of the + * specified window. If the window is full screen, the aspect ratio only takes + * effect once it is made windowed. If the window is not resizable, this + * function does nothing. + * + * The aspect ratio is specified as a numerator and a denominator and both + * values must be greater than zero. For example, the common 16:9 aspect ratio + * is specified as 16 and 9, respectively. + * + * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect + * ratio limit is disabled. + * + * The aspect ratio is applied immediately to a windowed mode window and may + * cause it to be resized. + * + * @param[in] window The window to set limits for. + * @param[in] numer The numerator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * @param[in] denom The denominator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @remark @wayland The aspect ratio will not be applied until the window is + * actually resized, either by the user or by the compositor. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa @ref glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); + +/*! @brief Sets the size of the client area of the specified window. + * + * This function sets the size, in screen coordinates, of the client area of + * the specified window. + * + * For full screen windows, this function updates the resolution of its desired + * video mode and switches to the video mode closest to it, without affecting + * the window's context. As the context is unaffected, the bit depths of the + * framebuffer remain unchanged. + * + * If you wish to update the refresh rate of the desired video mode in addition + * to its resolution, see @ref glfwSetWindowMonitor. + * + * The window manager may put limits on what sizes are allowed. GLFW cannot + * and should not override these limits. + * + * @param[in] window The window to resize. + * @param[in] width The desired width, in screen coordinates, of the window + * client area. + * @param[in] height The desired height, in screen coordinates, of the window + * client area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland A full screen window will not attempt to change the mode, + * no matter what the requested size. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * @sa @ref glfwGetWindowSize + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); + +/*! @brief Retrieves the size of the framebuffer of the specified window. + * + * This function retrieves the size, in pixels, of the framebuffer of the + * specified window. If you wish to retrieve the size of the window in screen + * coordinates, see @ref glfwGetWindowSize. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose framebuffer to query. + * @param[out] width Where to store the width, in pixels, of the framebuffer, + * or `NULL`. + * @param[out] height Where to store the height, in pixels, of the framebuffer, + * or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * @sa @ref glfwSetFramebufferSizeCallback + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height); + +/*! @brief Retrieves the size of the frame of the window. + * + * This function retrieves the size, in screen coordinates, of each edge of the + * frame of the specified window. This size includes the title bar, if the + * window has one. The size of the frame may vary depending on the + * [window-related hints](@ref window_hints_wnd) used to create it. + * + * Because this function retrieves the size of each window frame edge and not + * the offset along a particular coordinate axis, the retrieved values will + * always be zero or positive. + * + * Any or all of the size arguments may be `NULL`. If an error occurs, all + * non-`NULL` size arguments will be set to zero. + * + * @param[in] window The window whose frame size to query. + * @param[out] left Where to store the size, in screen coordinates, of the left + * edge of the window frame, or `NULL`. + * @param[out] top Where to store the size, in screen coordinates, of the top + * edge of the window frame, or `NULL`. + * @param[out] right Where to store the size, in screen coordinates, of the + * right edge of the window frame, or `NULL`. + * @param[out] bottom Where to store the size, in screen coordinates, of the + * bottom edge of the window frame, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland The window frame is currently unimplemented, as if + * `GLFW_DECORATED` was always set to `GLFW_FALSE`, so the returned values + * will always be zero. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @brief Iconifies the specified window. + * + * This function iconifies (minimizes) the specified window if it was + * previously restored. If the window is already iconified, this function does + * nothing. + * + * If the specified window is a full screen window, the original monitor + * resolution is restored until the window is restored. + * + * @param[in] window The window to iconify. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland There is no concept of iconification in wl_shell, this + * function will always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwRestoreWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwIconifyWindow(GLFWwindow* window); + +/*! @brief Restores the specified window. + * + * This function restores the specified window if it was previously iconified + * (minimized) or maximized. If the window is already restored, this function + * does nothing. + * + * If the specified window is a full screen window, the resolution chosen for + * the window is restored on the selected monitor. + * + * @param[in] window The window to restore. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @par Thread Safety + * This function may only be called from the main thread. + * + * @sa @ref window_iconify + * @sa @ref glfwIconifyWindow + * @sa @ref glfwRestoreWindow + * + * @since Added in GLFW 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); + +/*! @brief Makes the specified window visible. + * + * This function makes the specified window visible if it was previously + * hidden. If the window is already visible or is in full screen mode, this + * function does nothing. + * + * @param[in] window The window to make visible. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwHideWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwShowWindow(GLFWwindow* window); + +/*! @brief Hides the specified window. + * + * This function hides the specified window if it was previously visible. If + * the window is already hidden or is in full screen mode, this function does + * nothing. + * + * @param[in] window The window to hide. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_hide + * @sa @ref glfwShowWindow + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwHideWindow(GLFWwindow* window); + +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable + * this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland It is not possible for an application to bring its windows + * to front, this function will always emit @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + +/*! @brief Returns the monitor that the window uses for full screen mode. + * + * This function returns the handle of the monitor that the specified window is + * in full screen on. + * + * @param[in] window The window to query. + * @return The monitor, or `NULL` if the window is in windowed mode or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref glfwSetWindowMonitor + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); + +/*! @brief Sets the mode, monitor, video mode and placement of a window. + * + * This function sets the monitor that the window uses for full screen mode or, + * if the monitor is `NULL`, makes it windowed mode. + * + * When setting a monitor, this function updates the width, height and refresh + * rate of the desired video mode and switches to the video mode closest to it. + * The window position is ignored when setting a monitor. + * + * When the monitor is `NULL`, the position, width and height are used to + * place the window client area. The refresh rate is ignored when no monitor + * is specified. + * + * If you only wish to update the resolution of a full screen window or the + * size of a windowed mode window, see @ref glfwSetWindowSize. + * + * When a window transitions from full screen to windowed mode, this function + * restores any previous window settings such as whether it is decorated, + * floating, resizable, has size or aspect ratio limits, etc. + * + * @param[in] window The window whose monitor, size or video mode to set. + * @param[in] monitor The desired monitor, or `NULL` to set windowed mode. + * @param[in] xpos The desired x-coordinate of the upper-left corner of the + * client area. + * @param[in] ypos The desired y-coordinate of the upper-left corner of the + * client area. + * @param[in] width The desired with, in screen coordinates, of the client area + * or video mode. + * @param[in] height The desired height, in screen coordinates, of the client + * area or video mode. + * @param[in] refreshRate The desired refresh rate, in Hz, of the video mode, + * or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland The desired window position is ignored, as there is no way + * for an application to set this property. + * + * @remark @wayland Setting the window to full screen will not attempt to + * change the mode, no matter what the requested size or refresh rate. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_monitor + * @sa @ref window_full_screen + * @sa @ref glfwGetWindowMonitor + * @sa @ref glfwSetWindowSize + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* window, GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + +/*! @brief Returns an attribute of the specified window. + * + * This function returns the value of an attribute of the specified window or + * its OpenGL or OpenGL ES context. + * + * @param[in] window The window to query. + * @param[in] attrib The [window attribute](@ref window_attribs) whose value to + * return. + * @return The value of the attribute, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark Framebuffer related hints are not window attributes. See @ref + * window_attribs_fb for more information. + * + * @remark Zero is a valid value for many window and context related + * attributes so you cannot use a return value of zero as an indication of + * errors. However, this function should not fail as long as it is passed + * valid arguments and the library has been [initialized](@ref intro_init). + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_attribs + * + * @since Added in version 3.0. Replaces `glfwGetWindowParam` and + * `glfwGetGLVersion`. + * + * @ingroup window + */ +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); + +/*! @brief Sets the user pointer of the specified window. + * + * This function sets the user-defined pointer of the specified window. The + * current value is retained until the window is destroyed. The initial value + * is `NULL`. + * + * @param[in] window The window whose pointer to set. + * @param[in] pointer The new value. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwGetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); + +/*! @brief Returns the user pointer of the specified window. + * + * This function returns the current value of the user-defined pointer of the + * specified window. The initial value is `NULL`. + * + * @param[in] window The window whose pointer to return. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @sa @ref window_userptr + * @sa @ref glfwSetWindowUserPointer + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); + +/*! @brief Sets the position callback for the specified window. + * + * This function sets the position callback of the specified window, which is + * called when the window is moved. The callback is provided with the screen + * position of the upper-left corner of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland This callback will never be called, as there is no way for + * an application to know its global position. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_pos + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindowposfun cbfun); + +/*! @brief Sets the size callback for the specified window. + * + * This function sets the size callback of the specified window, which is + * called when the window is resized. The callback is provided with the size, + * in screen coordinates, of the client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_size + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwindowsizefun cbfun); + +/*! @brief Sets the close callback for the specified window. + * + * This function sets the close callback of the specified window, which is + * called when the user attempts to close the window, for example by clicking + * the close widget in the title bar. + * + * The close flag is set before this callback is called, but you can modify it + * at any time with @ref glfwSetWindowShouldClose. + * + * The close callback is not triggered by @ref glfwDestroyWindow. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @macos Selecting Quit from the application menu will trigger the + * close callback for all windows. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_close + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwindowclosefun cbfun); + +/*! @brief Sets the refresh callback for the specified window. + * + * This function sets the refresh callback of the specified window, which is + * called when the client area of the window needs to be redrawn, for example + * if the window has been exposed after having been covered by another window. + * + * On compositing window systems such as Aero, Compiz, Aqua or Wayland, where + * the window contents are saved off-screen, this callback may be called only + * very infrequently or never at all. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_refresh + * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup window + */ +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GLFWwindowrefreshfun cbfun); + +/*! @brief Sets the focus callback for the specified window. + * + * This function sets the focus callback of the specified window, which is + * called when the window gains or loses input focus. + * + * After the focus callback is called for a window that lost input focus, + * synthetic key and mouse button release events will be generated for all such + * that had been pressed. For more information, see @ref glfwSetKeyCallback + * and @ref glfwSetMouseButtonCallback. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwindowfocusfun cbfun); + +/*! @brief Sets the iconify callback for the specified window. + * + * This function sets the iconification callback of the specified window, which + * is called when the window is iconified or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland The wl_shell protocol has no concept of iconification, + * this callback will never be called. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GLFWwindowiconifyfun cbfun); + +/*! @brief Sets the maximize callback for the specified window. + * + * This function sets the maximization callback of the specified window, which + * is called when the window is maximized or restored. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_maximize + * + * @since Added in version 3.3. + * + * @ingroup window + */ +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* window, GLFWwindowmaximizefun cbfun); + +/*! @brief Sets the framebuffer resize callback for the specified window. + * + * This function sets the framebuffer resize callback of the specified window, + * which is called when the framebuffer of the specified window is resized. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_fbsize + * + * @since Added in version 3.0. + * + * @ingroup window + */ +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window, GLFWframebuffersizefun cbfun); + +/*! @brief Processes all pending events. + * + * This function processes only those events that are already in the event + * queue and then returns immediately. Processing events will cause the window + * and input callbacks associated with those events to be called. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * On some platforms, certain events are sent directly to the application + * without going through the event queue, causing callbacks to be called + * outside of a call to one of the event processing functions. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 1.0. + * + * @ingroup window + */ +GLFWAPI void glfwPollEvents(void); + +/*! @brief Waits until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue. Once one or more events are available, + * it behaves exactly like @ref glfwPollEvents, i.e. the events in the queue + * are processed and the function then returns immediately. Processing events + * will cause the window and input callbacks associated with those events to be + * called. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * On some platforms, certain callbacks may be called outside of a call to one + * of the event processing functions. + * + * If no windows exist, this function returns immediately. For synchronization + * of threads in applications that do not create windows, use your threading + * library of choice. + * + * Event processing is not required for joystick input to work. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 2.5. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEvents(void); + +/*! @brief Waits with timeout until events are queued and processes them. + * + * This function puts the calling thread to sleep until at least one event is + * available in the event queue, or until the specified timeout is reached. If + * one or more events are available, it behaves exactly like @ref + * glfwPollEvents, i.e. the events in the queue are processed and the function + * then returns immediately. Processing events will cause the window and input + * callbacks associated with those events to be called. + * + * The timeout value must be a positive finite number. + * + * Since not all events are associated with callbacks, this function may return + * without a callback having been called even if you are monitoring all + * callbacks. + * + * On some platforms, a window move, resize or menu operation will cause event + * processing to block. This is due to how event processing is designed on + * those platforms. You can use the + * [window refresh callback](@ref window_refresh) to redraw the contents of + * your window when necessary during such operations. + * + * On some platforms, certain callbacks may be called outside of a call to one + * of the event processing functions. + * + * If no windows exist, this function returns immediately. For synchronization + * of threads in applications that do not create windows, use your threading + * library of choice. + * + * Event processing is not required for joystick input to work. + * + * @param[in] timeout The maximum amount of time, in seconds, to wait. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref events + * @sa @ref glfwPollEvents + * @sa @ref glfwWaitEvents + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwWaitEventsTimeout(double timeout); + +/*! @brief Posts an empty event to the event queue. + * + * This function posts an empty event from the current thread to the event + * queue, causing @ref glfwWaitEvents or @ref glfwWaitEventsTimeout to return. + * + * If no windows exist, this function returns immediately. For synchronization + * of threads in applications that do not create windows, use your threading + * library of choice. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref events + * @sa @ref glfwWaitEvents + * @sa @ref glfwWaitEventsTimeout + * + * @since Added in version 3.1. + * + * @ingroup window + */ +GLFWAPI void glfwPostEmptyEvent(void); + +/*! @brief Returns the value of an input option for the specified window. + * + * This function returns the value of an input option for the specified window. + * The mode must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * + * @param[in] window The window to query. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); + +/*! @brief Sets an input option for the specified window. + * + * This function sets an input mode option for the specified window. The mode + * must be one of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * + * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor + * modes: + * - `GLFW_CURSOR_NORMAL` makes the cursor visible and behaving normally. + * - `GLFW_CURSOR_HIDDEN` makes the cursor invisible when it is over the client + * area of the window but does not restrict the cursor from leaving. + * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual + * and unlimited cursor movement. This is useful for implementing for + * example 3D camera controls. + * + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to + * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are + * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` + * the next time it is called even if the key had been released before the + * call. This is useful when you are only interested in whether keys have been + * pressed but not when or in which order. + * + * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either + * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. + * If sticky mouse buttons are enabled, a mouse button press will ensure that + * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. + * + * @param[in] window The window whose input mode to set. + * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or + * `GLFW_STICKY_MOUSE_BUTTONS`. + * @param[in] value The new value of the specified input mode. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref glfwGetInputMode + * + * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. + * + * @ingroup input + */ +GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); + +/*! @brief Returns the localized name of the specified printable key. + * + * This function returns the localized name of the specified printable key. + * This is intended for displaying key bindings to the user. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise + * the scancode is ignored. If a non-printable key or (if the key is + * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is + * specified, this function returns `NULL`. + * + * This behavior allows you to pass in the arguments passed to the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` + * + * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. + * @param[in] scancode The scancode of the key to query. + * @return The localized name of the key, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetKeyName, or until the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key_name + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key, int scancode); + +/*! @brief Returns the platform dependent scancode of the specified key. + * + * This function returns the platform dependent scancode of the specified key. + * This is intended for platform specific default keybindings. + * + * If the key is `GLFW_KEY_UNKNOWN` or does not exist on the keyboard this + * method will return `-1`. + * + * @param[in] key The key to query. + * @return The platform dependent scancode for the key, or `-1` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref input_key_scancode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwGetKeyScancode(int key); + +/*! @brief Returns the last reported state of a keyboard key for the specified + * window. + * + * This function returns the last state reported for the specified key to the + * specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. The higher-level action `GLFW_REPEAT` is only reported to + * the key callback. + * + * If the `GLFW_STICKY_KEYS` input mode is enabled, this function returns + * `GLFW_PRESS` the first time you call it for a key that was pressed, even if + * that key has already been released. + * + * The key functions deal with physical keys, with [key tokens](@ref keys) + * named after their use on the standard US keyboard layout. If you want to + * input text, use the Unicode character callback instead. + * + * The [modifier key bit masks](@ref mods) are not key tokens and cannot be + * used with this function. + * + * __Do not use this function__ to implement [text input](@ref input_char). + * + * @param[in] window The desired window. + * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is + * not a valid key for this function. + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetKey(GLFWwindow* window, int key); + +/*! @brief Returns the last reported state of a mouse button for the specified + * window. + * + * This function returns the last state reported for the specified mouse button + * to the specified window. The returned state is one of `GLFW_PRESS` or + * `GLFW_RELEASE`. + * + * If the `GLFW_STICKY_MOUSE_BUTTONS` input mode is enabled, this function + * `GLFW_PRESS` the first time you call it for a mouse button that was pressed, + * even if that mouse button has already been released. + * + * @param[in] window The desired window. + * @param[in] button The desired [mouse button](@ref buttons). + * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup input + */ +GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); + +/*! @brief Retrieves the position of the cursor relative to the client area of + * the window. + * + * This function returns the position of the cursor, in screen coordinates, + * relative to the upper-left corner of the client area of the specified + * window. + * + * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor + * position is unbounded and limited only by the minimum and maximum values of + * a `double`. + * + * The coordinate can be converted to their integer equivalents with the + * `floor` function. Casting directly to an integer type works for positive + * coordinates, but fails for negative ones. + * + * Any or all of the position arguments may be `NULL`. If an error occurs, all + * non-`NULL` position arguments will be set to zero. + * + * @param[in] window The desired window. + * @param[out] xpos Where to store the cursor x-coordinate, relative to the + * left edge of the client area, or `NULL`. + * @param[out] ypos Where to store the cursor y-coordinate, relative to the to + * top edge of the client area, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwSetCursorPos + * + * @since Added in version 3.0. Replaces `glfwGetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); + +/*! @brief Sets the position of the cursor, relative to the client area of the + * window. + * + * This function sets the position, in screen coordinates, of the cursor + * relative to the upper-left corner of the client area of the specified + * window. The window must have input focus. If the window does not have + * input focus when this function is called, it fails silently. + * + * __Do not use this function__ to implement things like camera controls. GLFW + * already provides the `GLFW_CURSOR_DISABLED` cursor mode that hides the + * cursor, transparently re-centers it and provides unconstrained cursor + * motion. See @ref glfwSetInputMode for more information. + * + * If the cursor mode is `GLFW_CURSOR_DISABLED` then the cursor position is + * unconstrained and limited only by the minimum and maximum values of + * a `double`. + * + * @param[in] window The desired window. + * @param[in] xpos The desired x-coordinate, relative to the left edge of the + * client area. + * @param[in] ypos The desired y-coordinate, relative to the top edge of the + * client area. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland This function will only work when the cursor mode is + * `GLFW_CURSOR_DISABLED`, otherwise it will do nothing. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * @sa @ref glfwGetCursorPos + * + * @since Added in version 3.0. Replaces `glfwSetMousePos`. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); + +/*! @brief Creates a custom cursor. + * + * Creates a new custom cursor image that can be set for a window with @ref + * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. + * Any remaining cursors are destroyed by @ref glfwTerminate. + * + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel. They are arranged canonically as packed sequential rows, + * starting from the top-left corner. + * + * The cursor hotspot is specified in pixels, relative to the upper-left corner + * of the cursor image. Like all other coordinate systems in GLFW, the X-axis + * points to the right and the Y-axis points down. + * + * @param[in] image The desired cursor image. + * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. + * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. + * @return The handle of the created cursor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The specified image data is copied before this function + * returns. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwDestroyCursor + * @sa @ref glfwCreateStandardCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot); + +/*! @brief Creates a cursor with a standard shape. + * + * Returns a cursor with a [standard shape](@ref shapes), that can be set for + * a window with @ref glfwSetCursor. + * + * @param[in] shape One of the [standard shapes](@ref shapes). + * @return A new cursor ready to use or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); + +/*! @brief Destroys a cursor. + * + * This function destroys a cursor previously created with @ref + * glfwCreateCursor. Any remaining cursors will be destroyed by @ref + * glfwTerminate. + * + * @param[in] cursor The cursor object to destroy. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * @sa @ref glfwCreateCursor + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); + +/*! @brief Sets the cursor for the window. + * + * This function sets the cursor image to be used when the cursor is over the + * client area of the specified window. The set cursor will only be visible + * when the [cursor mode](@ref cursor_mode) of the window is + * `GLFW_CURSOR_NORMAL`. + * + * On some platforms, the set cursor may not be visible unless the window also + * has input focus. + * + * @param[in] window The window to set the cursor for. + * @param[in] cursor The cursor to set, or `NULL` to switch back to the default + * arrow cursor. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_object + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); + +/*! @brief Sets the key callback. + * + * This function sets the key callback of the specified window, which is called + * when a key is pressed, repeated or released. + * + * The key functions deal with physical keys, with layout independent + * [key tokens](@ref keys) named after their values in the standard US keyboard + * layout. If you want to input text, use the + * [character callback](@ref glfwSetCharCallback) instead. + * + * When a window loses input focus, it will generate synthetic key release + * events for all pressed keys. You can tell these events from user-generated + * events by the fact that the synthetic ones are generated after the focus + * loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * The scancode of a key is specific to that platform or sometimes even to that + * machine. Scancodes are intended to allow users to bind keys that don't have + * a GLFW key token. Such keys have `key` set to `GLFW_KEY_UNKNOWN`, their + * state is not saved and so it cannot be queried with @ref glfwGetKey. + * + * Sometimes GLFW needs to generate synthetic key events, in which case the + * scancode may be zero. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new key callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); + +/*! @brief Sets the Unicode character callback. + * + * This function sets the character callback of the specified window, which is + * called when a Unicode character is input. + * + * The character callback is intended for Unicode text input. As it deals with + * characters, it is keyboard layout dependent, whereas the + * [key callback](@ref glfwSetKeyCallback) is not. Characters do not map 1:1 + * to physical keys, as a key may produce zero, one or more characters. If you + * want to know whether a specific physical key was pressed or released, see + * the key callback instead. + * + * The character callback behaves as system text input normally does and will + * not be called if modifier keys are held down that would prevent normal text + * input on that platform, for example a Super (Command) key on macOS or Alt key + * on Windows. There is a + * [character with modifiers callback](@ref glfwSetCharModsCallback) that + * receives these events. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); + +/*! @brief Sets the Unicode character with modifiers callback. + * + * This function sets the character with modifiers callback of the specified + * window, which is called when a Unicode character is input regardless of what + * modifier keys are used. + * + * The character with modifiers callback is intended for implementing custom + * Unicode character input. For regular Unicode text input, see the + * [character callback](@ref glfwSetCharCallback). Like the character + * callback, the character with modifiers callback deals with characters and is + * keyboard layout dependent. Characters do not map 1:1 to physical keys, as + * a key may produce zero, one or more characters. If you want to know whether + * a specific physical key was pressed or released, see the + * [key callback](@ref glfwSetKeyCallback) instead. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_char + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmodsfun cbfun); + +/*! @brief Sets the mouse button callback. + * + * This function sets the mouse button callback of the specified window, which + * is called when a mouse button is pressed or released. + * + * When a window loses input focus, it will generate synthetic mouse button + * release events for all pressed mouse buttons. You can tell these events + * from user-generated events by the fact that the synthetic ones are generated + * after the focus loss event has been processed, i.e. after the + * [window focus callback](@ref glfwSetWindowFocusCallback) has been called. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_mouse_button + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. + * + * @ingroup input + */ +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmousebuttonfun cbfun); + +/*! @brief Sets the cursor position callback. + * + * This function sets the cursor position callback of the specified window, + * which is called when the cursor is moved. The callback is provided with the + * position, in screen coordinates, relative to the upper-left corner of the + * client area of the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_pos + * + * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursorposfun cbfun); + +/*! @brief Sets the cursor enter/exit callback. + * + * This function sets the cursor boundary crossing callback of the specified + * window, which is called when the cursor enters or leaves the client area of + * the window. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref cursor_enter + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcursorenterfun cbfun); + +/*! @brief Sets the scroll callback. + * + * This function sets the scroll callback of the specified window, which is + * called when a scrolling device is used, such as a mouse wheel or scrolling + * area of a touchpad. + * + * The scroll callback receives all scrolling input, like that from a mouse + * wheel or a touchpad scrolling area. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new scroll callback, or `NULL` to remove the currently + * set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref scrolling + * + * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. + * + * @ingroup input + */ +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cbfun); + +/*! @brief Sets the file drop callback. + * + * This function sets the file drop callback of the specified window, which is + * called when one or more dragged files are dropped on the window. + * + * Because the path array and its strings may have been generated specifically + * for that event, they are not guaranteed to be valid after the callback has + * returned. If you wish to use them after the callback returns, you need to + * make a deep copy. + * + * @param[in] window The window whose callback to set. + * @param[in] cbfun The new file drop callback, or `NULL` to remove the + * currently set callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @wayland File drop is currently unimplemented. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref path_drop + * + * @since Added in version 3.1. + * + * @ingroup input + */ +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); + +/*! @brief Returns whether the specified joystick is present. + * + * This function returns whether the specified joystick is present. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick + * + * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. + * + * @ingroup input + */ +GLFWAPI int glfwJoystickPresent(int jid); + +/*! @brief Returns the values of all axes of the specified joystick. + * + * This function returns the values of all axes of the specified joystick. + * Each element in the array is a value between -1.0 and 1.0. + * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of axis values in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of axis values, or `NULL` if the joystick is not present or + * an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_axis + * + * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. + * + * @ingroup input + */ +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count); + +/*! @brief Returns the state of all buttons of the specified joystick. + * + * This function returns the state of all buttons of the specified joystick. + * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. + * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @param[out] count Where to store the number of button states in the returned + * array. This is set to zero if the joystick is not present or an error + * occurred. + * @return An array of button states, or `NULL` if the joystick is not present + * or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_button + * + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. + * + * @ingroup input + */ +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count); + +/*! @brief Returns the name of the specified joystick. + * + * This function returns the name, encoded as UTF-8, of the specified joystick. + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. + * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * + * @param[in] jid The [joystick](@ref joysticks) to query. + * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick + * is not present or an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_name + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetJoystickName(int jid); + +/*! @brief Sets the joystick configuration callback. + * + * This function sets the joystick configuration callback, or removes the + * currently set callback. This is called when a joystick is connected to or + * disconnected from the system. + * + * @param[in] cbfun The new callback, or `NULL` to remove the currently set + * callback. + * @return The previously set callback, or `NULL` if no callback was set or the + * library had not been [initialized](@ref intro_init). + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref joystick_event + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun); + +/*! @brief Sets the clipboard to the specified string. + * + * This function sets the system clipboard to the specified, UTF-8 encoded + * string. + * + * @param[in] window The window that will own the clipboard contents. + * @param[in] string A UTF-8 encoded string. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Clipboard is currently unimplemented. + * + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwGetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); + +/*! @brief Returns the contents of the clipboard as a string. + * + * This function returns the contents of the system clipboard, if it contains + * or is convertible to a UTF-8 encoded string. If the clipboard is empty or + * if its contents cannot be converted, `NULL` is returned and a @ref + * GLFW_FORMAT_UNAVAILABLE error is generated. + * + * @param[in] window The window that will request the clipboard contents. + * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` + * if an [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark @wayland Clipboard is currently unimplemented. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref clipboard + * @sa @ref glfwSetClipboardString + * + * @since Added in version 3.0. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); + +/*! @brief Returns the value of the GLFW timer. + * + * This function returns the value of the GLFW timer. Unless the timer has + * been set using @ref glfwSetTime, the timer measures time elapsed since GLFW + * was initialized. + * + * The resolution of the timer is system dependent, but is usually on the order + * of a few micro- or nanoseconds. It uses the highest-resolution monotonic + * time source on each supported platform. + * + * @return The current value, in seconds, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal timer offset is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwSetTime. + * + * @sa @ref time + * + * @since Added in version 1.0. + * + * @ingroup input + */ +GLFWAPI double glfwGetTime(void); + +/*! @brief Sets the GLFW timer. + * + * This function sets the value of the GLFW timer. It then continues to count + * up from that value. The value must be a positive finite number less than + * or equal to 18446744073.0, which is approximately 584.5 years. + * + * @param[in] time The new value, in seconds. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @remark The upper limit of the timer is calculated as + * floor((264 - 1) / 109) and is due to implementations + * storing nanoseconds in 64 bits. The limit may be increased in the future. + * + * @thread_safety This function may be called from any thread. Reading and + * writing of the internal timer offset is not atomic, so it needs to be + * externally synchronized with calls to @ref glfwGetTime. + * + * @sa @ref time + * + * @since Added in version 2.2. + * + * @ingroup input + */ +GLFWAPI void glfwSetTime(double time); + +/*! @brief Returns the current value of the raw timer. + * + * This function returns the current value of the raw timer, measured in + * 1 / frequency seconds. To get the frequency, call @ref + * glfwGetTimerFrequency. + * + * @return The value of the timer, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerFrequency + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerValue(void); + +/*! @brief Returns the frequency, in Hz, of the raw timer. + * + * This function returns the frequency, in Hz, of the raw timer. + * + * @return The frequency of the timer, in Hz, or zero if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref time + * @sa @ref glfwGetTimerValue + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI uint64_t glfwGetTimerFrequency(void); + +/*! @brief Makes the context of the specified window current for the calling + * thread. + * + * This function makes the OpenGL or OpenGL ES context of the specified window + * current on the calling thread. A context can only be made current on + * a single thread at a time and each thread can have only a single current + * context at a time. + * + * By default, making a context non-current implicitly forces a pipeline flush. + * On machines that support `GL_KHR_context_flush_control`, you can control + * whether a context performs this flush by setting the + * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * @param[in] window The window whose context to make current, or `NULL` to + * detach the current context. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwGetCurrentContext + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); + +/*! @brief Returns the window whose context is current on the calling thread. + * + * This function returns the window whose OpenGL or OpenGL ES context is + * current on the calling thread. + * + * @return The window whose context is current, or `NULL` if no window's + * context is current. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_current + * @sa @ref glfwMakeContextCurrent + * + * @since Added in version 3.0. + * + * @ingroup context + */ +GLFWAPI GLFWwindow* glfwGetCurrentContext(void); + +/*! @brief Swaps the front and back buffers of the specified window. + * + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. + * + * @param[in] window The window whose buffers to swap. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapInterval + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwSwapBuffers(GLFWwindow* window); + +/*! @brief Sets the swap interval for the current context. + * + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. + * + * Contexts that support either of the `WGL_EXT_swap_control_tear` and + * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, + * which allow the driver to swap even if a frame arrives a little bit late. + * You can check for the presence of these extensions using @ref + * glfwExtensionSupported. For more information about swap tearing, see the + * extension specifications. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * + * @param[in] interval The minimum number of screen updates to wait for + * until the buffers are swapped by @ref glfwSwapBuffers. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark This function is not called during context creation, leaving the + * swap interval set to whatever is the default on that platform. This is done + * because some swap interval extensions used by GLFW do not allow the swap + * interval to be reset to zero once it has been set to a non-zero value. + * + * @remark Some GPU drivers do not honor the requested swap interval, either + * because of a user setting that overrides the application's request or due to + * bugs in the driver. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref buffer_swap + * @sa @ref glfwSwapBuffers + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI void glfwSwapInterval(int interval); + +/*! @brief Returns whether the specified extension is available. + * + * This function returns whether the specified + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * As this functions retrieves and searches one or more extension strings each + * call, it is recommended that you cache its results if it is going to be used + * frequently. The extension strings will not change during the lifetime of + * a context, so there is no danger in doing this. + * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * + * @param[in] extension The ASCII encoded name of the extension. + * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` + * otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwGetProcAddress + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI int glfwExtensionSupported(const char* extension); + +/*! @brief Returns the address of the specified function for the current + * context. + * + * This function returns the address of the specified OpenGL or OpenGL ES + * [core or extension function](@ref context_glext), if it is supported + * by the current context. + * + * A context must be current on the calling thread. Calling this function + * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark The address of a given function is not guaranteed to be the same + * between contexts. + * + * @remark This function may return a non-`NULL` address despite the + * associated version or extension not being available. Always check the + * context version or extension string first. + * + * @pointer_lifetime The returned function pointer is valid until the context + * is destroyed or the library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref context_glext + * @sa @ref glfwExtensionSupported + * + * @since Added in version 1.0. + * + * @ingroup context + */ +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); + +/*! @brief Returns whether the Vulkan loader has been found. + * + * This function returns whether the Vulkan loader has been found. This check + * is performed by @ref glfwInit. + * + * The availability of a Vulkan loader does not by itself guarantee that window + * surface creation or even device creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary + * for Vulkan surface creation are available and @ref + * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of + * a physical device supports image presentation. + * + * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_support + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwVulkanSupported(void); + +/*! @brief Returns the Vulkan instance extensions required by GLFW. + * + * This function returns an array of names of Vulkan instance extensions required + * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the + * list will always contains `VK_KHR_surface`, so if you don't require any + * additional extensions you can pass this list directly to the + * `VkInstanceCreateInfo` struct. + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available. + * + * If Vulkan is available but no set of extensions allowing window surface + * creation was found, this function returns `NULL`. You may still use Vulkan + * for off-screen rendering and compute work. + * + * @param[out] count Where to store the number of extensions in the returned + * array. This is set to zero if an error occurred. + * @return An array of ASCII encoded extension names, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @remarks Additional extensions may be required by future versions of GLFW. + * You should check if any extensions you wish to enable are already in the + * returned array, as it is an error to specify an extension more than once in + * the `VkInstanceCreateInfo` struct. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_ext + * @sa @ref glfwCreateWindowSurface + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count); + +#if defined(VK_VERSION_1_0) + +/*! @brief Returns the address of the specified Vulkan instance function. + * + * This function returns the address of the specified Vulkan core or extension + * function for the specified instance. If instance is set to `NULL` it can + * return any function exported from the Vulkan loader, including at least the + * following functions: + * + * - `vkEnumerateInstanceExtensionProperties` + * - `vkEnumerateInstanceLayerProperties` + * - `vkCreateInstance` + * - `vkGetInstanceProcAddr` + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available. + * + * This function is equivalent to calling `vkGetInstanceProcAddr` with + * a platform-specific query of the Vulkan loader as a fallback. + * + * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve + * functions related to instance creation. + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @pointer_lifetime The returned function pointer is valid until the library + * is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_proc + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); + +/*! @brief Returns whether the specified queue family can present images. + * + * This function returns whether the specified queue family of the specified + * physical device supports presentation to the platform GLFW was built for. + * + * If Vulkan or the required window surface creation instance extensions are + * not available on the machine, or if the specified instance was not created + * with the required extensions, this function returns `GLFW_FALSE` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available and @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * @param[in] instance The instance that the physical device belongs to. + * @param[in] device The physical device that the queue family belongs to. + * @param[in] queuefamily The index of the queue family to query. + * @return `GLFW_TRUE` if the queue family supports presentation, or + * `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_present + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @brief Creates a Vulkan surface for the specified window. + * + * This function creates a Vulkan surface for the specified window. + * + * If the Vulkan loader was not found at initialization, this function returns + * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE + * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was + * found. + * + * If the required window surface creation instance extensions are not + * available or if the specified instance was not created with these extensions + * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * The window surface must be destroyed before the specified Vulkan instance. + * It is the responsibility of the caller to destroy the window surface. GLFW + * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the + * surface. + * + * @param[in] instance The Vulkan instance to create the surface in. + * @param[in] window The window to create the surface for. + * @param[in] allocator The allocator to use, or `NULL` to use the default + * allocator. + * @param[out] surface Where to store the handle of the surface. This is set + * to `VK_NULL_HANDLE` if an error occurred. + * @return `VK_SUCCESS` if successful, or a Vulkan error code if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @remarks If an error occurs before the creation call is made, GLFW returns + * the Vulkan error code most appropriate for the error. Appropriate use of + * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should + * eliminate almost all occurrences of these errors. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_surface + * @sa @ref glfwGetRequiredInstanceExtensions + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +#endif /*VK_VERSION_1_0*/ + + +/************************************************************************* + * Global definition cleanup + *************************************************************************/ + +/* ------------------- BEGIN SYSTEM/COMPILER SPECIFIC -------------------- */ + +#ifdef GLFW_WINGDIAPI_DEFINED + #undef WINGDIAPI + #undef GLFW_WINGDIAPI_DEFINED +#endif + +#ifdef GLFW_CALLBACK_DEFINED + #undef CALLBACK + #undef GLFW_CALLBACK_DEFINED +#endif + +/* -------------------- END SYSTEM/COMPILER SPECIFIC --------------------- */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_h_ */ + diff --git a/apps/exampleViewer/common/glfw/include/GLFW/glfw3native.h b/apps/exampleViewer/common/glfw/include/GLFW/glfw3native.h new file mode 100644 index 0000000000..056bc82927 --- /dev/null +++ b/apps/exampleViewer/common/glfw/include/GLFW/glfw3native.h @@ -0,0 +1,456 @@ +/************************************************************************* + * GLFW 3.3 - www.glfw.org + * A library for OpenGL, window and input + *------------------------------------------------------------------------ + * Copyright (c) 2002-2006 Marcus Geelnard + * Copyright (c) 2006-2016 Camilla Berglund + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would + * be appreciated but is not required. + * + * 2. Altered source versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * 3. This notice may not be removed or altered from any source + * distribution. + * + *************************************************************************/ + +#ifndef _glfw3_native_h_ +#define _glfw3_native_h_ + +#ifdef __cplusplus +extern "C" { +#endif + + +/************************************************************************* + * Doxygen documentation + *************************************************************************/ + +/*! @file glfw3native.h + * @brief The header of the native access functions. + * + * This is the header file of the native access functions. See @ref native for + * more information. + */ +/*! @defgroup native Native access + * + * **By using the native access functions you assert that you know what you're + * doing and how to fix problems caused by using them. If you don't, you + * shouldn't be using them.** + * + * Before the inclusion of @ref glfw3native.h, you may define exactly one + * window system API macro and zero or more context creation API macros. + * + * The chosen backends must match those the library was compiled for. Failure + * to do this will cause a link-time error. + * + * The available window API macros are: + * * `GLFW_EXPOSE_NATIVE_WIN32` + * * `GLFW_EXPOSE_NATIVE_COCOA` + * * `GLFW_EXPOSE_NATIVE_X11` + * * `GLFW_EXPOSE_NATIVE_WAYLAND` + * * `GLFW_EXPOSE_NATIVE_MIR` + * + * The available context API macros are: + * * `GLFW_EXPOSE_NATIVE_WGL` + * * `GLFW_EXPOSE_NATIVE_NSGL` + * * `GLFW_EXPOSE_NATIVE_GLX` + * * `GLFW_EXPOSE_NATIVE_EGL` + * + * These macros select which of the native access functions that are declared + * and which platform-specific headers to include. It is then up your (by + * definition platform-specific) code to handle which of these should be + * defined. + */ + + +/************************************************************************* + * System headers and types + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) + // This is a workaround for the fact that glfw3.h needs to export APIENTRY (for + // example to allow applications to correctly declare a GL_ARB_debug_output + // callback) but windows.h assumes no one will define APIENTRY before it does + #undef APIENTRY + #include +#elif defined(GLFW_EXPOSE_NATIVE_COCOA) + #include + #if defined(__OBJC__) + #import + #else + typedef void* id; + #endif +#elif defined(GLFW_EXPOSE_NATIVE_X11) + #include + #include +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include +#elif defined(GLFW_EXPOSE_NATIVE_MIR) + #include +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) + /* WGL is declared by windows.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_NSGL) + /* NSGL is declared by Cocoa.h */ +#endif +#if defined(GLFW_EXPOSE_NATIVE_GLX) + #include +#endif +#if defined(GLFW_EXPOSE_NATIVE_EGL) + #include +#endif + + +/************************************************************************* + * Functions + *************************************************************************/ + +#if defined(GLFW_EXPOSE_NATIVE_WIN32) +/*! @brief Returns the adapter device name of the specified monitor. + * + * @return The UTF-8 encoded adapter device name (for example `\\.\DISPLAY1`) + * of the specified monitor, or `NULL` if an [error](@ref error_handling) + * occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the display device name of the specified monitor. + * + * @return The UTF-8 encoded display device name (for example + * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `HWND` of the specified window. + * + * @return The `HWND` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WGL) +/*! @brief Returns the `HGLRC` of the specified window. + * + * @return The `HGLRC` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_COCOA) +/*! @brief Returns the `CGDirectDisplayID` of the specified monitor. + * + * @return The `CGDirectDisplayID` of the specified monitor, or + * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `NSWindow` of the specified window. + * + * @return The `NSWindow` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_NSGL) +/*! @brief Returns the `NSOpenGLContext` of the specified window. + * + * @return The `NSOpenGLContext` of the specified window, or `nil` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_X11) +/*! @brief Returns the `Display` used by GLFW. + * + * @return The `Display` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Display* glfwGetX11Display(void); + +/*! @brief Returns the `RRCrtc` of the specified monitor. + * + * @return The `RRCrtc` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); + +/*! @brief Returns the `RROutput` of the specified monitor. + * + * @return The `RROutput` of the specified monitor, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.1. + * + * @ingroup native + */ +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); + +/*! @brief Returns the `Window` of the specified window. + * + * @return The `Window` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI Window glfwGetX11Window(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_GLX) +/*! @brief Returns the `GLXContext` of the specified window. + * + * @return The `GLXContext` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); + +/*! @brief Returns the `GLXWindow` of the specified window. + * + * @return The `GLXWindow` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) +/*! @brief Returns the `struct wl_display*` used by GLFW. + * + * @return The `struct wl_display*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); + +/*! @brief Returns the `struct wl_output*` of the specified monitor. + * + * @return The `struct wl_output*` of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the main `struct wl_surface*` of the specified window. + * + * @return The main `struct wl_surface*` of the specified window, or `NULL` if + * an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_MIR) +/*! @brief Returns the `MirConnection*` used by GLFW. + * + * @return The `MirConnection*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI MirConnection* glfwGetMirDisplay(void); + +/*! @brief Returns the Mir output ID of the specified monitor. + * + * @return The Mir output ID of the specified monitor, or zero if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `MirSurface*` of the specified window. + * + * @return The `MirSurface*` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_EGL) +/*! @brief Returns the `EGLDisplay` used by GLFW. + * + * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLDisplay glfwGetEGLDisplay(void); + +/*! @brief Returns the `EGLContext` of the specified window. + * + * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); + +/*! @brief Returns the `EGLSurface` of the specified window. + * + * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.0. + * + * @ingroup native + */ +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* window); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _glfw3_native_h_ */ + diff --git a/apps/exampleViewer/common/glfw/src/CMakeLists.txt b/apps/exampleViewer/common/glfw/src/CMakeLists.txt new file mode 100644 index 0000000000..5042aba382 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/CMakeLists.txt @@ -0,0 +1,120 @@ + +set(common_HEADERS internal.h + "${GLFW_BINARY_DIR}/src/glfw_config.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3.h" + "${GLFW_SOURCE_DIR}/include/GLFW/glfw3native.h") +set(common_SOURCES context.c init.c input.c monitor.c vulkan.c window.c) + +if (_GLFW_COCOA) + set(glfw_HEADERS ${common_HEADERS} cocoa_platform.h cocoa_joystick.h + posix_tls.h nsgl_context.h) + set(glfw_SOURCES ${common_SOURCES} cocoa_init.m cocoa_joystick.m + cocoa_monitor.m cocoa_window.m cocoa_time.c posix_tls.c + nsgl_context.m) +elseif (_GLFW_WIN32) + set(glfw_HEADERS ${common_HEADERS} win32_platform.h win32_joystick.h + wgl_context.h egl_context.h) + set(glfw_SOURCES ${common_SOURCES} win32_init.c win32_joystick.c + win32_monitor.c win32_time.c win32_tls.c win32_window.c + wgl_context.c egl_context.c) +elseif (_GLFW_X11) + set(glfw_HEADERS ${common_HEADERS} x11_platform.h xkb_unicode.h + linux_joystick.h posix_time.h posix_tls.h glx_context.h + egl_context.h) + set(glfw_SOURCES ${common_SOURCES} x11_init.c x11_monitor.c x11_window.c + xkb_unicode.c linux_joystick.c posix_time.c posix_tls.c + glx_context.c egl_context.c) +elseif (_GLFW_WAYLAND) + set(glfw_HEADERS ${common_HEADERS} wl_platform.h linux_joystick.h + posix_time.h posix_tls.h xkb_unicode.h egl_context.h) + set(glfw_SOURCES ${common_SOURCES} wl_init.c wl_monitor.c wl_window.c + linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c + egl_context.c) + + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/relative-pointer/relative-pointer-unstable-v1.xml + BASENAME relative-pointer-unstable-v1) + ecm_add_wayland_client_protocol(glfw_SOURCES + PROTOCOL + ${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml + BASENAME pointer-constraints-unstable-v1) +elseif (_GLFW_MIR) + set(glfw_HEADERS ${common_HEADERS} mir_platform.h linux_joystick.h + posix_time.h posix_tls.h xkb_unicode.h egl_context.h) + set(glfw_SOURCES ${common_SOURCES} mir_init.c mir_monitor.c mir_window.c + linux_joystick.c posix_time.c posix_tls.c xkb_unicode.c + egl_context.c) +endif() + +if (APPLE) + # For some reason, CMake doesn't know about .m + set_source_files_properties(${glfw_SOURCES} PROPERTIES LANGUAGE C) +endif() + +add_library(glfw ${glfw_SOURCES} ${glfw_HEADERS}) +set_target_properties(glfw PROPERTIES + OUTPUT_NAME ${GLFW_LIB_NAME} + VERSION ${GLFW_VERSION} + SOVERSION ${GLFW_VERSION_MAJOR} + POSITION_INDEPENDENT_CODE ON + FOLDER "GLFW3") + +target_compile_definitions(glfw PRIVATE -D_GLFW_USE_CONFIG_H) +target_include_directories(glfw PUBLIC + $ + $/include>) +target_include_directories(glfw PRIVATE + "${GLFW_SOURCE_DIR}/src" + "${GLFW_BINARY_DIR}/src" + ${glfw_INCLUDE_DIRS}) + +# HACK: When building on MinGW, WINVER and UNICODE need to be defined before +# the inclusion of stddef.h (by glfw3.h), which is itself included before +# win32_platform.h. We define them here until a saner solution can be found +# NOTE: MinGW-w64 and Visual C++ do /not/ need this hack. +target_compile_definitions(glfw PRIVATE + "$<$:UNICODE;WINVER=0x0501>") + +# Enable a reasonable set of warnings (no, -Wextra is not reasonable) +target_compile_options(glfw PRIVATE + "$<$:-Wall>" + "$<$:-Wall>") + +if (BUILD_SHARED_LIBS) + if (WIN32) + if (MINGW) + # Remove the lib prefix on the DLL (but not the import library + set_target_properties(glfw PROPERTIES PREFIX "") + + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw PROPERTIES IMPORT_SUFFIX "dll.a") + else() + # Add a suffix to the import library to avoid naming conflicts + set_target_properties(glfw PROPERTIES IMPORT_SUFFIX "dll.lib") + endif() + elseif (APPLE) + # Add -fno-common to work around a bug in Apple's GCC + target_compile_options(glfw PRIVATE "-fno-common") + + set_target_properties(glfw PROPERTIES + INSTALL_NAME_DIR "lib${LIB_SUFFIX}") + elseif (UNIX) + # Hide symbols not explicitly tagged for export from the shared library + target_compile_options(glfw PRIVATE "-fvisibility=hidden") + endif() + + target_compile_definitions(glfw INTERFACE -DGLFW_DLL) + target_link_libraries(glfw PRIVATE ${glfw_LIBRARIES}) +else() + target_link_libraries(glfw INTERFACE ${glfw_LIBRARIES}) +endif() + +if (MSVC) + target_compile_definitions(glfw PRIVATE _CRT_SECURE_NO_WARNINGS) +endif() + +if (GLFW_INSTALL) + install(TARGETS glfw EXPORT glfwTargets DESTINATION lib${LIB_SUFFIX}) +endif() + diff --git a/apps/exampleViewer/common/glfw/src/cocoa_init.m b/apps/exampleViewer/common/glfw/src/cocoa_init.m new file mode 100644 index 0000000000..fba749dbc6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_init.m @@ -0,0 +1,398 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" +#include // For MAXPATHLEN + + +#if defined(_GLFW_USE_CHDIR) + +// Change to our application bundle's resources directory, if present +// +static void changeToResourcesDirectory(void) +{ + char resourcesPath[MAXPATHLEN]; + + CFBundleRef bundle = CFBundleGetMainBundle(); + if (!bundle) + return; + + CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(bundle); + + CFStringRef last = CFURLCopyLastPathComponent(resourcesURL); + if (CFStringCompare(CFSTR("Resources"), last, 0) != kCFCompareEqualTo) + { + CFRelease(last); + CFRelease(resourcesURL); + return; + } + + CFRelease(last); + + if (!CFURLGetFileSystemRepresentation(resourcesURL, + true, + (UInt8*) resourcesPath, + MAXPATHLEN)) + { + CFRelease(resourcesURL); + return; + } + + CFRelease(resourcesURL); + + chdir(resourcesPath); +} + +#endif /* _GLFW_USE_CHDIR */ + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.ns.keycodes, -1, sizeof(_glfw.ns.keycodes)); + memset(_glfw.ns.scancodes, -1, sizeof(_glfw.ns.scancodes)); + + _glfw.ns.keycodes[0x1D] = GLFW_KEY_0; + _glfw.ns.keycodes[0x12] = GLFW_KEY_1; + _glfw.ns.keycodes[0x13] = GLFW_KEY_2; + _glfw.ns.keycodes[0x14] = GLFW_KEY_3; + _glfw.ns.keycodes[0x15] = GLFW_KEY_4; + _glfw.ns.keycodes[0x17] = GLFW_KEY_5; + _glfw.ns.keycodes[0x16] = GLFW_KEY_6; + _glfw.ns.keycodes[0x1A] = GLFW_KEY_7; + _glfw.ns.keycodes[0x1C] = GLFW_KEY_8; + _glfw.ns.keycodes[0x19] = GLFW_KEY_9; + _glfw.ns.keycodes[0x00] = GLFW_KEY_A; + _glfw.ns.keycodes[0x0B] = GLFW_KEY_B; + _glfw.ns.keycodes[0x08] = GLFW_KEY_C; + _glfw.ns.keycodes[0x02] = GLFW_KEY_D; + _glfw.ns.keycodes[0x0E] = GLFW_KEY_E; + _glfw.ns.keycodes[0x03] = GLFW_KEY_F; + _glfw.ns.keycodes[0x05] = GLFW_KEY_G; + _glfw.ns.keycodes[0x04] = GLFW_KEY_H; + _glfw.ns.keycodes[0x22] = GLFW_KEY_I; + _glfw.ns.keycodes[0x26] = GLFW_KEY_J; + _glfw.ns.keycodes[0x28] = GLFW_KEY_K; + _glfw.ns.keycodes[0x25] = GLFW_KEY_L; + _glfw.ns.keycodes[0x2E] = GLFW_KEY_M; + _glfw.ns.keycodes[0x2D] = GLFW_KEY_N; + _glfw.ns.keycodes[0x1F] = GLFW_KEY_O; + _glfw.ns.keycodes[0x23] = GLFW_KEY_P; + _glfw.ns.keycodes[0x0C] = GLFW_KEY_Q; + _glfw.ns.keycodes[0x0F] = GLFW_KEY_R; + _glfw.ns.keycodes[0x01] = GLFW_KEY_S; + _glfw.ns.keycodes[0x11] = GLFW_KEY_T; + _glfw.ns.keycodes[0x20] = GLFW_KEY_U; + _glfw.ns.keycodes[0x09] = GLFW_KEY_V; + _glfw.ns.keycodes[0x0D] = GLFW_KEY_W; + _glfw.ns.keycodes[0x07] = GLFW_KEY_X; + _glfw.ns.keycodes[0x10] = GLFW_KEY_Y; + _glfw.ns.keycodes[0x06] = GLFW_KEY_Z; + + _glfw.ns.keycodes[0x27] = GLFW_KEY_APOSTROPHE; + _glfw.ns.keycodes[0x2A] = GLFW_KEY_BACKSLASH; + _glfw.ns.keycodes[0x2B] = GLFW_KEY_COMMA; + _glfw.ns.keycodes[0x18] = GLFW_KEY_EQUAL; + _glfw.ns.keycodes[0x32] = GLFW_KEY_GRAVE_ACCENT; + _glfw.ns.keycodes[0x21] = GLFW_KEY_LEFT_BRACKET; + _glfw.ns.keycodes[0x1B] = GLFW_KEY_MINUS; + _glfw.ns.keycodes[0x2F] = GLFW_KEY_PERIOD; + _glfw.ns.keycodes[0x1E] = GLFW_KEY_RIGHT_BRACKET; + _glfw.ns.keycodes[0x29] = GLFW_KEY_SEMICOLON; + _glfw.ns.keycodes[0x2C] = GLFW_KEY_SLASH; + _glfw.ns.keycodes[0x0A] = GLFW_KEY_WORLD_1; + + _glfw.ns.keycodes[0x33] = GLFW_KEY_BACKSPACE; + _glfw.ns.keycodes[0x39] = GLFW_KEY_CAPS_LOCK; + _glfw.ns.keycodes[0x75] = GLFW_KEY_DELETE; + _glfw.ns.keycodes[0x7D] = GLFW_KEY_DOWN; + _glfw.ns.keycodes[0x77] = GLFW_KEY_END; + _glfw.ns.keycodes[0x24] = GLFW_KEY_ENTER; + _glfw.ns.keycodes[0x35] = GLFW_KEY_ESCAPE; + _glfw.ns.keycodes[0x7A] = GLFW_KEY_F1; + _glfw.ns.keycodes[0x78] = GLFW_KEY_F2; + _glfw.ns.keycodes[0x63] = GLFW_KEY_F3; + _glfw.ns.keycodes[0x76] = GLFW_KEY_F4; + _glfw.ns.keycodes[0x60] = GLFW_KEY_F5; + _glfw.ns.keycodes[0x61] = GLFW_KEY_F6; + _glfw.ns.keycodes[0x62] = GLFW_KEY_F7; + _glfw.ns.keycodes[0x64] = GLFW_KEY_F8; + _glfw.ns.keycodes[0x65] = GLFW_KEY_F9; + _glfw.ns.keycodes[0x6D] = GLFW_KEY_F10; + _glfw.ns.keycodes[0x67] = GLFW_KEY_F11; + _glfw.ns.keycodes[0x6F] = GLFW_KEY_F12; + _glfw.ns.keycodes[0x69] = GLFW_KEY_F13; + _glfw.ns.keycodes[0x6B] = GLFW_KEY_F14; + _glfw.ns.keycodes[0x71] = GLFW_KEY_F15; + _glfw.ns.keycodes[0x6A] = GLFW_KEY_F16; + _glfw.ns.keycodes[0x40] = GLFW_KEY_F17; + _glfw.ns.keycodes[0x4F] = GLFW_KEY_F18; + _glfw.ns.keycodes[0x50] = GLFW_KEY_F19; + _glfw.ns.keycodes[0x5A] = GLFW_KEY_F20; + _glfw.ns.keycodes[0x73] = GLFW_KEY_HOME; + _glfw.ns.keycodes[0x72] = GLFW_KEY_INSERT; + _glfw.ns.keycodes[0x7B] = GLFW_KEY_LEFT; + _glfw.ns.keycodes[0x3A] = GLFW_KEY_LEFT_ALT; + _glfw.ns.keycodes[0x3B] = GLFW_KEY_LEFT_CONTROL; + _glfw.ns.keycodes[0x38] = GLFW_KEY_LEFT_SHIFT; + _glfw.ns.keycodes[0x37] = GLFW_KEY_LEFT_SUPER; + _glfw.ns.keycodes[0x6E] = GLFW_KEY_MENU; + _glfw.ns.keycodes[0x47] = GLFW_KEY_NUM_LOCK; + _glfw.ns.keycodes[0x79] = GLFW_KEY_PAGE_DOWN; + _glfw.ns.keycodes[0x74] = GLFW_KEY_PAGE_UP; + _glfw.ns.keycodes[0x7C] = GLFW_KEY_RIGHT; + _glfw.ns.keycodes[0x3D] = GLFW_KEY_RIGHT_ALT; + _glfw.ns.keycodes[0x3E] = GLFW_KEY_RIGHT_CONTROL; + _glfw.ns.keycodes[0x3C] = GLFW_KEY_RIGHT_SHIFT; + _glfw.ns.keycodes[0x36] = GLFW_KEY_RIGHT_SUPER; + _glfw.ns.keycodes[0x31] = GLFW_KEY_SPACE; + _glfw.ns.keycodes[0x30] = GLFW_KEY_TAB; + _glfw.ns.keycodes[0x7E] = GLFW_KEY_UP; + + _glfw.ns.keycodes[0x52] = GLFW_KEY_KP_0; + _glfw.ns.keycodes[0x53] = GLFW_KEY_KP_1; + _glfw.ns.keycodes[0x54] = GLFW_KEY_KP_2; + _glfw.ns.keycodes[0x55] = GLFW_KEY_KP_3; + _glfw.ns.keycodes[0x56] = GLFW_KEY_KP_4; + _glfw.ns.keycodes[0x57] = GLFW_KEY_KP_5; + _glfw.ns.keycodes[0x58] = GLFW_KEY_KP_6; + _glfw.ns.keycodes[0x59] = GLFW_KEY_KP_7; + _glfw.ns.keycodes[0x5B] = GLFW_KEY_KP_8; + _glfw.ns.keycodes[0x5C] = GLFW_KEY_KP_9; + _glfw.ns.keycodes[0x45] = GLFW_KEY_KP_ADD; + _glfw.ns.keycodes[0x41] = GLFW_KEY_KP_DECIMAL; + _glfw.ns.keycodes[0x4B] = GLFW_KEY_KP_DIVIDE; + _glfw.ns.keycodes[0x4C] = GLFW_KEY_KP_ENTER; + _glfw.ns.keycodes[0x51] = GLFW_KEY_KP_EQUAL; + _glfw.ns.keycodes[0x43] = GLFW_KEY_KP_MULTIPLY; + _glfw.ns.keycodes[0x4E] = GLFW_KEY_KP_SUBTRACT; + + for (scancode = 0; scancode < 256; scancode++) + { + // Store the reverse translation for faster key name lookup + if (_glfw.ns.keycodes[scancode] >= 0) + _glfw.ns.scancodes[_glfw.ns.keycodes[scancode]] = scancode; + } +} + +// Retrieve Unicode data for the current keyboard layout +// +static GLFWbool updateUnicodeDataNS(void) +{ + if (_glfw.ns.inputSource) + { + CFRelease(_glfw.ns.inputSource); + _glfw.ns.inputSource = NULL; + _glfw.ns.unicodeData = nil; + } + + _glfw.ns.inputSource = TISCopyCurrentKeyboardLayoutInputSource(); + if (!_glfw.ns.inputSource) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve keyboard layout input source"); + return GLFW_FALSE; + } + + _glfw.ns.unicodeData = TISGetInputSourceProperty(_glfw.ns.inputSource, + kTISPropertyUnicodeKeyLayoutData); + if (!_glfw.ns.unicodeData) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve keyboard layout Unicode data"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Load HIToolbox.framework and the TIS symbols we need from it +// +static GLFWbool initializeTIS(void) +{ + // This works only because Cocoa has already loaded it properly + _glfw.ns.tis.bundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.HIToolbox")); + if (!_glfw.ns.tis.bundle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to load HIToolbox.framework"); + return GLFW_FALSE; + } + + CFStringRef* kPropertyUnicodeKeyLayoutData = + CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, + CFSTR("kTISPropertyUnicodeKeyLayoutData")); + CFStringRef* kNotifySelectedKeyboardInputSourceChanged = + CFBundleGetDataPointerForName(_glfw.ns.tis.bundle, + CFSTR("kTISNotifySelectedKeyboardInputSourceChanged")); + _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("TISCopyCurrentKeyboardLayoutInputSource")); + _glfw.ns.tis.GetInputSourceProperty = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("TISGetInputSourceProperty")); + _glfw.ns.tis.GetKbdType = + CFBundleGetFunctionPointerForName(_glfw.ns.tis.bundle, + CFSTR("LMGetKbdType")); + + if (!kPropertyUnicodeKeyLayoutData || + !kNotifySelectedKeyboardInputSourceChanged || + !TISCopyCurrentKeyboardLayoutInputSource || + !TISGetInputSourceProperty || + !LMGetKbdType) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to load TIS API symbols"); + return GLFW_FALSE; + } + + _glfw.ns.tis.kPropertyUnicodeKeyLayoutData = + *kPropertyUnicodeKeyLayoutData; + _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged = + *kNotifySelectedKeyboardInputSourceChanged; + + return updateUnicodeDataNS(); +} + +@interface GLFWLayoutListener : NSObject +@end + +@implementation GLFWLayoutListener + +- (void)selectedKeyboardInputSourceChanged:(NSObject* )object +{ + updateUnicodeDataNS(); +} + +@end + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; + + _glfw.ns.listener = [[GLFWLayoutListener alloc] init]; + [[NSDistributedNotificationCenter defaultCenter] + addObserver:_glfw.ns.listener + selector:@selector(selectedKeyboardInputSourceChanged:) + name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + object:nil]; + +#if defined(_GLFW_USE_CHDIR) + changeToResourcesDirectory(); +#endif + + createKeyTables(); + + _glfw.ns.eventSource = CGEventSourceCreate(kCGEventSourceStateHIDSystemState); + if (!_glfw.ns.eventSource) + return GLFW_FALSE; + + CGEventSourceSetLocalEventsSuppressionInterval(_glfw.ns.eventSource, 0.0); + + if (!initializeTIS()) + return GLFW_FALSE; + + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; + + _glfwInitTimerNS(); + _glfwInitJoysticksNS(); + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + if (_glfw.ns.inputSource) + { + CFRelease(_glfw.ns.inputSource); + _glfw.ns.inputSource = NULL; + _glfw.ns.unicodeData = nil; + } + + if (_glfw.ns.eventSource) + { + CFRelease(_glfw.ns.eventSource); + _glfw.ns.eventSource = NULL; + } + + if (_glfw.ns.delegate) + { + [NSApp setDelegate:nil]; + [_glfw.ns.delegate release]; + _glfw.ns.delegate = nil; + } + + if (_glfw.ns.listener) + { + [[NSDistributedNotificationCenter defaultCenter] + removeObserver:_glfw.ns.listener + name:(__bridge NSString*)kTISNotifySelectedKeyboardInputSourceChanged + object:nil]; + [[NSDistributedNotificationCenter defaultCenter] + removeObserver:_glfw.ns.listener]; + [_glfw.ns.listener release]; + _glfw.ns.listener = nil; + } + + [_glfw.ns.cursor release]; + _glfw.ns.cursor = nil; + + free(_glfw.ns.clipboardString); + + _glfwTerminateNSGL(); + _glfwTerminateJoysticksNS(); + _glfwTerminateThreadLocalStoragePOSIX(); + + [_glfw.ns.autoreleasePool release]; + _glfw.ns.autoreleasePool = nil; +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Cocoa NSGL" +#if defined(_GLFW_USE_CHDIR) + " chdir" +#endif +#if defined(_GLFW_USE_MENUBAR) + " menubar" +#endif +#if defined(_GLFW_USE_RETINA) + " retina" +#endif +#if defined(_GLFW_BUILD_DLL) + " dynamic" +#endif + ; +} + diff --git a/apps/exampleViewer/common/glfw/src/cocoa_joystick.h b/apps/exampleViewer/common/glfw/src/cocoa_joystick.h new file mode 100644 index 0000000000..9a59101533 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_joystick.h @@ -0,0 +1,60 @@ +//======================================================================== +// GLFW 3.3 Cocoa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_cocoa_joystick_h_ +#define _glfw3_cocoa_joystick_h_ + +#include +#include +#include +#include + +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ + _GLFWjoystickNS ns_js[GLFW_JOYSTICK_LAST + 1] + + +// Cocoa-specific per-joystick data +// +typedef struct _GLFWjoystickNS +{ + GLFWbool present; + char name[256]; + + IOHIDDeviceRef deviceRef; + + CFMutableArrayRef axisElements; + CFMutableArrayRef buttonElements; + CFMutableArrayRef hatElements; + + float* axes; + unsigned char* buttons; +} _GLFWjoystickNS; + + +void _glfwInitJoysticksNS(void); +void _glfwTerminateJoysticksNS(void); + +#endif // _glfw3_cocoa_joystick_h_ diff --git a/apps/exampleViewer/common/glfw/src/cocoa_joystick.m b/apps/exampleViewer/common/glfw/src/cocoa_joystick.m new file mode 100644 index 0000000000..72cebcfcd2 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_joystick.m @@ -0,0 +1,511 @@ +//======================================================================== +// GLFW 3.3 Cocoa - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// Copyright (c) 2012 Torsten Walluhn +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#include +#include + +#include +#include + + +// Joystick element information +// +typedef struct _GLFWjoyelementNS +{ + IOHIDElementRef elementRef; + + long min; + long max; + + long minReport; + long maxReport; + +} _GLFWjoyelementNS; + + +static void getElementsCFArrayHandler(const void* value, void* parameter); + +// Adds an element to the specified joystick +// +static void addJoystickElement(_GLFWjoystickNS* js, + IOHIDElementRef elementRef) +{ + IOHIDElementType elementType; + long usagePage, usage; + CFMutableArrayRef elementsArray = NULL; + + elementType = IOHIDElementGetType(elementRef); + usagePage = IOHIDElementGetUsagePage(elementRef); + usage = IOHIDElementGetUsage(elementRef); + + if ((elementType != kIOHIDElementTypeInput_Axis) && + (elementType != kIOHIDElementTypeInput_Button) && + (elementType != kIOHIDElementTypeInput_Misc)) + { + return; + } + + switch (usagePage) + { + case kHIDPage_GenericDesktop: + { + switch (usage) + { + case kHIDUsage_GD_X: + case kHIDUsage_GD_Y: + case kHIDUsage_GD_Z: + case kHIDUsage_GD_Rx: + case kHIDUsage_GD_Ry: + case kHIDUsage_GD_Rz: + case kHIDUsage_GD_Slider: + case kHIDUsage_GD_Dial: + case kHIDUsage_GD_Wheel: + elementsArray = js->axisElements; + break; + case kHIDUsage_GD_Hatswitch: + elementsArray = js->hatElements; + break; + } + + break; + } + + case kHIDPage_Button: + elementsArray = js->buttonElements; + break; + default: + break; + } + + if (elementsArray) + { + _GLFWjoyelementNS* element = calloc(1, sizeof(_GLFWjoyelementNS)); + + CFArrayAppendValue(elementsArray, element); + + element->elementRef = elementRef; + + element->minReport = IOHIDElementGetLogicalMin(elementRef); + element->maxReport = IOHIDElementGetLogicalMax(elementRef); + } +} + +// Adds an element to the specified joystick +// +static void getElementsCFArrayHandler(const void* value, void* parameter) +{ + if (CFGetTypeID(value) == IOHIDElementGetTypeID()) + { + addJoystickElement((_GLFWjoystickNS*) parameter, + (IOHIDElementRef) value); + } +} + +// Returns the value of the specified element of the specified joystick +// +static long getElementValue(_GLFWjoystickNS* js, _GLFWjoyelementNS* element) +{ + IOReturn result = kIOReturnSuccess; + IOHIDValueRef valueRef; + long value = 0; + + if (js && element && js->deviceRef) + { + result = IOHIDDeviceGetValue(js->deviceRef, + element->elementRef, + &valueRef); + + if (kIOReturnSuccess == result) + { + value = IOHIDValueGetIntegerValue(valueRef); + + // Record min and max for auto calibration + if (value < element->minReport) + element->minReport = value; + if (value > element->maxReport) + element->maxReport = value; + } + } + + // Auto user scale + return value; +} + +// Removes the specified joystick +// +static void removeJoystick(_GLFWjoystickNS* js) +{ + int i; + + if (!js->present) + return; + + for (i = 0; i < CFArrayGetCount(js->axisElements); i++) + free((void*) CFArrayGetValueAtIndex(js->axisElements, i)); + CFArrayRemoveAllValues(js->axisElements); + CFRelease(js->axisElements); + + for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) + free((void*) CFArrayGetValueAtIndex(js->buttonElements, i)); + CFArrayRemoveAllValues(js->buttonElements); + CFRelease(js->buttonElements); + + for (i = 0; i < CFArrayGetCount(js->hatElements); i++) + free((void*) CFArrayGetValueAtIndex(js->hatElements, i)); + CFArrayRemoveAllValues(js->hatElements); + CFRelease(js->hatElements); + + free(js->axes); + free(js->buttons); + + memset(js, 0, sizeof(_GLFWjoystickNS)); + + _glfwInputJoystickChange(js - _glfw.ns_js, GLFW_DISCONNECTED); +} + +// Polls for joystick axis events and updates GLFW state +// +static GLFWbool pollJoystickAxisEvents(_GLFWjoystickNS* js) +{ + CFIndex i; + + if (!js->present) + return GLFW_FALSE; + + for (i = 0; i < CFArrayGetCount(js->axisElements); i++) + { + _GLFWjoyelementNS* axis = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->axisElements, i); + + long value = getElementValue(js, axis); + long readScale = axis->maxReport - axis->minReport; + + if (readScale == 0) + js->axes[i] = value; + else + js->axes[i] = (2.f * (value - axis->minReport) / readScale) - 1.f; + } + + return GLFW_TRUE; +} + +// Polls for joystick button events and updates GLFW state +// +static GLFWbool pollJoystickButtonEvents(_GLFWjoystickNS* js) +{ + CFIndex i; + int buttonIndex = 0; + + if (!js->present) + return GLFW_FALSE; + + for (i = 0; i < CFArrayGetCount(js->buttonElements); i++) + { + _GLFWjoyelementNS* button = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->buttonElements, i); + + if (getElementValue(js, button)) + js->buttons[buttonIndex++] = GLFW_PRESS; + else + js->buttons[buttonIndex++] = GLFW_RELEASE; + } + + for (i = 0; i < CFArrayGetCount(js->hatElements); i++) + { + _GLFWjoyelementNS* hat = (_GLFWjoyelementNS*) + CFArrayGetValueAtIndex(js->hatElements, i); + + // Bit fields of button presses for each direction, including nil + const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; + + long j, value = getElementValue(js, hat); + if (value < 0 || value > 8) + value = 8; + + for (j = 0; j < 4; j++) + { + if (directions[value] & (1 << j)) + js->buttons[buttonIndex++] = GLFW_PRESS; + else + js->buttons[buttonIndex++] = GLFW_RELEASE; + } + } + + return GLFW_TRUE; +} + +// Callback for user-initiated joystick addition +// +static void matchCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef deviceRef) +{ + _GLFWjoystickNS* js; + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.ns_js[jid].present && _glfw.ns_js[jid].deviceRef == deviceRef) + return; + } + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.ns_js[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return; + + js = _glfw.ns_js + jid; + js->present = GLFW_TRUE; + js->deviceRef = deviceRef; + + CFStringRef name = IOHIDDeviceGetProperty(deviceRef, + CFSTR(kIOHIDProductKey)); + if (name) + { + CFStringGetCString(name, + js->name, + sizeof(js->name), + kCFStringEncodingUTF8); + } + else + strncpy(js->name, "Unknown", sizeof(js->name)); + + js->axisElements = CFArrayCreateMutable(NULL, 0, NULL); + js->buttonElements = CFArrayCreateMutable(NULL, 0, NULL); + js->hatElements = CFArrayCreateMutable(NULL, 0, NULL); + + CFArrayRef arrayRef = IOHIDDeviceCopyMatchingElements(deviceRef, + NULL, + kIOHIDOptionsTypeNone); + CFRange range = { 0, CFArrayGetCount(arrayRef) }; + CFArrayApplyFunction(arrayRef, + range, + getElementsCFArrayHandler, + (void*) js); + + CFRelease(arrayRef); + + js->axes = calloc(CFArrayGetCount(js->axisElements), sizeof(float)); + js->buttons = calloc(CFArrayGetCount(js->buttonElements) + + CFArrayGetCount(js->hatElements) * 4, 1); + + _glfwInputJoystickChange(jid, GLFW_CONNECTED); +} + +// Callback for user-initiated joystick removal +// +static void removeCallback(void* context, + IOReturn result, + void* sender, + IOHIDDeviceRef deviceRef) +{ + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.ns_js[jid].deviceRef == deviceRef) + { + removeJoystick(_glfw.ns_js + jid); + break; + } + } +} + +// Creates a dictionary to match against devices with the specified usage page +// and usage +// +static CFMutableDictionaryRef createMatchingDictionary(long usagePage, + long usage) +{ + CFMutableDictionaryRef result = + CFDictionaryCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + if (result) + { + CFNumberRef pageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &usagePage); + if (pageRef) + { + CFDictionarySetValue(result, + CFSTR(kIOHIDDeviceUsagePageKey), + pageRef); + CFRelease(pageRef); + + CFNumberRef usageRef = CFNumberCreate(kCFAllocatorDefault, + kCFNumberLongType, + &usage); + if (usageRef) + { + CFDictionarySetValue(result, + CFSTR(kIOHIDDeviceUsageKey), + usageRef); + CFRelease(usageRef); + } + } + } + + return result; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +void _glfwInitJoysticksNS(void) +{ + CFMutableArrayRef matchingCFArrayRef; + + _glfw.ns.hidManager = IOHIDManagerCreate(kCFAllocatorDefault, + kIOHIDOptionsTypeNone); + + matchingCFArrayRef = CFArrayCreateMutable(kCFAllocatorDefault, + 0, + &kCFTypeArrayCallBacks); + if (matchingCFArrayRef) + { + CFDictionaryRef matchingCFDictRef = + createMatchingDictionary(kHIDPage_GenericDesktop, + kHIDUsage_GD_Joystick); + if (matchingCFDictRef) + { + CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); + CFRelease(matchingCFDictRef); + } + + matchingCFDictRef = createMatchingDictionary(kHIDPage_GenericDesktop, + kHIDUsage_GD_GamePad); + if (matchingCFDictRef) + { + CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); + CFRelease(matchingCFDictRef); + } + + matchingCFDictRef = + createMatchingDictionary(kHIDPage_GenericDesktop, + kHIDUsage_GD_MultiAxisController); + if (matchingCFDictRef) + { + CFArrayAppendValue(matchingCFArrayRef, matchingCFDictRef); + CFRelease(matchingCFDictRef); + } + + IOHIDManagerSetDeviceMatchingMultiple(_glfw.ns.hidManager, + matchingCFArrayRef); + CFRelease(matchingCFArrayRef); + } + + IOHIDManagerRegisterDeviceMatchingCallback(_glfw.ns.hidManager, + &matchCallback, NULL); + IOHIDManagerRegisterDeviceRemovalCallback(_glfw.ns.hidManager, + &removeCallback, NULL); + + IOHIDManagerScheduleWithRunLoop(_glfw.ns.hidManager, + CFRunLoopGetMain(), + kCFRunLoopDefaultMode); + + IOHIDManagerOpen(_glfw.ns.hidManager, kIOHIDOptionsTypeNone); + + // Execute the run loop once in order to register any initially-attached + // joysticks + CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0, false); +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksNS(void) +{ + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + _GLFWjoystickNS* js = _glfw.ns_js + jid; + removeJoystick(js); + } + + CFRelease(_glfw.ns.hidManager); + _glfw.ns.hidManager = NULL; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformJoystickPresent(int jid) +{ + _GLFWjoystickNS* js = _glfw.ns_js + jid; + return js->present; +} + +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) +{ + _GLFWjoystickNS* js = _glfw.ns_js + jid; + if (!pollJoystickAxisEvents(js)) + return NULL; + + *count = (int) CFArrayGetCount(js->axisElements); + return js->axes; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) +{ + _GLFWjoystickNS* js = _glfw.ns_js + jid; + if (!pollJoystickButtonEvents(js)) + return NULL; + + *count = (int) CFArrayGetCount(js->buttonElements) + + (int) CFArrayGetCount(js->hatElements) * 4; + return js->buttons; +} + +const char* _glfwPlatformGetJoystickName(int jid) +{ + _GLFWjoystickNS* js = _glfw.ns_js + jid; + if (!js->present) + return NULL; + + return js->name; +} + diff --git a/apps/exampleViewer/common/glfw/src/cocoa_monitor.m b/apps/exampleViewer/common/glfw/src/cocoa_monitor.m new file mode 100644 index 0000000000..0d6cb8fd8b --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_monitor.m @@ -0,0 +1,412 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +#include +#include +#include +#include + + +// Get the name of the specified display +// +static char* getDisplayName(CGDirectDisplayID displayID) +{ + char* name; + CFDictionaryRef info, names; + CFStringRef value; + CFIndex size; + + // NOTE: This uses a deprecated function because Apple has + // (as of January 2015) not provided any alternative + info = IODisplayCreateInfoDictionary(CGDisplayIOServicePort(displayID), + kIODisplayOnlyPreferredName); + names = CFDictionaryGetValue(info, CFSTR(kDisplayProductName)); + + if (!names || !CFDictionaryGetValueIfPresent(names, CFSTR("en_US"), + (const void**) &value)) + { + // This may happen if a desktop Mac is running headless + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve display name"); + + CFRelease(info); + return strdup("Unknown"); + } + + size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), + kCFStringEncodingUTF8); + name = calloc(size + 1, 1); + CFStringGetCString(value, name, size, kCFStringEncodingUTF8); + + CFRelease(info); + + return name; +} + +// Check whether the display mode should be included in enumeration +// +static GLFWbool modeIsGood(CGDisplayModeRef mode) +{ + uint32_t flags = CGDisplayModeGetIOFlags(mode); + + if (!(flags & kDisplayModeValidFlag) || !(flags & kDisplayModeSafeFlag)) + return GLFW_FALSE; + if (flags & kDisplayModeInterlacedFlag) + return GLFW_FALSE; + if (flags & kDisplayModeStretchedFlag) + return GLFW_FALSE; + + CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); + if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) && + CFStringCompare(format, CFSTR(IO32BitDirectPixels), 0)) + { + CFRelease(format); + return GLFW_FALSE; + } + + CFRelease(format); + return GLFW_TRUE; +} + +// Convert Core Graphics display mode to GLFW video mode +// +static GLFWvidmode vidmodeFromCGDisplayMode(CGDisplayModeRef mode, + CVDisplayLinkRef link) +{ + GLFWvidmode result; + result.width = (int) CGDisplayModeGetWidth(mode); + result.height = (int) CGDisplayModeGetHeight(mode); + result.refreshRate = (int) CGDisplayModeGetRefreshRate(mode); + + if (result.refreshRate == 0) + { + const CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(link); + if (!(time.flags & kCVTimeIsIndefinite)) + result.refreshRate = (int) (time.timeScale / (double) time.timeValue); + } + + CFStringRef format = CGDisplayModeCopyPixelEncoding(mode); + + if (CFStringCompare(format, CFSTR(IO16BitDirectPixels), 0) == 0) + { + result.redBits = 5; + result.greenBits = 5; + result.blueBits = 5; + } + else + { + result.redBits = 8; + result.greenBits = 8; + result.blueBits = 8; + } + + CFRelease(format); + return result; +} + +// Starts reservation for display fading +// +static CGDisplayFadeReservationToken beginFadeReservation(void) +{ + CGDisplayFadeReservationToken token = kCGDisplayFadeReservationInvalidToken; + + if (CGAcquireDisplayFadeReservation(5, &token) == kCGErrorSuccess) + CGDisplayFade(token, 0.3, kCGDisplayBlendNormal, kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, TRUE); + + return token; +} + +// Ends reservation for display fading +// +static void endFadeReservation(CGDisplayFadeReservationToken token) +{ + if (token != kCGDisplayFadeReservationInvalidToken) + { + CGDisplayFade(token, 0.5, kCGDisplayBlendSolidColor, kCGDisplayBlendNormal, 0.0, 0.0, 0.0, FALSE); + CGReleaseDisplayFadeReservation(token); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Change the current video mode +// +GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + CFArrayRef modes; + CFIndex count, i; + CVDisplayLinkRef link; + CGDisplayModeRef native = NULL; + GLFWvidmode current; + const GLFWvidmode* best; + + best = _glfwChooseVideoMode(monitor, desired); + _glfwPlatformGetVideoMode(monitor, ¤t); + if (_glfwCompareVideoModes(¤t, best) == 0) + return GLFW_TRUE; + + CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); + + modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); + count = CFArrayGetCount(modes); + + for (i = 0; i < count; i++) + { + CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (!modeIsGood(dm)) + continue; + + const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); + if (_glfwCompareVideoModes(best, &mode) == 0) + { + native = dm; + break; + } + } + + if (native) + { + if (monitor->ns.previousMode == NULL) + monitor->ns.previousMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); + + CGDisplayFadeReservationToken token = beginFadeReservation(); + CGDisplaySetDisplayMode(monitor->ns.displayID, native, NULL); + endFadeReservation(token); + } + + CFRelease(modes); + CVDisplayLinkRelease(link); + + if (!native) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Monitor mode list changed"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Restore the previously saved (original) video mode +// +void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor) +{ + if (monitor->ns.previousMode) + { + CGDisplayFadeReservationToken token = beginFadeReservation(); + CGDisplaySetDisplayMode(monitor->ns.displayID, + monitor->ns.previousMode, NULL); + endFadeReservation(token); + + CGDisplayModeRelease(monitor->ns.previousMode); + monitor->ns.previousMode = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + uint32_t i, found = 0, displayCount; + _GLFWmonitor** monitors; + CGDirectDisplayID* displays; + + *count = 0; + + CGGetOnlineDisplayList(0, NULL, &displayCount); + displays = calloc(displayCount, sizeof(CGDirectDisplayID)); + monitors = calloc(displayCount, sizeof(_GLFWmonitor*)); + + CGGetOnlineDisplayList(displayCount, displays, &displayCount); + + for (i = 0; i < displayCount; i++) + { + _GLFWmonitor* monitor; + + if (CGDisplayIsAsleep(displays[i])) + continue; + + const CGSize size = CGDisplayScreenSize(displays[i]); + char* name = getDisplayName(displays[i]); + + monitor = _glfwAllocMonitor(name, size.width, size.height); + monitor->ns.displayID = displays[i]; + monitor->ns.unitNumber = CGDisplayUnitNumber(displays[i]); + + free(name); + + found++; + monitors[found - 1] = monitor; + } + + free(displays); + + *count = found; + return monitors; +} + +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + // HACK: Compare unit numbers instead of display IDs to work around display + // replacement on machines with automatic graphics switching + return first->ns.unitNumber == second->ns.unitNumber; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + const CGRect bounds = CGDisplayBounds(monitor->ns.displayID); + + if (xpos) + *xpos = (int) bounds.origin.x; + if (ypos) + *ypos = (int) bounds.origin.y; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + CFArrayRef modes; + CFIndex found, i, j; + GLFWvidmode* result; + CVDisplayLinkRef link; + + *count = 0; + + CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); + + modes = CGDisplayCopyAllDisplayModes(monitor->ns.displayID, NULL); + found = CFArrayGetCount(modes); + result = calloc(found, sizeof(GLFWvidmode)); + + for (i = 0; i < found; i++) + { + CGDisplayModeRef dm = (CGDisplayModeRef) CFArrayGetValueAtIndex(modes, i); + if (!modeIsGood(dm)) + continue; + + const GLFWvidmode mode = vidmodeFromCGDisplayMode(dm, link); + + for (j = 0; j < *count; j++) + { + if (_glfwCompareVideoModes(result + j, &mode) == 0) + break; + } + + // Skip duplicate modes + if (i < *count) + continue; + + (*count)++; + result[*count - 1] = mode; + } + + CFRelease(modes); + CVDisplayLinkRelease(link); + return result; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode *mode) +{ + CGDisplayModeRef displayMode; + CVDisplayLinkRef link; + + CVDisplayLinkCreateWithCGDisplay(monitor->ns.displayID, &link); + + displayMode = CGDisplayCopyDisplayMode(monitor->ns.displayID); + *mode = vidmodeFromCGDisplayMode(displayMode, link); + CGDisplayModeRelease(displayMode); + + CVDisplayLinkRelease(link); +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + uint32_t i, size = CGDisplayGammaTableCapacity(monitor->ns.displayID); + CGGammaValue* values = calloc(size * 3, sizeof(CGGammaValue)); + + CGGetDisplayTransferByTable(monitor->ns.displayID, + size, + values, + values + size, + values + size * 2, + &size); + + _glfwAllocGammaArrays(ramp, size); + + for (i = 0; i < size; i++) + { + ramp->red[i] = (unsigned short) (values[i] * 65535); + ramp->green[i] = (unsigned short) (values[i + size] * 65535); + ramp->blue[i] = (unsigned short) (values[i + size * 2] * 65535); + } + + free(values); +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + int i; + CGGammaValue* values = calloc(ramp->size * 3, sizeof(CGGammaValue)); + + for (i = 0; i < ramp->size; i++) + { + values[i] = ramp->red[i] / 65535.f; + values[i + ramp->size] = ramp->green[i] / 65535.f; + values[i + ramp->size * 2] = ramp->blue[i] / 65535.f; + } + + CGSetDisplayTransferByTable(monitor->ns.displayID, + ramp->size, + values, + values + ramp->size, + values + ramp->size * 2); + + free(values); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(kCGNullDirectDisplay); + return monitor->ns.displayID; +} + diff --git a/apps/exampleViewer/common/glfw/src/cocoa_platform.h b/apps/exampleViewer/common/glfw/src/cocoa_platform.h new file mode 100644 index 0000000000..6147e538bb --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_platform.h @@ -0,0 +1,152 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_cocoa_platform_h_ +#define _glfw3_cocoa_platform_h_ + +#include +#include + +#if defined(__OBJC__) +#import +#import +#else +#include +#include +typedef void* id; +#endif + +#include "posix_tls.h" +#include "cocoa_joystick.h" +#include "nsgl_context.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowNS ns +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryNS ns +#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeNS ns_time +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorNS ns +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorNS ns + +#define _GLFW_EGL_CONTEXT_STATE +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE + +// HIToolbox.framework pointer typedefs +#define kTISPropertyUnicodeKeyLayoutData _glfw.ns.tis.kPropertyUnicodeKeyLayoutData +#define kTISNotifySelectedKeyboardInputSourceChanged _glfw.ns.tis.kNotifySelectedKeyboardInputSourceChanged +typedef TISInputSourceRef (*PFN_TISCopyCurrentKeyboardLayoutInputSource)(void); +#define TISCopyCurrentKeyboardLayoutInputSource _glfw.ns.tis.CopyCurrentKeyboardLayoutInputSource +typedef void* (*PFN_TISGetInputSourceProperty)(TISInputSourceRef,CFStringRef); +#define TISGetInputSourceProperty _glfw.ns.tis.GetInputSourceProperty +typedef UInt8 (*PFN_LMGetKbdType)(void); +#define LMGetKbdType _glfw.ns.tis.GetKbdType + + +// Cocoa-specific per-window data +// +typedef struct _GLFWwindowNS +{ + id object; + id delegate; + id view; + + GLFWbool maximized; + + // The total sum of the distances the cursor has been warped + // since the last cursor motion event was processed + // This is kept to counteract Cocoa doing the same internally + double cursorWarpDeltaX, cursorWarpDeltaY; + +} _GLFWwindowNS; + +// Cocoa-specific global data +// +typedef struct _GLFWlibraryNS +{ + CGEventSourceRef eventSource; + id delegate; + id autoreleasePool; + id cursor; + TISInputSourceRef inputSource; + IOHIDManagerRef hidManager; + id unicodeData; + id listener; + + char keyName[64]; + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; + char* clipboardString; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + + struct { + CFBundleRef bundle; + PFN_TISCopyCurrentKeyboardLayoutInputSource CopyCurrentKeyboardLayoutInputSource; + PFN_TISGetInputSourceProperty GetInputSourceProperty; + PFN_LMGetKbdType GetKbdType; + CFStringRef kPropertyUnicodeKeyLayoutData; + CFStringRef kNotifySelectedKeyboardInputSourceChanged; + } tis; + +} _GLFWlibraryNS; + +// Cocoa-specific per-monitor data +// +typedef struct _GLFWmonitorNS +{ + CGDirectDisplayID displayID; + CGDisplayModeRef previousMode; + uint32_t unitNumber; + +} _GLFWmonitorNS; + +// Cocoa-specific per-cursor data +// +typedef struct _GLFWcursorNS +{ + id object; + +} _GLFWcursorNS; + +// Cocoa-specific global timer data +// +typedef struct _GLFWtimeNS +{ + uint64_t frequency; + +} _GLFWtimeNS; + + +void _glfwInitTimerNS(void); + +GLFWbool _glfwSetVideoModeNS(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeNS(_GLFWmonitor* monitor); + +#endif // _glfw3_cocoa_platform_h_ diff --git a/apps/exampleViewer/common/glfw/src/cocoa_time.c b/apps/exampleViewer/common/glfw/src/cocoa_time.c new file mode 100644 index 0000000000..5e6d893af2 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_time.c @@ -0,0 +1,60 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerNS(void) +{ + mach_timebase_info_data_t info; + mach_timebase_info(&info); + + _glfw.ns_time.frequency = (info.denom * 1e9) / info.numer; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ + return mach_absolute_time(); +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.ns_time.frequency; +} + diff --git a/apps/exampleViewer/common/glfw/src/cocoa_window.m b/apps/exampleViewer/common/glfw/src/cocoa_window.m new file mode 100644 index 0000000000..5db64cc8d6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/cocoa_window.m @@ -0,0 +1,1684 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +// Needed for _NSGetProgname +#include + +// HACK: The 10.12 SDK adds new symbols and immediately deprecates the old ones +#if MAC_OS_X_VERSION_MAX_ALLOWED < 101200 + #define NSWindowStyleMaskBorderless NSBorderlessWindowMask + #define NSWindowStyleMaskClosable NSClosableWindowMask + #define NSWindowStyleMaskMiniaturizable NSMiniaturizableWindowMask + #define NSWindowStyleMaskResizable NSResizableWindowMask + #define NSWindowStyleMaskTitled NSTitledWindowMask + #define NSEventModifierFlagCommand NSCommandKeyMask + #define NSEventModifierFlagControl NSControlKeyMask + #define NSEventModifierFlagOption NSAlternateKeyMask + #define NSEventModifierFlagShift NSShiftKeyMask + #define NSEventModifierFlagDeviceIndependentFlagsMask NSDeviceIndependentModifierFlagsMask + #define NSEventMaskAny NSAnyEventMask + #define NSEventTypeApplicationDefined NSApplicationDefined + #define NSEventTypeKeyUp NSKeyUp +#endif + + +// Returns the specified standard cursor +// +static NSCursor* getStandardCursor(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return [NSCursor arrowCursor]; + case GLFW_IBEAM_CURSOR: + return [NSCursor IBeamCursor]; + case GLFW_CROSSHAIR_CURSOR: + return [NSCursor crosshairCursor]; + case GLFW_HAND_CURSOR: + return [NSCursor pointingHandCursor]; + case GLFW_HRESIZE_CURSOR: + return [NSCursor resizeLeftRightCursor]; + case GLFW_VRESIZE_CURSOR: + return [NSCursor resizeUpDownCursor]; + } + + return nil; +} + +// Returns the style mask corresponding to the window settings +// +static NSUInteger getStyleMask(_GLFWwindow* window) +{ + NSUInteger styleMask = 0; + + if (window->monitor || !window->decorated) + styleMask |= NSWindowStyleMaskBorderless; + else + { + styleMask |= NSWindowStyleMaskTitled | + NSWindowStyleMaskClosable | + NSWindowStyleMaskMiniaturizable; + + if (window->resizable) + styleMask |= NSWindowStyleMaskResizable; + } + + return styleMask; +} + +// Center the cursor in the view of the window +// +static void centerCursor(_GLFWwindow *window) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); +} + +// Returns whether the cursor is in the client area of the specified window +// +static GLFWbool cursorInClientArea(_GLFWwindow* window) +{ + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + return [window->ns.view mouse:pos inRect:[window->ns.view frame]]; +} + +// Updates the cursor image according to its cursor mode +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (window->cursor) + [(NSCursor*) window->cursor->ns.object set]; + else + [[NSCursor arrowCursor] set]; + } + else + [(NSCursor*) _glfw.ns.cursor set]; +} + +// Transforms the specified y-coordinate between the CG display and NS screen +// coordinate systems +// +static float transformY(float y) +{ + return CGDisplayBounds(CGMainDisplayID()).size.height - y; +} + +// Make the specified window and its video mode active on its monitor +// +static GLFWbool acquireMonitor(_GLFWwindow* window) +{ + const GLFWbool status = _glfwSetVideoModeNS(window->monitor, &window->videoMode); + const CGRect bounds = CGDisplayBounds(window->monitor->ns.displayID); + const NSRect frame = NSMakeRect(bounds.origin.x, + transformY(bounds.origin.y + bounds.size.height), + bounds.size.width, + bounds.size.height); + + [window->ns.object setFrame:frame display:YES]; + + _glfwInputMonitorWindowChange(window->monitor, window); + return status; +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwRestoreVideoModeNS(window->monitor); +} + +// Translates macOS key modifiers into GLFW ones +// +static int translateFlags(NSUInteger flags) +{ + int mods = 0; + + if (flags & NSEventModifierFlagShift) + mods |= GLFW_MOD_SHIFT; + if (flags & NSEventModifierFlagControl) + mods |= GLFW_MOD_CONTROL; + if (flags & NSEventModifierFlagOption) + mods |= GLFW_MOD_ALT; + if (flags & NSEventModifierFlagCommand) + mods |= GLFW_MOD_SUPER; + + return mods; +} + +// Translates a macOS keycode to a GLFW keycode +// +static int translateKey(unsigned int key) +{ + if (key >= sizeof(_glfw.ns.keycodes) / sizeof(_glfw.ns.keycodes[0])) + return GLFW_KEY_UNKNOWN; + + return _glfw.ns.keycodes[key]; +} + +// Translate a GLFW keycode to a Cocoa modifier flag +// +static NSUInteger translateKeyToModifierFlag(int key) +{ + switch (key) + { + case GLFW_KEY_LEFT_SHIFT: + case GLFW_KEY_RIGHT_SHIFT: + return NSEventModifierFlagShift; + case GLFW_KEY_LEFT_CONTROL: + case GLFW_KEY_RIGHT_CONTROL: + return NSEventModifierFlagControl; + case GLFW_KEY_LEFT_ALT: + case GLFW_KEY_RIGHT_ALT: + return NSEventModifierFlagOption; + case GLFW_KEY_LEFT_SUPER: + case GLFW_KEY_RIGHT_SUPER: + return NSEventModifierFlagCommand; + } + + return 0; +} + +// Defines a constant for empty ranges in NSTextInputClient +// +static const NSRange kEmptyRange = { NSNotFound, 0 }; + + +//------------------------------------------------------------------------ +// Delegate for window related notifications +//------------------------------------------------------------------------ + +@interface GLFWWindowDelegate : NSObject +{ + _GLFWwindow* window; +} + +- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; + +@end + +@implementation GLFWWindowDelegate + +- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +{ + self = [super init]; + if (self != nil) + window = initWindow; + + return self; +} + +- (BOOL)windowShouldClose:(id)sender +{ + _glfwInputWindowCloseRequest(window); + return NO; +} + +- (void)windowDidResize:(NSNotification *)notification +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + if (_glfw.ns.disabledCursorWindow == window) + centerCursor(window); + + const int maximized = [window->ns.object isZoomed]; + if (window->ns.maximized != maximized) + { + window->ns.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } + + const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; + + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); + _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); +} + +- (void)windowDidMove:(NSNotification *)notification +{ + if (window->context.client != GLFW_NO_API) + [window->context.nsgl.object update]; + + if (_glfw.ns.disabledCursorWindow == window) + centerCursor(window); + + int x, y; + _glfwPlatformGetWindowPos(window, &x, &y); + _glfwInputWindowPos(window, x, y); +} + +- (void)windowDidMiniaturize:(NSNotification *)notification +{ + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowIconify(window, GLFW_TRUE); +} + +- (void)windowDidDeminiaturize:(NSNotification *)notification +{ + if (window->monitor) + acquireMonitor(window); + + _glfwInputWindowIconify(window, GLFW_FALSE); +} + +- (void)windowDidBecomeKey:(NSNotification *)notification +{ + if (_glfw.ns.disabledCursorWindow == window) + centerCursor(window); + + _glfwInputWindowFocus(window, GLFW_TRUE); + _glfwPlatformSetCursorMode(window, window->cursorMode); +} + +- (void)windowDidResignKey:(NSNotification *)notification +{ + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +@end + + +//------------------------------------------------------------------------ +// Delegate for application related notifications +//------------------------------------------------------------------------ + +@interface GLFWApplicationDelegate : NSObject +@end + +@implementation GLFWApplicationDelegate + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender +{ + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + _glfwInputWindowCloseRequest(window); + + return NSTerminateCancel; +} + +- (void)applicationDidChangeScreenParameters:(NSNotification *) notification +{ + _glfwInputMonitorChange(); +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + [NSApp stop:nil]; + + _glfwPlatformPostEmptyEvent(); +} + +- (void)applicationDidHide:(NSNotification *)notification +{ + int i; + + for (i = 0; i < _glfw.monitorCount; i++) + _glfwRestoreVideoModeNS(_glfw.monitors[i]); +} + +@end + + +//------------------------------------------------------------------------ +// Content view class for the GLFW window +//------------------------------------------------------------------------ + +@interface GLFWContentView : NSView +{ + _GLFWwindow* window; + NSTrackingArea* trackingArea; + NSMutableAttributedString* markedText; +} + +- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow; + +@end + +@implementation GLFWContentView + ++ (void)initialize +{ + if (self == [GLFWContentView class]) + { + if (_glfw.ns.cursor == nil) + { + NSImage* data = [[NSImage alloc] initWithSize:NSMakeSize(16, 16)]; + _glfw.ns.cursor = [[NSCursor alloc] initWithImage:data + hotSpot:NSZeroPoint]; + [data release]; + } + } +} + +- (id)initWithGlfwWindow:(_GLFWwindow *)initWindow +{ + self = [super init]; + if (self != nil) + { + window = initWindow; + trackingArea = nil; + markedText = [[NSMutableAttributedString alloc] init]; + + [self updateTrackingAreas]; + [self registerForDraggedTypes:[NSArray arrayWithObjects: + NSFilenamesPboardType, nil]]; + } + + return self; +} + +- (void)dealloc +{ + [trackingArea release]; + [markedText release]; + [super dealloc]; +} + +- (BOOL)isOpaque +{ + return YES; +} + +- (BOOL)canBecomeKeyView +{ + return YES; +} + +- (BOOL)acceptsFirstResponder +{ + return YES; +} + +- (void)cursorUpdate:(NSEvent *)event +{ + updateCursorImage(window); +} + +- (void)mouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)mouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)mouseMoved:(NSEvent *)event +{ + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + const double dx = [event deltaX] - window->ns.cursorWarpDeltaX; + const double dy = [event deltaY] - window->ns.cursorWarpDeltaY; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + { + const NSRect contentRect = [window->ns.view frame]; + const NSPoint pos = [event locationInWindow]; + + _glfwInputCursorPos(window, pos.x, contentRect.size.height - pos.y); + } + + window->ns.cursorWarpDeltaX = 0; + window->ns.cursorWarpDeltaY = 0; +} + +- (void)rightMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)rightMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)otherMouseDown:(NSEvent *)event +{ + _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_PRESS, + translateFlags([event modifierFlags])); +} + +- (void)otherMouseDragged:(NSEvent *)event +{ + [self mouseMoved:event]; +} + +- (void)otherMouseUp:(NSEvent *)event +{ + _glfwInputMouseClick(window, + (int) [event buttonNumber], + GLFW_RELEASE, + translateFlags([event modifierFlags])); +} + +- (void)mouseExited:(NSEvent *)event +{ + _glfwInputCursorEnter(window, GLFW_FALSE); +} + +- (void)mouseEntered:(NSEvent *)event +{ + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +- (void)viewDidChangeBackingProperties +{ + const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; + + _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); +} + +- (void)drawRect:(NSRect)rect +{ + _glfwInputWindowDamage(window); +} + +- (void)updateTrackingAreas +{ + if (trackingArea != nil) + { + [self removeTrackingArea:trackingArea]; + [trackingArea release]; + } + + const NSTrackingAreaOptions options = NSTrackingMouseEnteredAndExited | + NSTrackingActiveInKeyWindow | + NSTrackingEnabledDuringMouseDrag | + NSTrackingCursorUpdate | + NSTrackingInVisibleRect | + NSTrackingAssumeInside; + + trackingArea = [[NSTrackingArea alloc] initWithRect:[self bounds] + options:options + owner:self + userInfo:nil]; + + [self addTrackingArea:trackingArea]; + [super updateTrackingAreas]; +} + +- (void)keyDown:(NSEvent *)event +{ + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + + _glfwInputKey(window, key, [event keyCode], GLFW_PRESS, mods); + + [self interpretKeyEvents:[NSArray arrayWithObject:event]]; +} + +- (void)flagsChanged:(NSEvent *)event +{ + int action; + const unsigned int modifierFlags = + [event modifierFlags] & NSEventModifierFlagDeviceIndependentFlagsMask; + const int key = translateKey([event keyCode]); + const int mods = translateFlags(modifierFlags); + const NSUInteger keyFlag = translateKeyToModifierFlag(key); + + if (keyFlag & modifierFlags) + { + if (window->keys[key] == GLFW_PRESS) + action = GLFW_RELEASE; + else + action = GLFW_PRESS; + } + else + action = GLFW_RELEASE; + + _glfwInputKey(window, key, [event keyCode], action, mods); +} + +- (void)keyUp:(NSEvent *)event +{ + const int key = translateKey([event keyCode]); + const int mods = translateFlags([event modifierFlags]); + _glfwInputKey(window, key, [event keyCode], GLFW_RELEASE, mods); +} + +- (void)scrollWheel:(NSEvent *)event +{ + double deltaX, deltaY; + + deltaX = [event scrollingDeltaX]; + deltaY = [event scrollingDeltaY]; + + if ([event hasPreciseScrollingDeltas]) + { + deltaX *= 0.1; + deltaY *= 0.1; + } + + if (fabs(deltaX) > 0.0 || fabs(deltaY) > 0.0) + _glfwInputScroll(window, deltaX, deltaY); +} + +- (NSDragOperation)draggingEntered:(id )sender +{ + if ((NSDragOperationGeneric & [sender draggingSourceOperationMask]) + == NSDragOperationGeneric) + { + [self setNeedsDisplay:YES]; + return NSDragOperationGeneric; + } + + return NSDragOperationNone; +} + +- (BOOL)prepareForDragOperation:(id )sender +{ + [self setNeedsDisplay:YES]; + return YES; +} + +- (BOOL)performDragOperation:(id )sender +{ + NSPasteboard* pasteboard = [sender draggingPasteboard]; + NSArray* files = [pasteboard propertyListForType:NSFilenamesPboardType]; + + const NSRect contentRect = [window->ns.view frame]; + _glfwInputCursorPos(window, + [sender draggingLocation].x, + contentRect.size.height - [sender draggingLocation].y); + + const int count = [files count]; + if (count) + { + NSEnumerator* e = [files objectEnumerator]; + char** paths = calloc(count, sizeof(char*)); + int i; + + for (i = 0; i < count; i++) + paths[i] = strdup([[e nextObject] UTF8String]); + + _glfwInputDrop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + free(paths[i]); + free(paths); + } + + return YES; +} + +- (void)concludeDragOperation:(id )sender +{ + [self setNeedsDisplay:YES]; +} + +- (BOOL)hasMarkedText +{ + return [markedText length] > 0; +} + +- (NSRange)markedRange +{ + if ([markedText length] > 0) + return NSMakeRange(0, [markedText length] - 1); + else + return kEmptyRange; +} + +- (NSRange)selectedRange +{ + return kEmptyRange; +} + +- (void)setMarkedText:(id)string + selectedRange:(NSRange)selectedRange + replacementRange:(NSRange)replacementRange +{ + if ([string isKindOfClass:[NSAttributedString class]]) + [markedText initWithAttributedString:string]; + else + [markedText initWithString:string]; +} + +- (void)unmarkText +{ + [[markedText mutableString] setString:@""]; +} + +- (NSArray*)validAttributesForMarkedText +{ + return [NSArray array]; +} + +- (NSAttributedString*)attributedSubstringForProposedRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + return nil; +} + +- (NSUInteger)characterIndexForPoint:(NSPoint)point +{ + return 0; +} + +- (NSRect)firstRectForCharacterRange:(NSRange)range + actualRange:(NSRangePointer)actualRange +{ + int xpos, ypos; + _glfwPlatformGetWindowPos(window, &xpos, &ypos); + const NSRect contentRect = [window->ns.view frame]; + return NSMakeRect(xpos, transformY(ypos + contentRect.size.height), 0.0, 0.0); +} + +- (void)insertText:(id)string replacementRange:(NSRange)replacementRange +{ + NSString* characters; + NSEvent* event = [NSApp currentEvent]; + const int mods = translateFlags([event modifierFlags]); + const int plain = !(mods & GLFW_MOD_SUPER); + + if ([string isKindOfClass:[NSAttributedString class]]) + characters = [string string]; + else + characters = (NSString*) string; + + NSUInteger i, length = [characters length]; + + for (i = 0; i < length; i++) + { + const unichar codepoint = [characters characterAtIndex:i]; + if ((codepoint & 0xff00) == 0xf700) + continue; + + _glfwInputChar(window, codepoint, mods, plain); + } +} + +- (void)doCommandBySelector:(SEL)selector +{ +} + +@end + + +//------------------------------------------------------------------------ +// GLFW window class +//------------------------------------------------------------------------ + +@interface GLFWWindow : NSWindow {} +@end + +@implementation GLFWWindow + +- (BOOL)canBecomeKeyWindow +{ + // Required for NSWindowStyleMaskBorderless windows + return YES; +} + +@end + + +//------------------------------------------------------------------------ +// GLFW application class +//------------------------------------------------------------------------ + +@interface GLFWApplication : NSApplication +@end + +@implementation GLFWApplication + +// From http://cocoadev.com/index.pl?GameKeyboardHandlingAlmost +// This works around an AppKit bug, where key up events while holding +// down the command key don't get sent to the key window. +- (void)sendEvent:(NSEvent *)event +{ + if ([event type] == NSEventTypeKeyUp && + ([event modifierFlags] & NSEventModifierFlagCommand)) + { + [[self keyWindow] sendEvent:event]; + } + else + [super sendEvent:event]; +} + + +// No-op thread entry point +// +- (void)doNothing:(id)object +{ +} +@end + +#if defined(_GLFW_USE_MENUBAR) + +// Try to figure out what the calling application is called +// +static NSString* findAppName(void) +{ + size_t i; + NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; + + // Keys to search for as potential application names + NSString* GLFWNameKeys[] = + { + @"CFBundleDisplayName", + @"CFBundleName", + @"CFBundleExecutable", + }; + + for (i = 0; i < sizeof(GLFWNameKeys) / sizeof(GLFWNameKeys[0]); i++) + { + id name = [infoDictionary objectForKey:GLFWNameKeys[i]]; + if (name && + [name isKindOfClass:[NSString class]] && + ![name isEqualToString:@""]) + { + return name; + } + } + + char** progname = _NSGetProgname(); + if (progname && *progname) + return [NSString stringWithUTF8String:*progname]; + + // Really shouldn't get here + return @"GLFW Application"; +} + +// Set up the menu bar (manually) +// This is nasty, nasty stuff -- calls to undocumented semi-private APIs that +// could go away at any moment, lots of stuff that really should be +// localize(d|able), etc. Loading a nib would save us this horror, but that +// doesn't seem like a good thing to require of GLFW users. +// +static void createMenuBar(void) +{ + NSString* appName = findAppName(); + + NSMenu* bar = [[NSMenu alloc] init]; + [NSApp setMainMenu:bar]; + + NSMenuItem* appMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + NSMenu* appMenu = [[NSMenu alloc] init]; + [appMenuItem setSubmenu:appMenu]; + + [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", appName] + action:@selector(orderFrontStandardAboutPanel:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + NSMenu* servicesMenu = [[NSMenu alloc] init]; + [NSApp setServicesMenu:servicesMenu]; + [[appMenu addItemWithTitle:@"Services" + action:NULL + keyEquivalent:@""] setSubmenu:servicesMenu]; + [servicesMenu release]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", appName] + action:@selector(hide:) + keyEquivalent:@"h"]; + [[appMenu addItemWithTitle:@"Hide Others" + action:@selector(hideOtherApplications:) + keyEquivalent:@"h"] + setKeyEquivalentModifierMask:NSEventModifierFlagOption | NSEventModifierFlagCommand]; + [appMenu addItemWithTitle:@"Show All" + action:@selector(unhideAllApplications:) + keyEquivalent:@""]; + [appMenu addItem:[NSMenuItem separatorItem]]; + [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", appName] + action:@selector(terminate:) + keyEquivalent:@"q"]; + + NSMenuItem* windowMenuItem = + [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; + [bar release]; + NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; + [NSApp setWindowsMenu:windowMenu]; + [windowMenuItem setSubmenu:windowMenu]; + + [windowMenu addItemWithTitle:@"Minimize" + action:@selector(performMiniaturize:) + keyEquivalent:@"m"]; + [windowMenu addItemWithTitle:@"Zoom" + action:@selector(performZoom:) + keyEquivalent:@""]; + [windowMenu addItem:[NSMenuItem separatorItem]]; + [windowMenu addItemWithTitle:@"Bring All to Front" + action:@selector(arrangeInFront:) + keyEquivalent:@""]; + + // TODO: Make this appear at the bottom of the menu (for consistency) + [windowMenu addItem:[NSMenuItem separatorItem]]; + [[windowMenu addItemWithTitle:@"Enter Full Screen" + action:@selector(toggleFullScreen:) + keyEquivalent:@"f"] + setKeyEquivalentModifierMask:NSEventModifierFlagControl | NSEventModifierFlagCommand]; + + // Prior to Snow Leopard, we need to use this oddly-named semi-private API + // to get the application menu working properly. + SEL setAppleMenuSelector = NSSelectorFromString(@"setAppleMenu:"); + [NSApp performSelector:setAppleMenuSelector withObject:appMenu]; +} + +#endif /* _GLFW_USE_MENUBAR */ + +// Initialize the Cocoa Application Kit +// +static GLFWbool initializeAppKit(void) +{ + if (NSApp) + return GLFW_TRUE; + + // Implicitly create shared NSApplication instance + [GLFWApplication sharedApplication]; + + // Make Cocoa enter multi-threaded mode + [NSThread detachNewThreadSelector:@selector(doNothing:) + toTarget:NSApp + withObject:nil]; + + // In case we are unbundled, make us a proper UI application + [NSApp setActivationPolicy:NSApplicationActivationPolicyRegular]; + +#if defined(_GLFW_USE_MENUBAR) + // Menu bar setup must go between sharedApplication above and + // finishLaunching below, in order to properly emulate the behavior + // of NSApplicationMain + createMenuBar(); +#endif + + // There can only be one application delegate, but we allocate it the + // first time a window is created to keep all window code in this file + _glfw.ns.delegate = [[GLFWApplicationDelegate alloc] init]; + if (_glfw.ns.delegate == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create application delegate"); + return GLFW_FALSE; + } + + [NSApp setDelegate:_glfw.ns.delegate]; + [NSApp run]; + + return GLFW_TRUE; +} + +// Create the Cocoa window +// +static GLFWbool createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->ns.delegate = [[GLFWWindowDelegate alloc] initWithGlfwWindow:window]; + if (window->ns.delegate == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to create window delegate"); + return GLFW_FALSE; + } + + NSRect contentRect; + + if (window->monitor) + { + GLFWvidmode mode; + int xpos, ypos; + + _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + + contentRect = NSMakeRect(xpos, ypos, mode.width, mode.height); + } + else + contentRect = NSMakeRect(0, 0, wndconfig->width, wndconfig->height); + + window->ns.object = [[GLFWWindow alloc] + initWithContentRect:contentRect + styleMask:getStyleMask(window) + backing:NSBackingStoreBuffered + defer:NO]; + + if (window->ns.object == nil) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Cocoa: Failed to create window"); + return GLFW_FALSE; + } + + if (window->monitor) + [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; + else + { + [window->ns.object center]; + + if (wndconfig->resizable) + [window->ns.object setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; + + if (wndconfig->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + + if (wndconfig->maximized) + [window->ns.object zoom:nil]; + } + + window->ns.view = [[GLFWContentView alloc] initWithGlfwWindow:window]; + +#if defined(_GLFW_USE_RETINA) + [window->ns.view setWantsBestResolutionOpenGLSurface:YES]; +#endif /*_GLFW_USE_RETINA*/ + + [window->ns.object setContentView:window->ns.view]; + [window->ns.object makeFirstResponder:window->ns.view]; + [window->ns.object setTitle:[NSString stringWithUTF8String:wndconfig->title]]; + [window->ns.object setDelegate:window->ns.delegate]; + [window->ns.object setAcceptsMouseMovedEvents:YES]; + [window->ns.object setRestorable:NO]; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!initializeAppKit()) + return GLFW_FALSE; + + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitNSGL()) + return GLFW_FALSE; + if (!_glfwCreateContextNSGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Cocoa: EGL not available"); + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); + if (!acquireMonitor(window)) + return GLFW_FALSE; + + centerCursor(window); + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (_glfw.ns.disabledCursorWindow == window) + _glfw.ns.disabledCursorWindow = NULL; + + [window->ns.object orderOut:nil]; + + if (window->monitor) + releaseMonitor(window); + + if (window->context.destroy) + window->context.destroy(window); + + [window->ns.object setDelegate:nil]; + [window->ns.delegate release]; + window->ns.delegate = nil; + + [window->ns.view release]; + window->ns.view = nil; + + [window->ns.object close]; + window->ns.object = nil; + + [_glfw.ns.autoreleasePool drain]; + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char *title) +{ + [window->ns.object setTitle:[NSString stringWithUTF8String:title]]; +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + // Regular windows do not have icons +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + const NSRect contentRect = + [window->ns.object contentRectForFrameRect:[window->ns.object frame]]; + + if (xpos) + *xpos = contentRect.origin.x; + if (ypos) + *ypos = transformY(contentRect.origin.y + contentRect.size.height); +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int x, int y) +{ + const NSRect contentRect = [window->ns.view frame]; + const NSRect dummyRect = NSMakeRect(x, transformY(y + contentRect.size.height), 0, 0); + const NSRect frameRect = [window->ns.object frameRectForContentRect:dummyRect]; + [window->ns.object setFrameOrigin:frameRect.origin]; +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + const NSRect contentRect = [window->ns.view frame]; + + if (width) + *width = contentRect.size.width; + if (height) + *height = contentRect.size.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } + else + [window->ns.object setContentSize:NSMakeSize(width, height)]; +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + if (minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) + [window->ns.object setContentMinSize:NSMakeSize(0, 0)]; + else + [window->ns.object setContentMinSize:NSMakeSize(minwidth, minheight)]; + + if (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE) + [window->ns.object setContentMaxSize:NSMakeSize(DBL_MAX, DBL_MAX)]; + else + [window->ns.object setContentMaxSize:NSMakeSize(maxwidth, maxheight)]; +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) + [window->ns.object setResizeIncrements:NSMakeSize(1.0, 1.0)]; + else + [window->ns.object setContentAspectRatio:NSMakeSize(numer, denom)]; +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + const NSRect contentRect = [window->ns.view frame]; + const NSRect fbRect = [window->ns.view convertRectToBacking:contentRect]; + + if (width) + *width = (int) fbRect.size.width; + if (height) + *height = (int) fbRect.size.height; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + const NSRect contentRect = [window->ns.view frame]; + const NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect]; + + if (left) + *left = contentRect.origin.x - frameRect.origin.x; + if (top) + *top = frameRect.origin.y + frameRect.size.height - + contentRect.origin.y - contentRect.size.height; + if (right) + *right = frameRect.origin.x + frameRect.size.width - + contentRect.origin.x - contentRect.size.width; + if (bottom) + *bottom = contentRect.origin.y - frameRect.origin.y; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + [window->ns.object miniaturize:nil]; +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + if ([window->ns.object isMiniaturized]) + [window->ns.object deminiaturize:nil]; + else if ([window->ns.object isZoomed]) + [window->ns.object zoom:nil]; +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (![window->ns.object isZoomed]) + [window->ns.object zoom:nil]; +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + [window->ns.object orderFront:nil]; +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + [window->ns.object orderOut:nil]; +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + // Make us the active application + // HACK: This has been moved here from initializeAppKit to prevent + // applications using only hidden windows from being activated, but + // should probably not be done every time any window is shown + [NSApp activateIgnoringOtherApps:YES]; + + [window->ns.object makeKeyAndOrderFront:nil]; +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + const NSRect contentRect = + NSMakeRect(xpos, transformY(ypos + height), width, height); + const NSRect frameRect = + [window->ns.object frameRectForContentRect:contentRect + styleMask:getStyleMask(window)]; + + [window->ns.object setFrame:frameRect display:YES]; + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + + const NSUInteger styleMask = getStyleMask(window); + [window->ns.object setStyleMask:styleMask]; + [window->ns.object makeFirstResponder:window->ns.view]; + + NSRect contentRect; + + if (monitor) + { + GLFWvidmode mode; + + _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + + contentRect = NSMakeRect(xpos, transformY(ypos + mode.height), + mode.width, mode.height); + } + else + { + contentRect = NSMakeRect(xpos, transformY(ypos + height), + width, height); + } + + NSRect frameRect = [window->ns.object frameRectForContentRect:contentRect + styleMask:styleMask]; + [window->ns.object setFrame:frameRect display:YES]; + + if (monitor) + { + [window->ns.object setLevel:NSMainMenuWindowLevel + 1]; + [window->ns.object setHasShadow:NO]; + + acquireMonitor(window); + } + else + { + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + [window->ns.object setContentAspectRatio:NSMakeSize(window->numer, + window->denom)]; + } + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMinSize:NSMakeSize(window->minwidth, + window->minheight)]; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + [window->ns.object setContentMaxSize:NSMakeSize(window->maxwidth, + window->maxheight)]; + } + + if (window->floating) + [window->ns.object setLevel:NSFloatingWindowLevel]; + else + [window->ns.object setLevel:NSNormalWindowLevel]; + + [window->ns.object setHasShadow:YES]; + } +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return [window->ns.object isKeyWindow]; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return [window->ns.object isMiniaturized]; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return [window->ns.object isVisible]; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return [window->ns.object isZoomed]; +} + +void _glfwPlatformPollEvents(void) +{ + for (;;) + { + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantPast] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event == nil) + break; + + [NSApp sendEvent:event]; + } + + [_glfw.ns.autoreleasePool drain]; + _glfw.ns.autoreleasePool = [[NSAutoreleasePool alloc] init]; +} + +void _glfwPlatformWaitEvents(void) +{ + // I wanted to pass NO to dequeue:, and rely on PollEvents to + // dequeue and send. For reasons not at all clear to me, passing + // NO to dequeue: causes this method never to return. + NSEvent *event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + NSDate* date = [NSDate dateWithTimeIntervalSinceNow:timeout]; + NSEvent* event = [NSApp nextEventMatchingMask:NSEventMaskAny + untilDate:date + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) + [NSApp sendEvent:event]; + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + NSEvent* event = [NSEvent otherEventWithType:NSEventTypeApplicationDefined + location:NSMakePoint(0, 0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:nil + subtype:0 + data1:0 + data2:0]; + [NSApp postEvent:event atStart:YES]; + [pool drain]; +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + const NSRect contentRect = [window->ns.view frame]; + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = contentRect.size.height - pos.y - 1; +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + updateCursorImage(window); + + const NSRect contentRect = [window->ns.view frame]; + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + window->ns.cursorWarpDeltaX += x - pos.x; + window->ns.cursorWarpDeltaY += y - contentRect.size.height + pos.y; + + if (window->monitor) + { + CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, + CGPointMake(x, y)); + } + else + { + const NSRect localRect = NSMakeRect(x, contentRect.size.height - y - 1, 0, 0); + const NSRect globalRect = [window->ns.object convertRectToScreen:localRect]; + const NSPoint globalPoint = globalRect.origin; + + CGWarpMouseCursorPosition(CGPointMake(globalPoint.x, + transformY(globalPoint.y))); + } +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + _glfw.ns.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.ns.restoreCursorPosX, + &_glfw.ns.restoreCursorPosY); + centerCursor(window); + CGAssociateMouseAndMouseCursorPosition(false); + } + else if (_glfw.ns.disabledCursorWindow == window) + { + _glfw.ns.disabledCursorWindow = NULL; + CGAssociateMouseAndMouseCursorPosition(true); + _glfwPlatformSetCursorPos(window, + _glfw.ns.restoreCursorPosX, + _glfw.ns.restoreCursorPosY); + } + + if (cursorInClientArea(window)) + updateCursorImage(window); +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + if (key != GLFW_KEY_UNKNOWN) + scancode = _glfw.ns.scancodes[key]; + + if (!_glfwIsPrintable(_glfw.ns.keycodes[scancode])) + return NULL; + + UInt32 deadKeyState = 0; + UniChar characters[8]; + UniCharCount characterCount = 0; + + if (UCKeyTranslate([(NSData*) _glfw.ns.unicodeData bytes], + scancode, + kUCKeyActionDisplay, + 0, + LMGetKbdType(), + kUCKeyTranslateNoDeadKeysBit, + &deadKeyState, + sizeof(characters) / sizeof(characters[0]), + &characterCount, + characters) != noErr) + { + return NULL; + } + + if (!characterCount) + return NULL; + + CFStringRef string = CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, + characters, + characterCount, + kCFAllocatorNull); + CFStringGetCString(string, + _glfw.ns.keyName, + sizeof(_glfw.ns.keyName), + kCFStringEncodingUTF8); + CFRelease(string); + + return _glfw.ns.keyName; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.ns.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + NSImage* native; + NSBitmapImageRep* rep; + + if (!initializeAppKit()) + return GLFW_FALSE; + + rep = [[NSBitmapImageRep alloc] + initWithBitmapDataPlanes:NULL + pixelsWide:image->width + pixelsHigh:image->height + bitsPerSample:8 + samplesPerPixel:4 + hasAlpha:YES + isPlanar:NO + colorSpaceName:NSCalibratedRGBColorSpace + bitmapFormat:NSAlphaNonpremultipliedBitmapFormat + bytesPerRow:image->width * 4 + bitsPerPixel:32]; + + if (rep == nil) + return GLFW_FALSE; + + memcpy([rep bitmapData], image->pixels, image->width * image->height * 4); + + native = [[NSImage alloc] initWithSize:NSMakeSize(image->width, image->height)]; + [native addRepresentation:rep]; + + cursor->ns.object = [[NSCursor alloc] initWithImage:native + hotSpot:NSMakePoint(xhot, yhot)]; + + [native release]; + [rep release]; + + if (cursor->ns.object == nil) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + if (!initializeAppKit()) + return GLFW_FALSE; + + cursor->ns.object = getStandardCursor(shape); + if (!cursor->ns.object) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve standard cursor"); + return GLFW_FALSE; + } + + [cursor->ns.object retain]; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->ns.object) + [(NSCursor*) cursor->ns.object release]; +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (cursorInClientArea(window)) + updateCursorImage(window); +} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil]; + + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard declareTypes:types owner:nil]; + [pasteboard setString:[NSString stringWithUTF8String:string] + forType:NSStringPboardType]; +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + + if (![[pasteboard types] containsObject:NSStringPboardType]) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "Cocoa: Failed to retrieve string from pasteboard"); + return NULL; + } + + NSString* object = [pasteboard stringForType:NSStringPboardType]; + if (!object) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Cocoa: Failed to retrieve object from pasteboard"); + return NULL; + } + + free(_glfw.ns.clipboardString); + _glfw.ns.clipboardString = strdup([object UTF8String]); + + return _glfw.ns.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + return GLFW_FALSE; +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + return VK_ERROR_EXTENSION_NOT_PRESENT; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI id glfwGetCocoaWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(nil); + return window->ns.object; +} + diff --git a/apps/exampleViewer/common/glfw/src/context.c b/apps/exampleViewer/common/glfw/src/context.c new file mode 100644 index 0000000000..e633487833 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/context.c @@ -0,0 +1,720 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig) +{ + if (ctxconfig->source != GLFW_NATIVE_CONTEXT_API && + ctxconfig->source != GLFW_EGL_CONTEXT_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context creation API %i", + ctxconfig->source); + return GLFW_FALSE; + } + + if (ctxconfig->client != GLFW_NO_API && + ctxconfig->client != GLFW_OPENGL_API && + ctxconfig->client != GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid client API %i", + ctxconfig->client); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if ((ctxconfig->major < 1 || ctxconfig->minor < 0) || + (ctxconfig->major == 1 && ctxconfig->minor > 5) || + (ctxconfig->major == 2 && ctxconfig->minor > 1) || + (ctxconfig->major == 3 && ctxconfig->minor > 3)) + { + // OpenGL 1.0 is the smallest valid version + // OpenGL 1.x series ended with version 1.5 + // OpenGL 2.x series ended with version 2.1 + // OpenGL 3.x series ended with version 3.3 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + + if (ctxconfig->profile) + { + if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE && + ctxconfig->profile != GLFW_OPENGL_COMPAT_PROFILE) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid OpenGL profile %i", + ctxconfig->profile); + return GLFW_FALSE; + } + + if (ctxconfig->major <= 2 || + (ctxconfig->major == 3 && ctxconfig->minor < 2)) + { + // Desktop OpenGL context profiles are only defined for version 3.2 + // and above + + _glfwInputError(GLFW_INVALID_VALUE, + "Context profiles are only defined for OpenGL version 3.2 and above"); + return GLFW_FALSE; + } + } + + if (ctxconfig->forward && ctxconfig->major <= 2) + { + // Forward-compatible contexts are only defined for OpenGL version 3.0 and above + _glfwInputError(GLFW_INVALID_VALUE, + "Forward-compatibility is only defined for OpenGL version 3.0 and above"); + return GLFW_FALSE; + } + } + else if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major < 1 || ctxconfig->minor < 0 || + (ctxconfig->major == 1 && ctxconfig->minor > 1) || + (ctxconfig->major == 2 && ctxconfig->minor > 0)) + { + // OpenGL ES 1.0 is the smallest valid version + // OpenGL ES 1.x series ended with version 1.1 + // OpenGL ES 2.x series ended with version 2.0 + // For now, let everything else through + + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid OpenGL ES version %i.%i", + ctxconfig->major, ctxconfig->minor); + return GLFW_FALSE; + } + } + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness != GLFW_NO_RESET_NOTIFICATION && + ctxconfig->robustness != GLFW_LOSE_CONTEXT_ON_RESET) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context robustness mode %i", + ctxconfig->robustness); + return GLFW_FALSE; + } + } + + if (ctxconfig->release) + { + if (ctxconfig->release != GLFW_RELEASE_BEHAVIOR_NONE && + ctxconfig->release != GLFW_RELEASE_BEHAVIOR_FLUSH) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid context release behavior %i", + ctxconfig->release); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count) +{ + unsigned int i; + unsigned int missing, leastMissing = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + unsigned int extraDiff, leastExtraDiff = UINT_MAX; + const _GLFWfbconfig* current; + const _GLFWfbconfig* closest = NULL; + + for (i = 0; i < count; i++) + { + current = alternatives + i; + + if (desired->stereo > 0 && current->stereo == 0) + { + // Stereo is a hard constraint + continue; + } + + if (desired->doublebuffer != current->doublebuffer) + { + // Double buffering is a hard constraint + continue; + } + + // Count number of missing buffers + { + missing = 0; + + if (desired->alphaBits > 0 && current->alphaBits == 0) + missing++; + + if (desired->depthBits > 0 && current->depthBits == 0) + missing++; + + if (desired->stencilBits > 0 && current->stencilBits == 0) + missing++; + + if (desired->auxBuffers > 0 && + current->auxBuffers < desired->auxBuffers) + { + missing += desired->auxBuffers - current->auxBuffers; + } + + if (desired->samples > 0 && current->samples == 0) + { + // Technically, several multisampling buffers could be + // involved, but that's a lower level implementation detail and + // not important to us here, so we count them as one + missing++; + } + } + + // These polynomials make many small channel size differences matter + // less than one large channel size difference + + // Calculate color channel size difference value + { + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + { + colorDiff += (desired->redBits - current->redBits) * + (desired->redBits - current->redBits); + } + + if (desired->greenBits != GLFW_DONT_CARE) + { + colorDiff += (desired->greenBits - current->greenBits) * + (desired->greenBits - current->greenBits); + } + + if (desired->blueBits != GLFW_DONT_CARE) + { + colorDiff += (desired->blueBits - current->blueBits) * + (desired->blueBits - current->blueBits); + } + } + + // Calculate non-color channel size difference value + { + extraDiff = 0; + + if (desired->alphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->alphaBits - current->alphaBits) * + (desired->alphaBits - current->alphaBits); + } + + if (desired->depthBits != GLFW_DONT_CARE) + { + extraDiff += (desired->depthBits - current->depthBits) * + (desired->depthBits - current->depthBits); + } + + if (desired->stencilBits != GLFW_DONT_CARE) + { + extraDiff += (desired->stencilBits - current->stencilBits) * + (desired->stencilBits - current->stencilBits); + } + + if (desired->accumRedBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumRedBits - current->accumRedBits) * + (desired->accumRedBits - current->accumRedBits); + } + + if (desired->accumGreenBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumGreenBits - current->accumGreenBits) * + (desired->accumGreenBits - current->accumGreenBits); + } + + if (desired->accumBlueBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumBlueBits - current->accumBlueBits) * + (desired->accumBlueBits - current->accumBlueBits); + } + + if (desired->accumAlphaBits != GLFW_DONT_CARE) + { + extraDiff += (desired->accumAlphaBits - current->accumAlphaBits) * + (desired->accumAlphaBits - current->accumAlphaBits); + } + + if (desired->samples != GLFW_DONT_CARE) + { + extraDiff += (desired->samples - current->samples) * + (desired->samples - current->samples); + } + + if (desired->sRGB && !current->sRGB) + extraDiff++; + } + + // Figure out if the current one is better than the best one found so far + // Least number of missing buffers is the most important heuristic, + // then color buffer size match and lastly size match for other buffers + + if (missing < leastMissing) + closest = current; + else if (missing == leastMissing) + { + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && extraDiff < leastExtraDiff)) + { + closest = current; + } + } + + if (current == closest) + { + leastMissing = missing; + leastColorDiff = colorDiff; + leastExtraDiff = extraDiff; + } + } + + return closest; +} + +GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig) +{ + int i; + _GLFWwindow* window; + const char* version; + const char* prefixes[] = + { + "OpenGL ES-CM ", + "OpenGL ES-CL ", + "OpenGL ES ", + NULL + }; + + window = _glfwPlatformGetCurrentContext(); + + window->context.source = ctxconfig->source; + window->context.client = GLFW_OPENGL_API; + + window->context.GetIntegerv = (PFNGLGETINTEGERVPROC) + window->context.getProcAddress("glGetIntegerv"); + window->context.GetString = (PFNGLGETSTRINGPROC) + window->context.getProcAddress("glGetString"); + if (!window->context.GetIntegerv || !window->context.GetString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Entry point retrieval is broken"); + return GLFW_FALSE; + } + + version = (const char*) window->context.GetString(GL_VERSION); + if (!version) + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL version string retrieval is broken"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "OpenGL ES version string retrieval is broken"); + } + + return GLFW_FALSE; + } + + for (i = 0; prefixes[i]; i++) + { + const size_t length = strlen(prefixes[i]); + + if (strncmp(version, prefixes[i], length) == 0) + { + version += length; + window->context.client = GLFW_OPENGL_ES_API; + break; + } + } + + if (!sscanf(version, "%d.%d.%d", + &window->context.major, + &window->context.minor, + &window->context.revision)) + { + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL version string"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "No version found in OpenGL ES version string"); + } + + return GLFW_FALSE; + } + + if (window->context.major < ctxconfig->major || + (window->context.major == ctxconfig->major && + window->context.minor < ctxconfig->minor)) + { + // The desired OpenGL version is greater than the actual version + // This only happens if the machine lacks {GLX|WGL}_ARB_create_context + // /and/ the user has requested an OpenGL version greater than 1.0 + + // For API consistency, we emulate the behavior of the + // {GLX|WGL}_ARB_create_context extension and fail here + + if (window->context.client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "Requested OpenGL ES version %i.%i, got version %i.%i", + ctxconfig->major, ctxconfig->minor, + window->context.major, window->context.minor); + } + + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + // OpenGL 3.0+ uses a different function for extension string retrieval + // We cache it here instead of in glfwExtensionSupported mostly to alert + // users as early as possible that their build may be broken + + window->context.GetStringi = (PFNGLGETSTRINGIPROC) + window->context.getProcAddress("glGetStringi"); + if (!window->context.GetStringi) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Entry point retrieval is broken"); + return GLFW_FALSE; + } + } + + if (window->context.client == GLFW_OPENGL_API) + { + // Read back context flags (OpenGL 3.0 and above) + if (window->context.major >= 3) + { + GLint flags; + window->context.GetIntegerv(GL_CONTEXT_FLAGS, &flags); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + window->context.forward = GLFW_TRUE; + + if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) + window->context.debug = GLFW_TRUE; + else if (glfwExtensionSupported("GL_ARB_debug_output") && + ctxconfig->debug) + { + // HACK: This is a workaround for older drivers (pre KHR_debug) + // not setting the debug bit in the context flags for + // debug contexts + window->context.debug = GLFW_TRUE; + } + + if (flags & GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR) + window->context.noerror = GLFW_TRUE; + } + + // Read back OpenGL context profile (OpenGL 3.2 and above) + if (window->context.major >= 4 || + (window->context.major == 3 && window->context.minor >= 2)) + { + GLint mask; + window->context.GetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + + if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + else if (mask & GL_CONTEXT_CORE_PROFILE_BIT) + window->context.profile = GLFW_OPENGL_CORE_PROFILE; + else if (glfwExtensionSupported("GL_ARB_compatibility")) + { + // HACK: This is a workaround for the compatibility profile bit + // not being set in the context flags if an OpenGL 3.2+ + // context was created without having requested a specific + // version + window->context.profile = GLFW_OPENGL_COMPAT_PROFILE; + } + } + + // Read back robustness strategy + if (glfwExtensionSupported("GL_ARB_robustness")) + { + // NOTE: We avoid using the context flags for detection, as they are + // only present from 3.0 while the extension applies from 1.1 + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + else + { + // Read back robustness strategy + if (glfwExtensionSupported("GL_EXT_robustness")) + { + // NOTE: The values of these constants match those of the OpenGL ARB + // one, so we can reuse them here + + GLint strategy; + window->context.GetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, + &strategy); + + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + window->context.robustness = GLFW_LOSE_CONTEXT_ON_RESET; + else if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + window->context.robustness = GLFW_NO_RESET_NOTIFICATION; + } + } + + if (glfwExtensionSupported("GL_KHR_context_flush_control")) + { + GLint behavior; + window->context.GetIntegerv(GL_CONTEXT_RELEASE_BEHAVIOR, &behavior); + + if (behavior == GL_NONE) + window->context.release = GLFW_RELEASE_BEHAVIOR_NONE; + else if (behavior == GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH) + window->context.release = GLFW_RELEASE_BEHAVIOR_FLUSH; + } + + // Clearing the front buffer to black to avoid garbage pixels left over from + // previous uses of our bit of VRAM + { + PFNGLCLEARPROC glClear = (PFNGLCLEARPROC) + window->context.getProcAddress("glClear"); + glClear(GL_COLOR_BUFFER_BIT); + window->context.swapBuffers(window); + } + + return GLFW_TRUE; +} + +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions) +{ + const char* start = extensions; + + for (;;) + { + const char* where; + const char* terminator; + + where = strstr(start, string); + if (!where) + return GLFW_FALSE; + + terminator = where + strlen(string); + if (where == start || *(where - 1) == ' ') + { + if (*terminator == ' ' || *terminator == '\0') + break; + } + + start = terminator; + } + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI void glfwMakeContextCurrent(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFWwindow* previous = _glfwPlatformGetCurrentContext(); + + _GLFW_REQUIRE_INIT(); + + if (window && window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return; + } + + if (previous) + { + if (!window || window->context.source != previous->context.source) + previous->context.makeCurrent(NULL); + } + + if (window) + window->context.makeCurrent(window); +} + +GLFWAPI GLFWwindow* glfwGetCurrentContext(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return (GLFWwindow*) _glfwPlatformGetCurrentContext(); +} + +GLFWAPI void glfwSwapBuffers(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return; + } + + window->context.swapBuffers(window); +} + +GLFWAPI void glfwSwapInterval(int interval) +{ + _GLFWwindow* window; + + _GLFW_REQUIRE_INIT(); + + window = _glfwPlatformGetCurrentContext(); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + return; + } + + window->context.swapInterval(interval); +} + +GLFWAPI int glfwExtensionSupported(const char* extension) +{ + _GLFWwindow* window; + + assert(extension != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + window = _glfwPlatformGetCurrentContext(); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + return GLFW_FALSE; + } + + if (*extension == '\0') + { + _glfwInputError(GLFW_INVALID_VALUE, "Extension name is empty string"); + return GLFW_FALSE; + } + + if (window->context.major >= 3) + { + int i; + GLint count; + + // Check if extension is in the modern OpenGL extensions string list + + window->context.GetIntegerv(GL_NUM_EXTENSIONS, &count); + + for (i = 0; i < count; i++) + { + const char* en = (const char*) + window->context.GetStringi(GL_EXTENSIONS, i); + if (!en) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (strcmp(en, extension) == 0) + return GLFW_TRUE; + } + } + else + { + // Check if extension is in the old style OpenGL extensions string + + const char* extensions = (const char*) + window->context.GetString(GL_EXTENSIONS); + if (!extensions) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Extension string retrieval is broken"); + return GLFW_FALSE; + } + + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + // Check if extension is in the platform-specific string + return window->context.extensionSupported(extension); +} + +GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname) +{ + _GLFWwindow* window; + assert(procname != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + window = _glfwPlatformGetCurrentContext(); + if (!window) + { + _glfwInputError(GLFW_NO_CURRENT_CONTEXT, NULL); + return NULL; + } + + return window->context.getProcAddress(procname); +} + diff --git a/apps/exampleViewer/common/glfw/src/egl_context.c b/apps/exampleViewer/common/glfw/src/egl_context.c new file mode 100644 index 0000000000..a0743f3d41 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/egl_context.c @@ -0,0 +1,749 @@ +//======================================================================== +// GLFW 3.3 EGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +// Return a description of the specified EGL error +// +static const char* getEGLErrorString(EGLint error) +{ + switch (error) + { + case EGL_SUCCESS: + return "Success"; + case EGL_NOT_INITIALIZED: + return "EGL is not or could not be initialized"; + case EGL_BAD_ACCESS: + return "EGL cannot access a requested resource"; + case EGL_BAD_ALLOC: + return "EGL failed to allocate resources for the requested operation"; + case EGL_BAD_ATTRIBUTE: + return "An unrecognized attribute or attribute value was passed in the attribute list"; + case EGL_BAD_CONTEXT: + return "An EGLContext argument does not name a valid EGL rendering context"; + case EGL_BAD_CONFIG: + return "An EGLConfig argument does not name a valid EGL frame buffer configuration"; + case EGL_BAD_CURRENT_SURFACE: + return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid"; + case EGL_BAD_DISPLAY: + return "An EGLDisplay argument does not name a valid EGL display connection"; + case EGL_BAD_SURFACE: + return "An EGLSurface argument does not name a valid surface configured for GL rendering"; + case EGL_BAD_MATCH: + return "Arguments are inconsistent"; + case EGL_BAD_PARAMETER: + return "One or more argument values are invalid"; + case EGL_BAD_NATIVE_PIXMAP: + return "A NativePixmapType argument does not refer to a valid native pixmap"; + case EGL_BAD_NATIVE_WINDOW: + return "A NativeWindowType argument does not refer to a valid native window"; + case EGL_CONTEXT_LOST: + return "The application must destroy all contexts and reinitialise"; + default: + return "ERROR: UNKNOWN EGL ERROR"; + } +} + +// Returns the specified attribute of the specified EGLConfig +// +static int getEGLConfigAttrib(EGLConfig config, int attrib) +{ + int value; + eglGetConfigAttrib(_glfw.egl.display, config, attrib, &value); + return value; +} + +// Return the EGLConfig most closely matching the specified hints +// +static GLFWbool chooseEGLConfig(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* desired, + EGLConfig* result) +{ + EGLConfig* nativeConfigs; + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + + eglGetConfigs(_glfw.egl.display, NULL, 0, &nativeCount); + if (!nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: No EGLConfigs returned"); + return GLFW_FALSE; + } + + nativeConfigs = calloc(nativeCount, sizeof(EGLConfig)); + eglGetConfigs(_glfw.egl.display, nativeConfigs, nativeCount, &nativeCount); + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const EGLConfig n = nativeConfigs[i]; + _GLFWfbconfig* u = usableConfigs + usableCount; + + // Only consider RGB(A) EGLConfigs + if (!(getEGLConfigAttrib(n, EGL_COLOR_BUFFER_TYPE) & EGL_RGB_BUFFER)) + continue; + + // Only consider window EGLConfigs + if (!(getEGLConfigAttrib(n, EGL_SURFACE_TYPE) & EGL_WINDOW_BIT)) + continue; + +#if defined(_GLFW_X11) + // Only consider EGLConfigs with associated Visuals + if (!getEGLConfigAttrib(n, EGL_NATIVE_VISUAL_ID)) + continue; +#endif // _GLFW_X11 + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES_BIT)) + continue; + } + else + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_ES2_BIT)) + continue; + } + } + else if (ctxconfig->client == GLFW_OPENGL_API) + { + if (!(getEGLConfigAttrib(n, EGL_RENDERABLE_TYPE) & EGL_OPENGL_BIT)) + continue; + } + + u->redBits = getEGLConfigAttrib(n, EGL_RED_SIZE); + u->greenBits = getEGLConfigAttrib(n, EGL_GREEN_SIZE); + u->blueBits = getEGLConfigAttrib(n, EGL_BLUE_SIZE); + + u->alphaBits = getEGLConfigAttrib(n, EGL_ALPHA_SIZE); + u->depthBits = getEGLConfigAttrib(n, EGL_DEPTH_SIZE); + u->stencilBits = getEGLConfigAttrib(n, EGL_STENCIL_SIZE); + + u->samples = getEGLConfigAttrib(n, EGL_SAMPLES); + u->doublebuffer = GLFW_TRUE; + + u->handle = (uintptr_t) n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = (EGLConfig) closest->handle; + + free(nativeConfigs); + free(usableConfigs); + + return closest != NULL; +} + +static void makeContextCurrentEGL(_GLFWwindow* window) +{ + if (window) + { + if (!eglMakeCurrent(_glfw.egl.display, + window->context.egl.surface, + window->context.egl.surface, + window->context.egl.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to make context current: %s", + getEGLErrorString(eglGetError())); + return; + } + } + else + { + if (!eglMakeCurrent(_glfw.egl.display, + EGL_NO_SURFACE, + EGL_NO_SURFACE, + EGL_NO_CONTEXT)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to clear current context: %s", + getEGLErrorString(eglGetError())); + return; + } + } + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffersEGL(_GLFWwindow* window) +{ + if (window != _glfwPlatformGetCurrentContext()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: The context must be current on the calling thread when swapping buffers"); + return; + } + + eglSwapBuffers(_glfw.egl.display, window->context.egl.surface); +} + +static void swapIntervalEGL(int interval) +{ + eglSwapInterval(_glfw.egl.display, interval); +} + +static int extensionSupportedEGL(const char* extension) +{ + const char* extensions = eglQueryString(_glfw.egl.display, EGL_EXTENSIONS); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressEGL(const char* procname) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (window->context.egl.client) + { + GLFWglproc proc = (GLFWglproc) _glfw_dlsym(window->context.egl.client, + procname); + if (proc) + return proc; + } + + return eglGetProcAddress(procname); +} + +static void destroyContextEGL(_GLFWwindow* window) +{ +#if defined(_GLFW_X11) + // NOTE: Do not unload libGL.so.1 while the X11 display is still open, + // as it will make XCloseDisplay segfault + if (window->context.client != GLFW_OPENGL_API) +#endif // _GLFW_X11 + { + if (window->context.egl.client) + { + _glfw_dlclose(window->context.egl.client); + window->context.egl.client = NULL; + } + } + + if (window->context.egl.surface) + { + eglDestroySurface(_glfw.egl.display, window->context.egl.surface); + window->context.egl.surface = EGL_NO_SURFACE; + } + + if (window->context.egl.handle) + { + eglDestroyContext(_glfw.egl.display, window->context.egl.handle); + window->context.egl.handle = EGL_NO_CONTEXT; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize EGL +// +GLFWbool _glfwInitEGL(void) +{ + int i; + const char* sonames[] = + { +#if defined(_GLFW_WIN32) + "libEGL.dll", + "EGL.dll", +#elif defined(_GLFW_COCOA) + "libEGL.dylib", +#else + "libEGL.so.1", +#endif + NULL + }; + + if (_glfw.egl.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.egl.handle = _glfw_dlopen(sonames[i]); + if (_glfw.egl.handle) + break; + } + + if (!_glfw.egl.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: Library not found"); + return GLFW_FALSE; + } + + _glfw.egl.prefix = (strncmp(sonames[i], "lib", 3) == 0); + + _glfw.egl.GetConfigAttrib = (PFNEGLGETCONFIGATTRIBPROC) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigAttrib"); + _glfw.egl.GetConfigs = (PFNEGLGETCONFIGSPROC) + _glfw_dlsym(_glfw.egl.handle, "eglGetConfigs"); + _glfw.egl.GetDisplay = (PFNEGLGETDISPLAYPROC) + _glfw_dlsym(_glfw.egl.handle, "eglGetDisplay"); + _glfw.egl.GetError = (PFNEGLGETERRORPROC) + _glfw_dlsym(_glfw.egl.handle, "eglGetError"); + _glfw.egl.Initialize = (PFNEGLINITIALIZEPROC) + _glfw_dlsym(_glfw.egl.handle, "eglInitialize"); + _glfw.egl.Terminate = (PFNEGLTERMINATEPROC) + _glfw_dlsym(_glfw.egl.handle, "eglTerminate"); + _glfw.egl.BindAPI = (PFNEGLBINDAPIPROC) + _glfw_dlsym(_glfw.egl.handle, "eglBindAPI"); + _glfw.egl.CreateContext = (PFNEGLCREATECONTEXTPROC) + _glfw_dlsym(_glfw.egl.handle, "eglCreateContext"); + _glfw.egl.DestroySurface = (PFNEGLDESTROYSURFACEPROC) + _glfw_dlsym(_glfw.egl.handle, "eglDestroySurface"); + _glfw.egl.DestroyContext = (PFNEGLDESTROYCONTEXTPROC) + _glfw_dlsym(_glfw.egl.handle, "eglDestroyContext"); + _glfw.egl.CreateWindowSurface = (PFNEGLCREATEWINDOWSURFACEPROC) + _glfw_dlsym(_glfw.egl.handle, "eglCreateWindowSurface"); + _glfw.egl.MakeCurrent = (PFNEGLMAKECURRENTPROC) + _glfw_dlsym(_glfw.egl.handle, "eglMakeCurrent"); + _glfw.egl.SwapBuffers = (PFNEGLSWAPBUFFERSPROC) + _glfw_dlsym(_glfw.egl.handle, "eglSwapBuffers"); + _glfw.egl.SwapInterval = (PFNEGLSWAPINTERVALPROC) + _glfw_dlsym(_glfw.egl.handle, "eglSwapInterval"); + _glfw.egl.QueryString = (PFNEGLQUERYSTRINGPROC) + _glfw_dlsym(_glfw.egl.handle, "eglQueryString"); + _glfw.egl.GetProcAddress = (PFNEGLGETPROCADDRESSPROC) + _glfw_dlsym(_glfw.egl.handle, "eglGetProcAddress"); + + if (!_glfw.egl.GetConfigAttrib || + !_glfw.egl.GetConfigs || + !_glfw.egl.GetDisplay || + !_glfw.egl.GetError || + !_glfw.egl.Initialize || + !_glfw.egl.Terminate || + !_glfw.egl.BindAPI || + !_glfw.egl.CreateContext || + !_glfw.egl.DestroySurface || + !_glfw.egl.DestroyContext || + !_glfw.egl.CreateWindowSurface || + !_glfw.egl.MakeCurrent || + !_glfw.egl.SwapBuffers || + !_glfw.egl.SwapInterval || + !_glfw.egl.QueryString || + !_glfw.egl.GetProcAddress) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to load required entry points"); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.display = eglGetDisplay(_GLFW_EGL_NATIVE_DISPLAY); + if (_glfw.egl.display == EGL_NO_DISPLAY) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to get EGL display: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + if (!eglInitialize(_glfw.egl.display, &_glfw.egl.major, &_glfw.egl.minor)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to initialize EGL: %s", + getEGLErrorString(eglGetError())); + + _glfwTerminateEGL(); + return GLFW_FALSE; + } + + _glfw.egl.KHR_create_context = + extensionSupportedEGL("EGL_KHR_create_context"); + _glfw.egl.KHR_create_context_no_error = + extensionSupportedEGL("EGL_KHR_create_context_no_error"); + _glfw.egl.KHR_gl_colorspace = + extensionSupportedEGL("EGL_KHR_gl_colorspace"); + _glfw.egl.KHR_get_all_proc_addresses = + extensionSupportedEGL("EGL_KHR_get_all_proc_addresses"); + + return GLFW_TRUE; +} + +// Terminate EGL +// +void _glfwTerminateEGL(void) +{ + if (_glfw.egl.display) + { + eglTerminate(_glfw.egl.display); + _glfw.egl.display = EGL_NO_DISPLAY; + } + + if (_glfw.egl.handle) + { + _glfw_dlclose(_glfw.egl.handle); + _glfw.egl.handle = NULL; + } +} + +#define setEGLattrib(attribName, attribValue) \ +{ \ + attribs[index++] = attribName; \ + attribs[index++] = attribValue; \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + EGLint attribs[40]; + EGLConfig config; + EGLContext share = NULL; + + if (!_glfw.egl.display) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "EGL: API not available"); + return GLFW_FALSE; + } + + if (ctxconfig->share) + share = ctxconfig->share->context.egl.handle; + + if (!chooseEGLConfig(ctxconfig, fbconfig, &config)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (!eglBindAPI(EGL_OPENGL_ES_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL ES: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + else + { + if (!eglBindAPI(EGL_OPENGL_API)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to bind OpenGL: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + } + + if (_glfw.egl.KHR_create_context) + { + int index = 0, mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR; + + if (_glfw.egl.KHR_create_context_no_error) + { + if (ctxconfig->noerror) + flags |= EGL_CONTEXT_OPENGL_NO_ERROR_KHR; + } + } + + if (ctxconfig->debug) + flags |= EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR; + + if (ctxconfig->robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_NO_RESET_NOTIFICATION_KHR); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setEGLattrib(EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR, + EGL_LOSE_CONTEXT_ON_RESET_KHR); + } + + flags |= EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR; + } + + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setEGLattrib(EGL_CONTEXT_MAJOR_VERSION_KHR, ctxconfig->major); + setEGLattrib(EGL_CONTEXT_MINOR_VERSION_KHR, ctxconfig->minor); + } + + if (mask) + setEGLattrib(EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR, mask); + + if (flags) + setEGLattrib(EGL_CONTEXT_FLAGS_KHR, flags); + + setEGLattrib(EGL_NONE, EGL_NONE); + } + else + { + int index = 0; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + setEGLattrib(EGL_CONTEXT_CLIENT_VERSION, ctxconfig->major); + + setEGLattrib(EGL_NONE, EGL_NONE); + } + + // Context release behaviors (GL_KHR_context_flush_control) are not yet + // supported on EGL but are not a hard constraint, so ignore and continue + + window->context.egl.handle = eglCreateContext(_glfw.egl.display, + config, share, attribs); + + if (window->context.egl.handle == EGL_NO_CONTEXT) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "EGL: Failed to create context: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + // Set up attributes for surface creation + { + int index = 0; + + if (fbconfig->sRGB) + { + if (_glfw.egl.KHR_gl_colorspace) + { + setEGLattrib(EGL_GL_COLORSPACE_KHR, EGL_GL_COLORSPACE_SRGB_KHR); + } + } + + setEGLattrib(EGL_NONE, EGL_NONE); + } + + window->context.egl.surface = + eglCreateWindowSurface(_glfw.egl.display, + config, + _GLFW_EGL_NATIVE_WINDOW, + attribs); + if (window->context.egl.surface == EGL_NO_SURFACE) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to create window surface: %s", + getEGLErrorString(eglGetError())); + return GLFW_FALSE; + } + + window->context.egl.config = config; + + // Load the appropriate client library + if (!_glfw.egl.KHR_get_all_proc_addresses) + { + int i; + const char** sonames; + const char* es1sonames[] = + { +#if defined(_GLFW_WIN32) + "GLESv1_CM.dll", + "libGLES_CM.dll", +#elif defined(_GLFW_COCOA) + "libGLESv1_CM.dylib", +#else + "libGLESv1_CM.so.1", + "libGLES_CM.so.1", +#endif + NULL + }; + const char* es2sonames[] = + { +#if defined(_GLFW_WIN32) + "GLESv2.dll", + "libGLESv2.dll", +#elif defined(_GLFW_COCOA) + "libGLESv2.dylib", +#else + "libGLESv2.so.2", +#endif + NULL + }; + const char* glsonames[] = + { +#if defined(_GLFW_WIN32) +#elif defined(_GLFW_COCOA) +#else + "libGL.so.1", +#endif + NULL + }; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (ctxconfig->major == 1) + sonames = es1sonames; + else + sonames = es2sonames; + } + else + sonames = glsonames; + + for (i = 0; sonames[i]; i++) + { + // HACK: Match presence of lib prefix to increase chance of finding + // a matching pair in the jungle that is Win32 EGL/GLES + if (_glfw.egl.prefix != (strncmp(sonames[i], "lib", 3) == 0)) + continue; + + window->context.egl.client = _glfw_dlopen(sonames[i]); + if (window->context.egl.client) + break; + } + + if (!window->context.egl.client) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "EGL: Failed to load client library"); + return GLFW_FALSE; + } + } + + window->context.makeCurrent = makeContextCurrentEGL; + window->context.swapBuffers = swapBuffersEGL; + window->context.swapInterval = swapIntervalEGL; + window->context.extensionSupported = extensionSupportedEGL; + window->context.getProcAddress = getProcAddressEGL; + window->context.destroy = destroyContextEGL; + + return GLFW_TRUE; +} + +#undef setEGLattrib + +// Returns the Visual and depth of the chosen EGLConfig +// +#if defined(_GLFW_X11) +GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth) +{ + XVisualInfo* result; + XVisualInfo desired; + EGLConfig native; + EGLint visualID = 0, count = 0; + const long vimask = VisualScreenMask | VisualIDMask; + + if (!chooseEGLConfig(ctxconfig, fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "EGL: Failed to find a suitable EGLConfig"); + return GLFW_FALSE; + } + + eglGetConfigAttrib(_glfw.egl.display, native, + EGL_NATIVE_VISUAL_ID, &visualID); + + desired.screen = _glfw.x11.screen; + desired.visualid = visualID; + + result = XGetVisualInfo(_glfw.x11.display, vimask, &desired, &count); + if (!result) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "EGL: Failed to retrieve Visual for EGLConfig"); + return GLFW_FALSE; + } + + *visual = result->visual; + *depth = result->depth; + + XFree(result); + return GLFW_TRUE; +} +#endif // _GLFW_X11 + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI EGLDisplay glfwGetEGLDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_DISPLAY); + return _glfw.egl.display; +} + +GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_CONTEXT); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_CONTEXT; + } + + return window->context.egl.handle; +} + +GLFWAPI EGLSurface glfwGetEGLSurface(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(EGL_NO_SURFACE); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return EGL_NO_SURFACE; + } + + return window->context.egl.surface; +} + diff --git a/apps/exampleViewer/common/glfw/src/egl_context.h b/apps/exampleViewer/common/glfw/src/egl_context.h new file mode 100644 index 0000000000..dbe99340cd --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/egl_context.h @@ -0,0 +1,214 @@ +//======================================================================== +// GLFW 3.3 EGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_egl_context_h_ +#define _glfw3_egl_context_h_ + +#if defined(_GLFW_USE_EGLPLATFORM_H) + #include +#elif defined(_GLFW_WIN32) + #define EGLAPIENTRY __stdcall +typedef HDC EGLNativeDisplayType; +typedef HWND EGLNativeWindowType; +#elif defined(_GLFW_X11) + #define EGLAPIENTRY +typedef Display* EGLNativeDisplayType; +typedef Window EGLNativeWindowType; +#elif defined(_GLFW_WAYLAND) + #define EGLAPIENTRY +typedef struct wl_display* EGLNativeDisplayType; +typedef struct wl_egl_window* EGLNativeWindowType; +#elif defined(_GLFW_MIR) + #define EGLAPIENTRY +typedef MirEGLNativeDisplayType EGLNativeDisplayType; +typedef MirEGLNativeWindowType EGLNativeWindowType; +#else + #error "No supported EGL platform selected" +#endif + +#define EGL_SUCCESS 0x3000 +#define EGL_NOT_INITIALIZED 0x3001 +#define EGL_BAD_ACCESS 0x3002 +#define EGL_BAD_ALLOC 0x3003 +#define EGL_BAD_ATTRIBUTE 0x3004 +#define EGL_BAD_CONFIG 0x3005 +#define EGL_BAD_CONTEXT 0x3006 +#define EGL_BAD_CURRENT_SURFACE 0x3007 +#define EGL_BAD_DISPLAY 0x3008 +#define EGL_BAD_MATCH 0x3009 +#define EGL_BAD_NATIVE_PIXMAP 0x300a +#define EGL_BAD_NATIVE_WINDOW 0x300b +#define EGL_BAD_PARAMETER 0x300c +#define EGL_BAD_SURFACE 0x300d +#define EGL_CONTEXT_LOST 0x300e +#define EGL_COLOR_BUFFER_TYPE 0x303f +#define EGL_RGB_BUFFER 0x308e +#define EGL_SURFACE_TYPE 0x3033 +#define EGL_WINDOW_BIT 0x0004 +#define EGL_RENDERABLE_TYPE 0x3040 +#define EGL_OPENGL_ES_BIT 0x0001 +#define EGL_OPENGL_ES2_BIT 0x0004 +#define EGL_OPENGL_BIT 0x0008 +#define EGL_ALPHA_SIZE 0x3021 +#define EGL_BLUE_SIZE 0x3022 +#define EGL_GREEN_SIZE 0x3023 +#define EGL_RED_SIZE 0x3024 +#define EGL_DEPTH_SIZE 0x3025 +#define EGL_STENCIL_SIZE 0x3026 +#define EGL_SAMPLES 0x3031 +#define EGL_OPENGL_ES_API 0x30a0 +#define EGL_OPENGL_API 0x30a2 +#define EGL_NONE 0x3038 +#define EGL_EXTENSIONS 0x3055 +#define EGL_CONTEXT_CLIENT_VERSION 0x3098 +#define EGL_NATIVE_VISUAL_ID 0x302e +#define EGL_NO_SURFACE ((EGLSurface) 0) +#define EGL_NO_DISPLAY ((EGLDisplay) 0) +#define EGL_NO_CONTEXT ((EGLContext) 0) +#define EGL_DEFAULT_DISPLAY ((EGLNativeDisplayType) 0) + +#define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 +#define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 +#define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31bd +#define EGL_NO_RESET_NOTIFICATION_KHR 0x31be +#define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31bf +#define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 +#define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 +#define EGL_CONTEXT_MINOR_VERSION_KHR 0x30fb +#define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30fd +#define EGL_CONTEXT_FLAGS_KHR 0x30fc +#define EGL_CONTEXT_OPENGL_NO_ERROR_KHR 0x31b3 +#define EGL_GL_COLORSPACE_KHR 0x309d +#define EGL_GL_COLORSPACE_SRGB_KHR 0x3089 + +typedef int EGLint; +typedef unsigned int EGLBoolean; +typedef unsigned int EGLenum; +typedef void* EGLConfig; +typedef void* EGLContext; +typedef void* EGLDisplay; +typedef void* EGLSurface; + +// EGL function pointer typedefs +typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGATTRIBPROC)(EGLDisplay,EGLConfig,EGLint,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLGETCONFIGSPROC)(EGLDisplay,EGLConfig*,EGLint,EGLint*); +typedef EGLDisplay (EGLAPIENTRY * PFNEGLGETDISPLAYPROC)(EGLNativeDisplayType); +typedef EGLint (EGLAPIENTRY * PFNEGLGETERRORPROC)(void); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLINITIALIZEPROC)(EGLDisplay,EGLint*,EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLTERMINATEPROC)(EGLDisplay); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLBINDAPIPROC)(EGLenum); +typedef EGLContext (EGLAPIENTRY * PFNEGLCREATECONTEXTPROC)(EGLDisplay,EGLConfig,EGLContext,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYSURFACEPROC)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLDESTROYCONTEXTPROC)(EGLDisplay,EGLContext); +typedef EGLSurface (EGLAPIENTRY * PFNEGLCREATEWINDOWSURFACEPROC)(EGLDisplay,EGLConfig,EGLNativeWindowType,const EGLint*); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLMAKECURRENTPROC)(EGLDisplay,EGLSurface,EGLSurface,EGLContext); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPBUFFERSPROC)(EGLDisplay,EGLSurface); +typedef EGLBoolean (EGLAPIENTRY * PFNEGLSWAPINTERVALPROC)(EGLDisplay,EGLint); +typedef const char* (EGLAPIENTRY * PFNEGLQUERYSTRINGPROC)(EGLDisplay,EGLint); +typedef GLFWglproc (EGLAPIENTRY * PFNEGLGETPROCADDRESSPROC)(const char*); +#define eglGetConfigAttrib _glfw.egl.GetConfigAttrib +#define eglGetConfigs _glfw.egl.GetConfigs +#define eglGetDisplay _glfw.egl.GetDisplay +#define eglGetError _glfw.egl.GetError +#define eglInitialize _glfw.egl.Initialize +#define eglTerminate _glfw.egl.Terminate +#define eglBindAPI _glfw.egl.BindAPI +#define eglCreateContext _glfw.egl.CreateContext +#define eglDestroySurface _glfw.egl.DestroySurface +#define eglDestroyContext _glfw.egl.DestroyContext +#define eglCreateWindowSurface _glfw.egl.CreateWindowSurface +#define eglMakeCurrent _glfw.egl.MakeCurrent +#define eglSwapBuffers _glfw.egl.SwapBuffers +#define eglSwapInterval _glfw.egl.SwapInterval +#define eglQueryString _glfw.egl.QueryString +#define eglGetProcAddress _glfw.egl.GetProcAddress + +#define _GLFW_EGL_CONTEXT_STATE _GLFWcontextEGL egl +#define _GLFW_EGL_LIBRARY_CONTEXT_STATE _GLFWlibraryEGL egl + + +// EGL-specific per-context data +// +typedef struct _GLFWcontextEGL +{ + EGLConfig config; + EGLContext handle; + EGLSurface surface; + + void* client; + +} _GLFWcontextEGL; + +// EGL-specific global data +// +typedef struct _GLFWlibraryEGL +{ + EGLDisplay display; + EGLint major, minor; + GLFWbool prefix; + + GLFWbool KHR_create_context; + GLFWbool KHR_create_context_no_error; + GLFWbool KHR_gl_colorspace; + GLFWbool KHR_get_all_proc_addresses; + + void* handle; + + PFNEGLGETCONFIGATTRIBPROC GetConfigAttrib; + PFNEGLGETCONFIGSPROC GetConfigs; + PFNEGLGETDISPLAYPROC GetDisplay; + PFNEGLGETERRORPROC GetError; + PFNEGLINITIALIZEPROC Initialize; + PFNEGLTERMINATEPROC Terminate; + PFNEGLBINDAPIPROC BindAPI; + PFNEGLCREATECONTEXTPROC CreateContext; + PFNEGLDESTROYSURFACEPROC DestroySurface; + PFNEGLDESTROYCONTEXTPROC DestroyContext; + PFNEGLCREATEWINDOWSURFACEPROC CreateWindowSurface; + PFNEGLMAKECURRENTPROC MakeCurrent; + PFNEGLSWAPBUFFERSPROC SwapBuffers; + PFNEGLSWAPINTERVALPROC SwapInterval; + PFNEGLQUERYSTRINGPROC QueryString; + PFNEGLGETPROCADDRESSPROC GetProcAddress; + +} _GLFWlibraryEGL; + + +GLFWbool _glfwInitEGL(void); +void _glfwTerminateEGL(void); +GLFWbool _glfwCreateContextEGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +#if defined(_GLFW_X11) +GLFWbool _glfwChooseVisualEGL(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); +#endif /*_GLFW_X11*/ + +#endif // _glfw3_egl_context_h_ diff --git a/apps/exampleViewer/common/glfw/src/glfw3.pc.in b/apps/exampleViewer/common/glfw/src/glfw3.pc.in new file mode 100644 index 0000000000..f2e4d97693 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/glfw3.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=${prefix}/include +libdir=${exec_prefix}/lib@LIB_SUFFIX@ + +Name: GLFW +Description: A multi-platform library for OpenGL, window and input +Version: @GLFW_VERSION_FULL@ +URL: http://www.glfw.org/ +Requires.private: @GLFW_PKG_DEPS@ +Libs: -L${libdir} -l@GLFW_LIB_NAME@ +Libs.private: @GLFW_PKG_LIBS@ +Cflags: -I${includedir} diff --git a/apps/exampleViewer/common/glfw/src/glfw3Config.cmake.in b/apps/exampleViewer/common/glfw/src/glfw3Config.cmake.in new file mode 100644 index 0000000000..1fa200e270 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/glfw3Config.cmake.in @@ -0,0 +1 @@ +include("${CMAKE_CURRENT_LIST_DIR}/glfw3Targets.cmake") diff --git a/apps/exampleViewer/common/glfw/src/glfw_config.h.in b/apps/exampleViewer/common/glfw/src/glfw_config.h.in new file mode 100644 index 0000000000..f709726f07 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/glfw_config.h.in @@ -0,0 +1,65 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2010-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// As glfw_config.h.in, this file is used by CMake to produce the +// glfw_config.h configuration header file. If you are adding a feature +// requiring conditional compilation, this is where to add the macro. +//======================================================================== +// As glfw_config.h, this file defines compile-time option macros for a +// specific platform and development environment. If you are using the +// GLFW CMake files, modify glfw_config.h.in instead of this file. If you +// are using your own build system, make this file define the appropriate +// macros in whatever way is suitable. +//======================================================================== + +// Define this to 1 if building GLFW for X11 +#cmakedefine _GLFW_X11 +// Define this to 1 if building GLFW for Win32 +#cmakedefine _GLFW_WIN32 +// Define this to 1 if building GLFW for Cocoa +#cmakedefine _GLFW_COCOA +// Define this to 1 if building GLFW for Wayland +#cmakedefine _GLFW_WAYLAND +// Define this to 1 if building GLFW for Mir +#cmakedefine _GLFW_MIR + +// Define this to 1 if building as a shared library / dynamic library / DLL +#cmakedefine _GLFW_BUILD_DLL +// Define this to 1 to use Vulkan loader linked statically into application +#cmakedefine _GLFW_VULKAN_STATIC + +// Define this to 1 to force use of high-performance GPU on hybrid systems +#cmakedefine _GLFW_USE_HYBRID_HPG + +// Define this to 1 if the Xxf86vm X11 extension is available +#cmakedefine _GLFW_HAS_XF86VM + +// Define this to 1 if glfwInit should change the current directory +#cmakedefine _GLFW_USE_CHDIR +// Define this to 1 if glfwCreateWindow should populate the menu bar +#cmakedefine _GLFW_USE_MENUBAR +// Define this to 1 if windows should use full resolution on Retina displays +#cmakedefine _GLFW_USE_RETINA + diff --git a/apps/exampleViewer/common/glfw/src/glx_context.c b/apps/exampleViewer/common/glfw/src/glx_context.c new file mode 100644 index 0000000000..6f2f276127 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/glx_context.c @@ -0,0 +1,677 @@ +//======================================================================== +// GLFW 3.3 GLX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +#ifndef GLXBadProfileARB + #define GLXBadProfileARB 13 +#endif + + +// Returns the specified attribute of the specified GLXFBConfig +// +static int getGLXFBConfigAttrib(GLXFBConfig fbconfig, int attrib) +{ + int value; + glXGetFBConfigAttrib(_glfw.x11.display, fbconfig, attrib, &value); + return value; +} + +// Return the GLXFBConfig most closely matching the specified hints +// +static GLFWbool chooseGLXFBConfig(const _GLFWfbconfig* desired, GLXFBConfig* result) +{ + GLXFBConfig* nativeConfigs; + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, nativeCount, usableCount; + const char* vendor; + GLFWbool trustWindowBit = GLFW_TRUE; + + // HACK: This is a (hopefully temporary) workaround for Chromium + // (VirtualBox GL) not setting the window bit on any GLXFBConfigs + vendor = glXGetClientString(_glfw.x11.display, GLX_VENDOR); + if (strcmp(vendor, "Chromium") == 0) + trustWindowBit = GLFW_FALSE; + + nativeConfigs = + glXGetFBConfigs(_glfw.x11.display, _glfw.x11.screen, &nativeCount); + if (!nativeCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: No GLXFBConfigs returned"); + return GLFW_FALSE; + } + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const GLXFBConfig n = nativeConfigs[i]; + _GLFWfbconfig* u = usableConfigs + usableCount; + + // Only consider RGBA GLXFBConfigs + if (!(getGLXFBConfigAttrib(n, GLX_RENDER_TYPE) & GLX_RGBA_BIT)) + continue; + + // Only consider window GLXFBConfigs + if (!(getGLXFBConfigAttrib(n, GLX_DRAWABLE_TYPE) & GLX_WINDOW_BIT)) + { + if (trustWindowBit) + continue; + } + + u->redBits = getGLXFBConfigAttrib(n, GLX_RED_SIZE); + u->greenBits = getGLXFBConfigAttrib(n, GLX_GREEN_SIZE); + u->blueBits = getGLXFBConfigAttrib(n, GLX_BLUE_SIZE); + + u->alphaBits = getGLXFBConfigAttrib(n, GLX_ALPHA_SIZE); + u->depthBits = getGLXFBConfigAttrib(n, GLX_DEPTH_SIZE); + u->stencilBits = getGLXFBConfigAttrib(n, GLX_STENCIL_SIZE); + + u->accumRedBits = getGLXFBConfigAttrib(n, GLX_ACCUM_RED_SIZE); + u->accumGreenBits = getGLXFBConfigAttrib(n, GLX_ACCUM_GREEN_SIZE); + u->accumBlueBits = getGLXFBConfigAttrib(n, GLX_ACCUM_BLUE_SIZE); + u->accumAlphaBits = getGLXFBConfigAttrib(n, GLX_ACCUM_ALPHA_SIZE); + + u->auxBuffers = getGLXFBConfigAttrib(n, GLX_AUX_BUFFERS); + + if (getGLXFBConfigAttrib(n, GLX_STEREO)) + u->stereo = GLFW_TRUE; + if (getGLXFBConfigAttrib(n, GLX_DOUBLEBUFFER)) + u->doublebuffer = GLFW_TRUE; + + if (_glfw.glx.ARB_multisample) + u->samples = getGLXFBConfigAttrib(n, GLX_SAMPLES); + + if (_glfw.glx.ARB_framebuffer_sRGB || _glfw.glx.EXT_framebuffer_sRGB) + u->sRGB = getGLXFBConfigAttrib(n, GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB); + + u->handle = (uintptr_t) n; + usableCount++; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (closest) + *result = (GLXFBConfig) closest->handle; + + XFree(nativeConfigs); + free(usableConfigs); + + return closest != NULL; +} + +// Create the OpenGL context using legacy API +// +static GLXContext createLegacyContextGLX(_GLFWwindow* window, + GLXFBConfig fbconfig, + GLXContext share) +{ + return glXCreateNewContext(_glfw.x11.display, + fbconfig, + GLX_RGBA_TYPE, + share, + True); +} + +static void makeContextCurrentGLX(_GLFWwindow* window) +{ + if (window) + { + if (!glXMakeCurrent(_glfw.x11.display, + window->context.glx.window, + window->context.glx.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to make context current"); + return; + } + } + else + { + if (!glXMakeCurrent(_glfw.x11.display, None, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to clear current context"); + return; + } + } + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffersGLX(_GLFWwindow* window) +{ + glXSwapBuffers(_glfw.x11.display, window->context.glx.window); +} + +static void swapIntervalGLX(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + if (_glfw.glx.EXT_swap_control) + { + _glfw.glx.SwapIntervalEXT(_glfw.x11.display, + window->context.glx.window, + interval); + } + else if (_glfw.glx.MESA_swap_control) + _glfw.glx.SwapIntervalMESA(interval); + else if (_glfw.glx.SGI_swap_control) + { + if (interval > 0) + _glfw.glx.SwapIntervalSGI(interval); + } +} + +static int extensionSupportedGLX(const char* extension) +{ + const char* extensions = + glXQueryExtensionsString(_glfw.x11.display, _glfw.x11.screen); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressGLX(const char* procname) +{ + if (_glfw.glx.GetProcAddress) + return _glfw.glx.GetProcAddress((const GLubyte*) procname); + else if (_glfw.glx.GetProcAddressARB) + return _glfw.glx.GetProcAddressARB((const GLubyte*) procname); + else + return dlsym(_glfw.glx.handle, procname); +} + +// Destroy the OpenGL context +// +static void destroyContextGLX(_GLFWwindow* window) +{ + if (window->context.glx.window) + { + glXDestroyWindow(_glfw.x11.display, window->context.glx.window); + window->context.glx.window = None; + } + + if (window->context.glx.handle) + { + glXDestroyContext(_glfw.x11.display, window->context.glx.handle); + window->context.glx.handle = NULL; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize GLX +// +GLFWbool _glfwInitGLX(void) +{ + int i; + const char* sonames[] = + { +#if defined(__CYGWIN__) + "libGL-1.so", +#else + "libGL.so.1", + "libGL.so", +#endif + NULL + }; + + if (_glfw.glx.handle) + return GLFW_TRUE; + + for (i = 0; sonames[i]; i++) + { + _glfw.glx.handle = dlopen(sonames[i], RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.glx.handle) + break; + } + + if (!_glfw.glx.handle) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: Failed to load GLX"); + return GLFW_FALSE; + } + + _glfw.glx.GetFBConfigs = + dlsym(_glfw.glx.handle, "glXGetFBConfigs"); + _glfw.glx.GetFBConfigAttrib = + dlsym(_glfw.glx.handle, "glXGetFBConfigAttrib"); + _glfw.glx.GetClientString = + dlsym(_glfw.glx.handle, "glXGetClientString"); + _glfw.glx.QueryExtension = + dlsym(_glfw.glx.handle, "glXQueryExtension"); + _glfw.glx.QueryVersion = + dlsym(_glfw.glx.handle, "glXQueryVersion"); + _glfw.glx.DestroyContext = + dlsym(_glfw.glx.handle, "glXDestroyContext"); + _glfw.glx.MakeCurrent = + dlsym(_glfw.glx.handle, "glXMakeCurrent"); + _glfw.glx.SwapBuffers = + dlsym(_glfw.glx.handle, "glXSwapBuffers"); + _glfw.glx.QueryExtensionsString = + dlsym(_glfw.glx.handle, "glXQueryExtensionsString"); + _glfw.glx.CreateNewContext = + dlsym(_glfw.glx.handle, "glXCreateNewContext"); + _glfw.glx.CreateWindow = + dlsym(_glfw.glx.handle, "glXCreateWindow"); + _glfw.glx.DestroyWindow = + dlsym(_glfw.glx.handle, "glXDestroyWindow"); + _glfw.glx.GetProcAddress = + dlsym(_glfw.glx.handle, "glXGetProcAddress"); + _glfw.glx.GetProcAddressARB = + dlsym(_glfw.glx.handle, "glXGetProcAddressARB"); + _glfw.glx.GetVisualFromFBConfig = + dlsym(_glfw.glx.handle, "glXGetVisualFromFBConfig"); + + if (!_glfw.glx.GetFBConfigs || + !_glfw.glx.GetFBConfigAttrib || + !_glfw.glx.GetClientString || + !_glfw.glx.QueryExtension || + !_glfw.glx.QueryVersion || + !_glfw.glx.DestroyContext || + !_glfw.glx.MakeCurrent || + !_glfw.glx.SwapBuffers || + !_glfw.glx.QueryExtensionsString || + !_glfw.glx.CreateNewContext || + !_glfw.glx.CreateWindow || + !_glfw.glx.DestroyWindow || + !_glfw.glx.GetProcAddress || + !_glfw.glx.GetProcAddressARB || + !_glfw.glx.GetVisualFromFBConfig) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to load required entry points"); + return GLFW_FALSE; + } + + if (!glXQueryExtension(_glfw.x11.display, + &_glfw.glx.errorBase, + &_glfw.glx.eventBase)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "GLX: GLX extension not found"); + return GLFW_FALSE; + } + + if (!glXQueryVersion(_glfw.x11.display, &_glfw.glx.major, &_glfw.glx.minor)) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: Failed to query GLX version"); + return GLFW_FALSE; + } + + if (_glfw.glx.major == 1 && _glfw.glx.minor < 3) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: GLX version 1.3 is required"); + return GLFW_FALSE; + } + + if (extensionSupportedGLX("GLX_EXT_swap_control")) + { + _glfw.glx.SwapIntervalEXT = (PFNGLXSWAPINTERVALEXTPROC) + getProcAddressGLX("glXSwapIntervalEXT"); + + if (_glfw.glx.SwapIntervalEXT) + _glfw.glx.EXT_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_SGI_swap_control")) + { + _glfw.glx.SwapIntervalSGI = (PFNGLXSWAPINTERVALSGIPROC) + getProcAddressGLX("glXSwapIntervalSGI"); + + if (_glfw.glx.SwapIntervalSGI) + _glfw.glx.SGI_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_MESA_swap_control")) + { + _glfw.glx.SwapIntervalMESA = (PFNGLXSWAPINTERVALMESAPROC) + getProcAddressGLX("glXSwapIntervalMESA"); + + if (_glfw.glx.SwapIntervalMESA) + _glfw.glx.MESA_swap_control = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_ARB_multisample")) + _glfw.glx.ARB_multisample = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_framebuffer_sRGB")) + _glfw.glx.ARB_framebuffer_sRGB = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_EXT_framebuffer_sRGB")) + _glfw.glx.EXT_framebuffer_sRGB = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_create_context")) + { + _glfw.glx.CreateContextAttribsARB = (PFNGLXCREATECONTEXTATTRIBSARBPROC) + getProcAddressGLX("glXCreateContextAttribsARB"); + + if (_glfw.glx.CreateContextAttribsARB) + _glfw.glx.ARB_create_context = GLFW_TRUE; + } + + if (extensionSupportedGLX("GLX_ARB_create_context_robustness")) + _glfw.glx.ARB_create_context_robustness = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_create_context_profile")) + _glfw.glx.ARB_create_context_profile = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_EXT_create_context_es2_profile")) + _glfw.glx.EXT_create_context_es2_profile = GLFW_TRUE; + + if (extensionSupportedGLX("GLX_ARB_context_flush_control")) + _glfw.glx.ARB_context_flush_control = GLFW_TRUE; + + return GLFW_TRUE; +} + +// Terminate GLX +// +void _glfwTerminateGLX(void) +{ + // NOTE: This function must not call any X11 functions, as it is called + // after XCloseDisplay (see _glfwPlatformTerminate for details) + + if (_glfw.glx.handle) + { + dlclose(_glfw.glx.handle); + _glfw.glx.handle = NULL; + } +} + +#define setGLXattrib(attribName, attribValue) \ +{ \ + attribs[index++] = attribName; \ + attribs[index++] = attribValue; \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + int attribs[40]; + GLXFBConfig native = NULL; + GLXContext share = NULL; + + if (ctxconfig->share) + share = ctxconfig->share->context.glx.handle; + + if (!chooseGLXFBConfig(fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "GLX: Failed to find a suitable GLXFBConfig"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + if (!_glfw.glx.ARB_create_context || + !_glfw.glx.ARB_create_context_profile || + !_glfw.glx.EXT_create_context_es2_profile) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "GLX: OpenGL ES requested but GLX_EXT_create_context_es2_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->forward) + { + if (!_glfw.glx.ARB_create_context) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "GLX: Forward compatibility requested but GLX_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->profile) + { + if (!_glfw.glx.ARB_create_context || + !_glfw.glx.ARB_create_context_profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "GLX: An OpenGL profile requested but GLX_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + + _glfwGrabErrorHandlerX11(); + + if (_glfw.glx.ARB_create_context) + { + int index = 0, mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + else + mask |= GLX_CONTEXT_ES2_PROFILE_BIT_EXT; + + if (ctxconfig->debug) + flags |= GLX_CONTEXT_DEBUG_BIT_ARB; + if (ctxconfig->noerror) + flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; + + if (ctxconfig->robustness) + { + if (_glfw.glx.ARB_create_context_robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_NO_RESET_NOTIFICATION_ARB); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setGLXattrib(GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + GLX_LOSE_CONTEXT_ON_RESET_ARB); + } + + flags |= GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + } + + if (ctxconfig->release) + { + if (_glfw.glx.ARB_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setGLXattrib(GLX_CONTEXT_RELEASE_BEHAVIOR_ARB, + GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + } + } + } + + // NOTE: Only request an explicitly versioned context when necessary, as + // explicitly requesting version 1.0 does not always return the + // highest version supported by the driver + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setGLXattrib(GLX_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setGLXattrib(GLX_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + } + + if (mask) + setGLXattrib(GLX_CONTEXT_PROFILE_MASK_ARB, mask); + + if (flags) + setGLXattrib(GLX_CONTEXT_FLAGS_ARB, flags); + + setGLXattrib(None, None); + + window->context.glx.handle = + _glfw.glx.CreateContextAttribsARB(_glfw.x11.display, + native, + share, + True, + attribs); + + // HACK: This is a fallback for broken versions of the Mesa + // implementation of GLX_ARB_create_context_profile that fail + // default 1.0 context creation with a GLXBadProfileARB error in + // violation of the extension spec + if (!window->context.glx.handle) + { + if (_glfw.x11.errorCode == _glfw.glx.errorBase + GLXBadProfileARB && + ctxconfig->client == GLFW_OPENGL_API && + ctxconfig->profile == GLFW_OPENGL_ANY_PROFILE && + ctxconfig->forward == GLFW_FALSE) + { + window->context.glx.handle = + createLegacyContextGLX(window, native, share); + } + } + } + else + { + window->context.glx.handle = + createLegacyContextGLX(window, native, share); + } + + _glfwReleaseErrorHandlerX11(); + + if (!window->context.glx.handle) + { + _glfwInputErrorX11(GLFW_VERSION_UNAVAILABLE, "GLX: Failed to create context"); + return GLFW_FALSE; + } + + window->context.glx.window = + glXCreateWindow(_glfw.x11.display, native, window->x11.handle, NULL); + if (!window->context.glx.window) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "GLX: Failed to create window"); + return GLFW_FALSE; + } + + window->context.makeCurrent = makeContextCurrentGLX; + window->context.swapBuffers = swapBuffersGLX; + window->context.swapInterval = swapIntervalGLX; + window->context.extensionSupported = extensionSupportedGLX; + window->context.getProcAddress = getProcAddressGLX; + window->context.destroy = destroyContextGLX; + + return GLFW_TRUE; +} + +#undef setGLXattrib + +// Returns the Visual and depth of the chosen GLXFBConfig +// +GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth) +{ + GLXFBConfig native; + XVisualInfo* result; + + if (!chooseGLXFBConfig(fbconfig, &native)) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "GLX: Failed to find a suitable GLXFBConfig"); + return GLFW_FALSE; + } + + result = glXGetVisualFromFBConfig(_glfw.x11.display, native); + if (!result) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "GLX: Failed to retrieve Visual for GLXFBConfig"); + return GLFW_FALSE; + } + + *visual = result->visual; + *depth = result->depth; + + XFree(result); + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.glx.handle; +} + +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return None; + } + + return window->context.glx.window; +} + diff --git a/apps/exampleViewer/common/glfw/src/glx_context.h b/apps/exampleViewer/common/glfw/src/glx_context.h new file mode 100644 index 0000000000..15e4f9b259 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/glx_context.h @@ -0,0 +1,182 @@ +//======================================================================== +// GLFW 3.3 GLX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_glx_context_h_ +#define _glfw3_glx_context_h_ + +#define GLX_VENDOR 1 +#define GLX_RGBA_BIT 0x00000001 +#define GLX_WINDOW_BIT 0x00000001 +#define GLX_DRAWABLE_TYPE 0x8010 +#define GLX_RENDER_TYPE 0x8011 +#define GLX_RGBA_TYPE 0x8014 +#define GLX_DOUBLEBUFFER 5 +#define GLX_STEREO 6 +#define GLX_AUX_BUFFERS 7 +#define GLX_RED_SIZE 8 +#define GLX_GREEN_SIZE 9 +#define GLX_BLUE_SIZE 10 +#define GLX_ALPHA_SIZE 11 +#define GLX_DEPTH_SIZE 12 +#define GLX_STENCIL_SIZE 13 +#define GLX_ACCUM_RED_SIZE 14 +#define GLX_ACCUM_GREEN_SIZE 15 +#define GLX_ACCUM_BLUE_SIZE 16 +#define GLX_ACCUM_ALPHA_SIZE 17 +#define GLX_SAMPLES 0x186a1 +#define GLX_VISUAL_ID 0x800b + +#define GLX_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20b2 +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define GLX_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#define GLX_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define GLX_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define GLX_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GLX_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define GLX_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 + +typedef XID GLXWindow; +typedef XID GLXDrawable; +typedef struct __GLXFBConfig* GLXFBConfig; +typedef struct __GLXcontext* GLXContext; +typedef void (*__GLXextproc)(void); + +typedef int (*PFNGLXGETFBCONFIGATTRIBPROC)(Display*,GLXFBConfig,int,int*); +typedef const char* (*PFNGLXGETCLIENTSTRINGPROC)(Display*,int); +typedef Bool (*PFNGLXQUERYEXTENSIONPROC)(Display*,int*,int*); +typedef Bool (*PFNGLXQUERYVERSIONPROC)(Display*,int*,int*); +typedef void (*PFNGLXDESTROYCONTEXTPROC)(Display*,GLXContext); +typedef Bool (*PFNGLXMAKECURRENTPROC)(Display*,GLXDrawable,GLXContext); +typedef void (*PFNGLXSWAPBUFFERSPROC)(Display*,GLXDrawable); +typedef const char* (*PFNGLXQUERYEXTENSIONSSTRINGPROC)(Display*,int); +typedef GLXFBConfig* (*PFNGLXGETFBCONFIGSPROC)(Display*,int,int*); +typedef GLXContext (*PFNGLXCREATENEWCONTEXTPROC)(Display*,GLXFBConfig,int,GLXContext,Bool); +typedef __GLXextproc (* PFNGLXGETPROCADDRESSPROC)(const GLubyte *procName); +typedef int (*PFNGLXSWAPINTERVALMESAPROC)(int); +typedef int (*PFNGLXSWAPINTERVALSGIPROC)(int); +typedef void (*PFNGLXSWAPINTERVALEXTPROC)(Display*,GLXDrawable,int); +typedef GLXContext (*PFNGLXCREATECONTEXTATTRIBSARBPROC)(Display*,GLXFBConfig,GLXContext,Bool,const int*); +typedef XVisualInfo* (*PFNGLXGETVISUALFROMFBCONFIGPROC)(Display*,GLXFBConfig); +typedef GLXWindow (*PFNGLXCREATEWINDOWPROC)(Display*,GLXFBConfig,Window,const int*); +typedef void (*PFNGLXDESTROYWINDOWPROC)(Display*,GLXWindow); + +// libGL.so function pointer typedefs +#define glXGetFBConfigs _glfw.glx.GetFBConfigs +#define glXGetFBConfigAttrib _glfw.glx.GetFBConfigAttrib +#define glXGetClientString _glfw.glx.GetClientString +#define glXQueryExtension _glfw.glx.QueryExtension +#define glXQueryVersion _glfw.glx.QueryVersion +#define glXDestroyContext _glfw.glx.DestroyContext +#define glXMakeCurrent _glfw.glx.MakeCurrent +#define glXSwapBuffers _glfw.glx.SwapBuffers +#define glXQueryExtensionsString _glfw.glx.QueryExtensionsString +#define glXCreateNewContext _glfw.glx.CreateNewContext +#define glXGetVisualFromFBConfig _glfw.glx.GetVisualFromFBConfig +#define glXCreateWindow _glfw.glx.CreateWindow +#define glXDestroyWindow _glfw.glx.DestroyWindow + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextGLX glx +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryGLX glx + + +// GLX-specific per-context data +// +typedef struct _GLFWcontextGLX +{ + GLXContext handle; + GLXWindow window; + +} _GLFWcontextGLX; + +// GLX-specific global data +// +typedef struct _GLFWlibraryGLX +{ + int major, minor; + int eventBase; + int errorBase; + + // dlopen handle for libGL.so.1 + void* handle; + + // GLX 1.3 functions + PFNGLXGETFBCONFIGSPROC GetFBConfigs; + PFNGLXGETFBCONFIGATTRIBPROC GetFBConfigAttrib; + PFNGLXGETCLIENTSTRINGPROC GetClientString; + PFNGLXQUERYEXTENSIONPROC QueryExtension; + PFNGLXQUERYVERSIONPROC QueryVersion; + PFNGLXDESTROYCONTEXTPROC DestroyContext; + PFNGLXMAKECURRENTPROC MakeCurrent; + PFNGLXSWAPBUFFERSPROC SwapBuffers; + PFNGLXQUERYEXTENSIONSSTRINGPROC QueryExtensionsString; + PFNGLXCREATENEWCONTEXTPROC CreateNewContext; + PFNGLXGETVISUALFROMFBCONFIGPROC GetVisualFromFBConfig; + PFNGLXCREATEWINDOWPROC CreateWindow; + PFNGLXDESTROYWINDOWPROC DestroyWindow; + + // GLX 1.4 and extension functions + PFNGLXGETPROCADDRESSPROC GetProcAddress; + PFNGLXGETPROCADDRESSPROC GetProcAddressARB; + PFNGLXSWAPINTERVALSGIPROC SwapIntervalSGI; + PFNGLXSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNGLXSWAPINTERVALMESAPROC SwapIntervalMESA; + PFNGLXCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool SGI_swap_control; + GLFWbool EXT_swap_control; + GLFWbool MESA_swap_control; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_context_flush_control; + +} _GLFWlibraryGLX; + + +GLFWbool _glfwInitGLX(void); +void _glfwTerminateGLX(void); +GLFWbool _glfwCreateContextGLX(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextGLX(_GLFWwindow* window); +GLFWbool _glfwChooseVisualGLX(const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig, + Visual** visual, int* depth); + +#endif // _glfw3_glx_context_h_ diff --git a/apps/exampleViewer/common/glfw/src/init.c b/apps/exampleViewer/common/glfw/src/init.c new file mode 100644 index 0000000000..e0ebfadee2 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/init.c @@ -0,0 +1,200 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +// The three global variables below comprise all global data in GLFW. +// Any other global variable is a bug. + +// Global state shared between compilation units of GLFW +// These are documented in internal.h +// +GLFWbool _glfwInitialized = GLFW_FALSE; +_GLFWlibrary _glfw; + +// This is outside of _glfw so it can be initialized and usable before +// glfwInit is called, which lets that function report errors +// +static GLFWerrorfun _glfwErrorCallback = NULL; + + +// Returns a generic string representation of the specified error +// +static const char* getErrorString(int error) +{ + switch (error) + { + case GLFW_NOT_INITIALIZED: + return "The GLFW library is not initialized"; + case GLFW_NO_CURRENT_CONTEXT: + return "There is no current context"; + case GLFW_INVALID_ENUM: + return "Invalid argument for enum parameter"; + case GLFW_INVALID_VALUE: + return "Invalid value for parameter"; + case GLFW_OUT_OF_MEMORY: + return "Out of memory"; + case GLFW_API_UNAVAILABLE: + return "The requested API is unavailable"; + case GLFW_VERSION_UNAVAILABLE: + return "The requested API version is unavailable"; + case GLFW_PLATFORM_ERROR: + return "A platform-specific error occurred"; + case GLFW_FORMAT_UNAVAILABLE: + return "The requested format is unavailable"; + case GLFW_NO_WINDOW_CONTEXT: + return "The specified window has no context"; + default: + return "ERROR: UNKNOWN GLFW ERROR"; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputError(int error, const char* format, ...) +{ + if (_glfwErrorCallback) + { + char buffer[8192]; + const char* description; + + if (format) + { + int count; + va_list vl; + + va_start(vl, format); + count = vsnprintf(buffer, sizeof(buffer), format, vl); + va_end(vl); + + if (count < 0) + buffer[sizeof(buffer) - 1] = '\0'; + + description = buffer; + } + else + description = getErrorString(error); + + _glfwErrorCallback(error, description); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwInit(void) +{ + if (_glfwInitialized) + return GLFW_TRUE; + + memset(&_glfw, 0, sizeof(_glfw)); + + if (!_glfwPlatformInit()) + { + _glfwPlatformTerminate(); + return GLFW_FALSE; + } + + _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); + _glfwInitialized = GLFW_TRUE; + + _glfw.timerOffset = _glfwPlatformGetTimerValue(); + + // Not all window hints have zero as their default value + glfwDefaultWindowHints(); + + return GLFW_TRUE; +} + +GLFWAPI void glfwTerminate(void) +{ + int i; + + if (!_glfwInitialized) + return; + + memset(&_glfw.callbacks, 0, sizeof(_glfw.callbacks)); + + while (_glfw.windowListHead) + glfwDestroyWindow((GLFWwindow*) _glfw.windowListHead); + + while (_glfw.cursorListHead) + glfwDestroyCursor((GLFWcursor*) _glfw.cursorListHead); + + for (i = 0; i < _glfw.monitorCount; i++) + { + _GLFWmonitor* monitor = _glfw.monitors[i]; + if (monitor->originalRamp.size) + _glfwPlatformSetGammaRamp(monitor, &monitor->originalRamp); + } + + _glfwTerminateVulkan(); + + _glfwFreeMonitors(_glfw.monitors, _glfw.monitorCount); + _glfw.monitors = NULL; + _glfw.monitorCount = 0; + + _glfwPlatformTerminate(); + + memset(&_glfw, 0, sizeof(_glfw)); + _glfwInitialized = GLFW_FALSE; +} + +GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev) +{ + if (major != NULL) + *major = GLFW_VERSION_MAJOR; + + if (minor != NULL) + *minor = GLFW_VERSION_MINOR; + + if (rev != NULL) + *rev = GLFW_VERSION_REVISION; +} + +GLFWAPI const char* glfwGetVersionString(void) +{ + return _glfwPlatformGetVersionString(); +} + +GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun) +{ + _GLFW_SWAP_POINTERS(_glfwErrorCallback, cbfun); + return cbfun; +} + diff --git a/apps/exampleViewer/common/glfw/src/input.c b/apps/exampleViewer/common/glfw/src/input.c new file mode 100644 index 0000000000..cceae35da2 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/input.c @@ -0,0 +1,672 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + +// Internal key state used for sticky keys +#define _GLFW_STICK 3 + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key >= 0 && key <= GLFW_KEY_LAST) + { + GLFWbool repeated = GLFW_FALSE; + + if (action == GLFW_RELEASE && window->keys[key] == GLFW_RELEASE) + return; + + if (action == GLFW_PRESS && window->keys[key] == GLFW_PRESS) + repeated = GLFW_TRUE; + + if (action == GLFW_RELEASE && window->stickyKeys) + window->keys[key] = _GLFW_STICK; + else + window->keys[key] = (char) action; + + if (repeated) + action = GLFW_REPEAT; + } + + if (window->callbacks.key) + window->callbacks.key((GLFWwindow*) window, key, scancode, action, mods); +} + +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain) +{ + if (codepoint < 32 || (codepoint > 126 && codepoint < 160)) + return; + + if (window->callbacks.charmods) + window->callbacks.charmods((GLFWwindow*) window, codepoint, mods); + + if (plain) + { + if (window->callbacks.character) + window->callbacks.character((GLFWwindow*) window, codepoint); + } +} + +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset) +{ + if (window->callbacks.scroll) + window->callbacks.scroll((GLFWwindow*) window, xoffset, yoffset); +} + +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods) +{ + if (button < 0 || button > GLFW_MOUSE_BUTTON_LAST) + return; + + // Register mouse button action + if (action == GLFW_RELEASE && window->stickyMouseButtons) + window->mouseButtons[button] = _GLFW_STICK; + else + window->mouseButtons[button] = (char) action; + + if (window->callbacks.mouseButton) + window->callbacks.mouseButton((GLFWwindow*) window, button, action, mods); +} + +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + if (window->virtualCursorPosX == xpos && window->virtualCursorPosY == ypos) + return; + + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + + if (window->callbacks.cursorPos) + window->callbacks.cursorPos((GLFWwindow*) window, xpos, ypos); +} + +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered) +{ + if (window->callbacks.cursorEnter) + window->callbacks.cursorEnter((GLFWwindow*) window, entered); +} + +void _glfwInputDrop(_GLFWwindow* window, int count, const char** paths) +{ + if (window->callbacks.drop) + window->callbacks.drop((GLFWwindow*) window, count, paths); +} + +void _glfwInputJoystickChange(int jid, int event) +{ + if (_glfw.callbacks.joystick) + _glfw.callbacks.joystick(jid, event); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwIsPrintable(int key) +{ + return (key >= GLFW_KEY_APOSTROPHE && key <= GLFW_KEY_WORLD_2) || + (key >= GLFW_KEY_KP_0 && key <= GLFW_KEY_KP_ADD) || + key == GLFW_KEY_KP_EQUAL; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (mode) + { + case GLFW_CURSOR: + return window->cursorMode; + case GLFW_STICKY_KEYS: + return window->stickyKeys; + case GLFW_STICKY_MOUSE_BUTTONS: + return window->stickyMouseButtons; + default: + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); + return 0; + } +} + +GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + switch (mode) + { + case GLFW_CURSOR: + { + if (value != GLFW_CURSOR_NORMAL && + value != GLFW_CURSOR_HIDDEN && + value != GLFW_CURSOR_DISABLED) + { + _glfwInputError(GLFW_INVALID_ENUM, + "Invalid cursor mode %i", + value); + return; + } + + if (window->cursorMode == value) + return; + + window->cursorMode = value; + + _glfwPlatformGetCursorPos(window, + &window->virtualCursorPosX, + &window->virtualCursorPosY); + + if (_glfwPlatformWindowFocused(window)) + _glfwPlatformSetCursorMode(window, value); + + return; + } + + case GLFW_STICKY_KEYS: + { + if (window->stickyKeys == value) + return; + + if (!value) + { + int i; + + // Release all sticky keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == _GLFW_STICK) + window->keys[i] = GLFW_RELEASE; + } + } + + window->stickyKeys = value ? GLFW_TRUE : GLFW_FALSE; + return; + } + + case GLFW_STICKY_MOUSE_BUTTONS: + { + if (window->stickyMouseButtons == value) + return; + + if (!value) + { + int i; + + // Release all sticky mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == _GLFW_STICK) + window->mouseButtons[i] = GLFW_RELEASE; + } + } + + window->stickyMouseButtons = value ? GLFW_TRUE : GLFW_FALSE; + return; + } + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode %i", mode); +} + +GLFWAPI const char* glfwGetKeyName(int key, int scancode) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetKeyName(key, scancode); +} + +GLFWAPI int glfwGetKeyScancode(int key) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(-1); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + return _glfwPlatformGetKeyScancode(key); +} + +GLFWAPI int glfwGetKey(GLFWwindow* handle, int key) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (key < GLFW_KEY_SPACE || key > GLFW_KEY_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid key %i", key); + return GLFW_RELEASE; + } + + if (window->keys[key] == _GLFW_STICK) + { + // Sticky mode: release key now + window->keys[key] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->keys[key]; +} + +GLFWAPI int glfwGetMouseButton(GLFWwindow* handle, int button) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_RELEASE); + + if (button < GLFW_MOUSE_BUTTON_1 || button > GLFW_MOUSE_BUTTON_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid mouse button %i", button); + return GLFW_RELEASE; + } + + if (window->mouseButtons[button] == _GLFW_STICK) + { + // Sticky mode: release mouse button now + window->mouseButtons[button] = GLFW_RELEASE; + return GLFW_PRESS; + } + + return (int) window->mouseButtons[button]; +} + +GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (xpos) + *xpos = window->virtualCursorPosX; + if (ypos) + *ypos = window->virtualCursorPosY; + } + else + _glfwPlatformGetCursorPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (xpos != xpos || xpos < -DBL_MAX || xpos > DBL_MAX || + ypos != ypos || ypos < -DBL_MAX || ypos > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid cursor position %f %f", + xpos, ypos); + return; + } + + if (!_glfwPlatformWindowFocused(window)) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + // Only update the accumulated position if the cursor is disabled + window->virtualCursorPosX = xpos; + window->virtualCursorPosY = ypos; + } + else + { + // Update system cursor position + _glfwPlatformSetCursorPos(window, xpos, ypos); + } +} + +GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) +{ + _GLFWcursor* cursor; + + assert(image != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateCursor(cursor, image, xhot, yhot)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape) +{ + _GLFWcursor* cursor; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (shape != GLFW_ARROW_CURSOR && + shape != GLFW_IBEAM_CURSOR && + shape != GLFW_CROSSHAIR_CURSOR && + shape != GLFW_HAND_CURSOR && + shape != GLFW_HRESIZE_CURSOR && + shape != GLFW_VRESIZE_CURSOR) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid standard cursor %i", shape); + return NULL; + } + + cursor = calloc(1, sizeof(_GLFWcursor)); + cursor->next = _glfw.cursorListHead; + _glfw.cursorListHead = cursor; + + if (!_glfwPlatformCreateStandardCursor(cursor, shape)) + { + glfwDestroyCursor((GLFWcursor*) cursor); + return NULL; + } + + return (GLFWcursor*) cursor; +} + +GLFWAPI void glfwDestroyCursor(GLFWcursor* handle) +{ + _GLFWcursor* cursor = (_GLFWcursor*) handle; + + _GLFW_REQUIRE_INIT(); + + if (cursor == NULL) + return; + + // Make sure the cursor is not being used by any window + { + _GLFWwindow* window; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->cursor == cursor) + glfwSetCursor((GLFWwindow*) window, NULL); + } + } + + _glfwPlatformDestroyCursor(cursor); + + // Unlink cursor from global linked list + { + _GLFWcursor** prev = &_glfw.cursorListHead; + + while (*prev != cursor) + prev = &((*prev)->next); + + *prev = cursor->next; + } + + free(cursor); +} + +GLFWAPI void glfwSetCursor(GLFWwindow* windowHandle, GLFWcursor* cursorHandle) +{ + _GLFWwindow* window = (_GLFWwindow*) windowHandle; + _GLFWcursor* cursor = (_GLFWcursor*) cursorHandle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + window->cursor = cursor; + + _glfwPlatformSetCursor(window, cursor); +} + +GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* handle, GLFWkeyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.key, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* handle, GLFWcharfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.character, cbfun); + return cbfun; +} + +GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* handle, GLFWcharmodsfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.charmods, cbfun); + return cbfun; +} + +GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* handle, + GLFWmousebuttonfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.mouseButton, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* handle, + GLFWcursorposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorPos, cbfun); + return cbfun; +} + +GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* handle, + GLFWcursorenterfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.cursorEnter, cbfun); + return cbfun; +} + +GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* handle, + GLFWscrollfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.scroll, cbfun); + return cbfun; +} + +GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* handle, GLFWdropfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.drop, cbfun); + return cbfun; +} + +GLFWAPI int glfwJoystickPresent(int jid) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + return 0; + } + + return _glfwPlatformJoystickPresent(jid); +} + +GLFWAPI const float* glfwGetJoystickAxes(int jid, int* count) +{ + assert(count != NULL); + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + return NULL; + } + + return _glfwPlatformGetJoystickAxes(jid, count); +} + +GLFWAPI const unsigned char* glfwGetJoystickButtons(int jid, int* count) +{ + assert(count != NULL); + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + return NULL; + } + + return _glfwPlatformGetJoystickButtons(jid, count); +} + +GLFWAPI const char* glfwGetJoystickName(int jid) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (jid < 0 || jid > GLFW_JOYSTICK_LAST) + { + _glfwInputError(GLFW_INVALID_ENUM, "Invalid joystick %i", jid); + return NULL; + } + + return _glfwPlatformGetJoystickName(jid); +} + +GLFWAPI GLFWjoystickfun glfwSetJoystickCallback(GLFWjoystickfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.joystick, cbfun); + return cbfun; +} + +GLFWAPI void glfwSetClipboardString(GLFWwindow* handle, const char* string) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(string != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetClipboardString(window, string); +} + +GLFWAPI const char* glfwGetClipboardString(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfwPlatformGetClipboardString(window); +} + +GLFWAPI double glfwGetTime(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0.0); + return (double) (_glfwPlatformGetTimerValue() - _glfw.timerOffset) / + _glfwPlatformGetTimerFrequency(); +} + +GLFWAPI void glfwSetTime(double time) +{ + _GLFW_REQUIRE_INIT(); + + if (time != time || time < 0.0 || time > 18446744073.0) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", time); + return; + } + + _glfw.timerOffset = _glfwPlatformGetTimerValue() - + (uint64_t) (time * _glfwPlatformGetTimerFrequency()); +} + +GLFWAPI uint64_t glfwGetTimerValue(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerValue(); +} + +GLFWAPI uint64_t glfwGetTimerFrequency(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return _glfwPlatformGetTimerFrequency(); +} + diff --git a/apps/exampleViewer/common/glfw/src/internal.h b/apps/exampleViewer/common/glfw/src/internal.h new file mode 100644 index 0000000000..a623262e84 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/internal.h @@ -0,0 +1,1073 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_internal_h_ +#define _glfw3_internal_h_ + + +#if defined(_GLFW_USE_CONFIG_H) + #include "glfw_config.h" +#endif + +#if defined(GLFW_INCLUDE_GLCOREARB) || \ + defined(GLFW_INCLUDE_ES1) || \ + defined(GLFW_INCLUDE_ES2) || \ + defined(GLFW_INCLUDE_ES3) || \ + defined(GLFW_INCLUDE_NONE) || \ + defined(GLFW_INCLUDE_GLEXT) || \ + defined(GLFW_INCLUDE_GLU) || \ + defined(GLFW_INCLUDE_VULKAN) || \ + defined(GLFW_DLL) + #error "You must not define any header option macros when compiling GLFW" +#endif + +#define GLFW_INCLUDE_NONE +#include "../include/GLFW/glfw3.h" + +typedef int GLFWbool; + +typedef struct _GLFWwndconfig _GLFWwndconfig; +typedef struct _GLFWctxconfig _GLFWctxconfig; +typedef struct _GLFWfbconfig _GLFWfbconfig; +typedef struct _GLFWcontext _GLFWcontext; +typedef struct _GLFWwindow _GLFWwindow; +typedef struct _GLFWlibrary _GLFWlibrary; +typedef struct _GLFWmonitor _GLFWmonitor; +typedef struct _GLFWcursor _GLFWcursor; + +typedef void (* _GLFWmakecontextcurrentfun)(_GLFWwindow*); +typedef void (* _GLFWswapbuffersfun)(_GLFWwindow*); +typedef void (* _GLFWswapintervalfun)(int); +typedef int (* _GLFWextensionsupportedfun)(const char*); +typedef GLFWglproc (* _GLFWgetprocaddressfun)(const char*); +typedef void (* _GLFWdestroycontextfun)(_GLFWwindow*); + +#define GL_VERSION 0x1f02 +#define GL_NONE 0 +#define GL_COLOR_BUFFER_BIT 0x00004000 +#define GL_EXTENSIONS 0x1f03 +#define GL_NUM_EXTENSIONS 0x821d +#define GL_CONTEXT_FLAGS 0x821e +#define GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT 0x00000001 +#define GL_CONTEXT_FLAG_DEBUG_BIT 0x00000002 +#define GL_CONTEXT_PROFILE_MASK 0x9126 +#define GL_CONTEXT_COMPATIBILITY_PROFILE_BIT 0x00000002 +#define GL_CONTEXT_CORE_PROFILE_BIT 0x00000001 +#define GL_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define GL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define GL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define GL_CONTEXT_RELEASE_BEHAVIOR 0x82fb +#define GL_CONTEXT_RELEASE_BEHAVIOR_FLUSH 0x82fc +#define GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR 0x00000008 + +typedef int GLint; +typedef unsigned int GLuint; +typedef unsigned int GLenum; +typedef unsigned int GLbitfield; +typedef unsigned char GLubyte; + +typedef void (APIENTRY * PFNGLCLEARPROC)(GLbitfield); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGPROC)(GLenum); +typedef void (APIENTRY * PFNGLGETINTEGERVPROC)(GLenum,GLint*); +typedef const GLubyte* (APIENTRY * PFNGLGETSTRINGIPROC)(GLenum,GLuint); + +#define VK_NULL_HANDLE 0 + +typedef void* VkInstance; +typedef void* VkPhysicalDevice; +typedef uint64_t VkSurfaceKHR; +typedef uint32_t VkFlags; +typedef uint32_t VkBool32; + +typedef enum VkStructureType +{ + VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR = 1000004000, + VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR = 1000005000, + VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR = 1000006000, + VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR = 1000007000, + VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR = 1000009000, + VK_STRUCTURE_TYPE_MAX_ENUM = 0x7FFFFFFF +} VkStructureType; + +typedef enum VkResult +{ + VK_SUCCESS = 0, + VK_NOT_READY = 1, + VK_TIMEOUT = 2, + VK_EVENT_SET = 3, + VK_EVENT_RESET = 4, + VK_INCOMPLETE = 5, + VK_ERROR_OUT_OF_HOST_MEMORY = -1, + VK_ERROR_OUT_OF_DEVICE_MEMORY = -2, + VK_ERROR_INITIALIZATION_FAILED = -3, + VK_ERROR_DEVICE_LOST = -4, + VK_ERROR_MEMORY_MAP_FAILED = -5, + VK_ERROR_LAYER_NOT_PRESENT = -6, + VK_ERROR_EXTENSION_NOT_PRESENT = -7, + VK_ERROR_FEATURE_NOT_PRESENT = -8, + VK_ERROR_INCOMPATIBLE_DRIVER = -9, + VK_ERROR_TOO_MANY_OBJECTS = -10, + VK_ERROR_FORMAT_NOT_SUPPORTED = -11, + VK_ERROR_SURFACE_LOST_KHR = -1000000000, + VK_SUBOPTIMAL_KHR = 1000001003, + VK_ERROR_OUT_OF_DATE_KHR = -1000001004, + VK_ERROR_INCOMPATIBLE_DISPLAY_KHR = -1000003001, + VK_ERROR_NATIVE_WINDOW_IN_USE_KHR = -1000000001, + VK_ERROR_VALIDATION_FAILED_EXT = -1000011001, + VK_RESULT_MAX_ENUM = 0x7FFFFFFF +} VkResult; + +typedef struct VkAllocationCallbacks VkAllocationCallbacks; + +typedef struct VkExtensionProperties +{ + char extensionName[256]; + uint32_t specVersion; +} VkExtensionProperties; + +typedef void (APIENTRY * PFN_vkVoidFunction)(void); + +#if defined(_GLFW_VULKAN_STATIC) + PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance,const char*); + VkResult vkEnumerateInstanceExtensionProperties(const char*,uint32_t*,VkExtensionProperties*); +#else + typedef PFN_vkVoidFunction (APIENTRY * PFN_vkGetInstanceProcAddr)(VkInstance,const char*); + typedef VkResult (APIENTRY * PFN_vkEnumerateInstanceExtensionProperties)(const char*,uint32_t*,VkExtensionProperties*); + #define vkEnumerateInstanceExtensionProperties _glfw.vk.EnumerateInstanceExtensionProperties + #define vkGetInstanceProcAddr _glfw.vk.GetInstanceProcAddr +#endif + +#if defined(_GLFW_COCOA) + #include "cocoa_platform.h" +#elif defined(_GLFW_WIN32) + #include "win32_platform.h" +#elif defined(_GLFW_X11) + #include "x11_platform.h" +#elif defined(_GLFW_WAYLAND) + #include "wl_platform.h" +#elif defined(_GLFW_MIR) + #include "mir_platform.h" +#else + #error "No supported window creation API selected" +#endif + + +//======================================================================== +// Doxygen group definitions +//======================================================================== + +/*! @defgroup platform Platform interface + * @brief The interface implemented by the platform-specific code. + * + * The platform API is the interface exposed by the platform-specific code for + * each platform and is called by the shared code of the public API It mirrors + * the public API except it uses objects instead of handles. + */ +/*! @defgroup event Event interface + * @brief The interface used by the platform-specific code to report events. + * + * The event API is used by the platform-specific code to notify the shared + * code of events that can be translated into state changes and/or callback + * calls. + */ +/*! @defgroup utility Utility functions + * @brief Various utility functions for internal use. + * + * These functions are shared code and may be used by any part of GLFW + * Each platform may add its own utility functions, but those must only be + * called by the platform-specific code + */ + + +//======================================================================== +// Helper macros +//======================================================================== + +// Constructs a version number string from the public header macros +#define _GLFW_CONCAT_VERSION(m, n, r) #m "." #n "." #r +#define _GLFW_MAKE_VERSION(m, n, r) _GLFW_CONCAT_VERSION(m, n, r) +#define _GLFW_VERSION_NUMBER _GLFW_MAKE_VERSION(GLFW_VERSION_MAJOR, \ + GLFW_VERSION_MINOR, \ + GLFW_VERSION_REVISION) + +// Checks for whether the library has been initialized +#define _GLFW_REQUIRE_INIT() \ + if (!_glfwInitialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return; \ + } +#define _GLFW_REQUIRE_INIT_OR_RETURN(x) \ + if (!_glfwInitialized) \ + { \ + _glfwInputError(GLFW_NOT_INITIALIZED, NULL); \ + return x; \ + } + +// Swaps the provided pointers +#define _GLFW_SWAP_POINTERS(x, y) \ + { \ + void* t; \ + t = x; \ + x = y; \ + y = t; \ + } + + +//======================================================================== +// Platform-independent structures +//======================================================================== + +/*! @brief Window configuration. + * + * Parameters relating to the creation of the window but not directly related + * to the framebuffer. This is used to pass window creation parameters from + * shared code to the platform API. + */ +struct _GLFWwndconfig +{ + int width; + int height; + const char* title; + GLFWbool resizable; + GLFWbool visible; + GLFWbool decorated; + GLFWbool focused; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool maximized; +}; + +/*! @brief Context configuration. + * + * Parameters relating to the creation of the context but not directly related + * to the framebuffer. This is used to pass context creation parameters from + * shared code to the platform API. + */ +struct _GLFWctxconfig +{ + int client; + int source; + int major; + int minor; + GLFWbool forward; + GLFWbool debug; + GLFWbool noerror; + int profile; + int robustness; + int release; + _GLFWwindow* share; +}; + +/*! @brief Framebuffer configuration. + * + * This describes buffers and their sizes. It also contains + * a platform-specific ID used to map back to the backend API object. + * + * It is used to pass framebuffer parameters from shared code to the platform + * API and also to enumerate and select available framebuffer configs. + */ +struct _GLFWfbconfig +{ + int redBits; + int greenBits; + int blueBits; + int alphaBits; + int depthBits; + int stencilBits; + int accumRedBits; + int accumGreenBits; + int accumBlueBits; + int accumAlphaBits; + int auxBuffers; + GLFWbool stereo; + int samples; + GLFWbool sRGB; + GLFWbool doublebuffer; + uintptr_t handle; +}; + +/*! @brief Context structure. + */ +struct _GLFWcontext +{ + int client; + int source; + int major, minor, revision; + GLFWbool forward, debug, noerror; + int profile; + int robustness; + int release; + + PFNGLGETSTRINGIPROC GetStringi; + PFNGLGETINTEGERVPROC GetIntegerv; + PFNGLGETSTRINGPROC GetString; + + _GLFWmakecontextcurrentfun makeCurrent; + _GLFWswapbuffersfun swapBuffers; + _GLFWswapintervalfun swapInterval; + _GLFWextensionsupportedfun extensionSupported; + _GLFWgetprocaddressfun getProcAddress; + _GLFWdestroycontextfun destroy; + + // This is defined in the context API's context.h + _GLFW_PLATFORM_CONTEXT_STATE; + // This is defined in egl_context.h + _GLFW_EGL_CONTEXT_STATE; +}; + +/*! @brief Window and context structure. + */ +struct _GLFWwindow +{ + struct _GLFWwindow* next; + + // Window settings and state + GLFWbool resizable; + GLFWbool decorated; + GLFWbool autoIconify; + GLFWbool floating; + GLFWbool closed; + void* userPointer; + GLFWvidmode videoMode; + _GLFWmonitor* monitor; + _GLFWcursor* cursor; + + int minwidth, minheight; + int maxwidth, maxheight; + int numer, denom; + + GLFWbool stickyKeys; + GLFWbool stickyMouseButtons; + int cursorMode; + char mouseButtons[GLFW_MOUSE_BUTTON_LAST + 1]; + char keys[GLFW_KEY_LAST + 1]; + // Virtual cursor position when cursor is disabled + double virtualCursorPosX, virtualCursorPosY; + + _GLFWcontext context; + + struct { + GLFWwindowposfun pos; + GLFWwindowsizefun size; + GLFWwindowclosefun close; + GLFWwindowrefreshfun refresh; + GLFWwindowfocusfun focus; + GLFWwindowiconifyfun iconify; + GLFWwindowmaximizefun maximize; + GLFWframebuffersizefun fbsize; + GLFWmousebuttonfun mouseButton; + GLFWcursorposfun cursorPos; + GLFWcursorenterfun cursorEnter; + GLFWscrollfun scroll; + GLFWkeyfun key; + GLFWcharfun character; + GLFWcharmodsfun charmods; + GLFWdropfun drop; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_WINDOW_STATE; +}; + +/*! @brief Monitor structure. + */ +struct _GLFWmonitor +{ + char* name; + + // Physical dimensions in millimeters. + int widthMM, heightMM; + + // The window whose video mode is current on this monitor + _GLFWwindow* window; + + GLFWvidmode* modes; + int modeCount; + GLFWvidmode currentMode; + + GLFWgammaramp originalRamp; + GLFWgammaramp currentRamp; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_MONITOR_STATE; +}; + +/*! @brief Cursor structure + */ +struct _GLFWcursor +{ + _GLFWcursor* next; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_CURSOR_STATE; +}; + +/*! @brief Library global data. + */ +struct _GLFWlibrary +{ + struct { + _GLFWfbconfig framebuffer; + _GLFWwndconfig window; + _GLFWctxconfig context; + int refreshRate; + } hints; + + _GLFWcursor* cursorListHead; + + _GLFWwindow* windowListHead; + + _GLFWmonitor** monitors; + int monitorCount; + + uint64_t timerOffset; + + struct { + GLFWbool available; + void* handle; + char* extensions[2]; +#if !defined(_GLFW_VULKAN_STATIC) + PFN_vkEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; + PFN_vkGetInstanceProcAddr GetInstanceProcAddr; +#endif + GLFWbool KHR_surface; +#if defined(_GLFW_WIN32) + GLFWbool KHR_win32_surface; +#elif defined(_GLFW_X11) + GLFWbool KHR_xlib_surface; + GLFWbool KHR_xcb_surface; +#elif defined(_GLFW_WAYLAND) + GLFWbool KHR_wayland_surface; +#elif defined(_GLFW_MIR) + GLFWbool KHR_mir_surface; +#endif + } vk; + + struct { + GLFWmonitorfun monitor; + GLFWjoystickfun joystick; + } callbacks; + + // This is defined in the window API's platform.h + _GLFW_PLATFORM_LIBRARY_WINDOW_STATE; + // This is defined in the context API's context.h + _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE; + // This is defined in the platform's time.h + _GLFW_PLATFORM_LIBRARY_TIME_STATE; + // This is defined in the platform's joystick.h + _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE; + // This is defined in the platform's tls.h + _GLFW_PLATFORM_LIBRARY_TLS_STATE; + // This is defined in egl_context.h + _GLFW_EGL_LIBRARY_CONTEXT_STATE; +}; + + +//======================================================================== +// Global state shared between compilation units of GLFW +//======================================================================== + +/*! @brief Flag indicating whether GLFW has been successfully initialized. + */ +extern GLFWbool _glfwInitialized; + +/*! @brief All global data protected by @ref _glfwInitialized. + * This should only be touched after a call to @ref glfwInit that has not been + * followed by a call to @ref glfwTerminate. + */ +extern _GLFWlibrary _glfw; + + +//======================================================================== +// Platform API functions +//======================================================================== + +/*! @brief Initializes the platform-specific part of the library. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an error occurred. + * @ingroup platform + */ +int _glfwPlatformInit(void); + +/*! @brief Terminates the platform-specific part of the library. + * @ingroup platform + */ +void _glfwPlatformTerminate(void); + +/*! @copydoc glfwGetVersionString + * @ingroup platform + * + * @note The returned string must be available for the duration of the program. + * + * @note The returned string must not change for the duration of the program. + */ +const char* _glfwPlatformGetVersionString(void); + +/*! @copydoc glfwGetCursorPos + * @ingroup platform + */ +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); + +/*! @copydoc glfwSetCursorPos + * @ingroup platform + */ +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); + +/*! @brief Sets the specified cursor mode of the specified window. + * @param[in] window The window whose cursor mode to set. + * @ingroup platform + */ +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); + +/*! @copydoc glfwGetKeyName + * @ingroup platform + */ +const char* _glfwPlatformGetKeyName(int key, int scancode); + +/*! @copydoc glfwGetKeyScancode + * @ingroup platform + */ +int _glfwPlatformGetKeyScancode(int key); + +/*! @copydoc glfwGetMonitors + * @ingroup platform + */ +_GLFWmonitor** _glfwPlatformGetMonitors(int* count); + +/*! @brief Checks whether two monitor objects represent the same monitor. + * + * @param[in] first The first monitor. + * @param[in] second The second monitor. + * @return @c GLFW_TRUE if the monitor objects represent the same monitor, or + * @c GLFW_FALSE otherwise. + * @ingroup platform + */ +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second); + +/*! @copydoc glfwGetMonitorPos + * @ingroup platform + */ +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos); + +/*! @copydoc glfwGetVideoModes + * @ingroup platform + */ +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count); + +/*! @ingroup platform + */ +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode); + +/*! @copydoc glfwGetGammaRamp + * @ingroup platform + */ +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp); + +/*! @copydoc glfwSetGammaRamp + * @ingroup platform + */ +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp); + +/*! @copydoc glfwSetClipboardString + * @ingroup platform + */ +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string); + +/*! @copydoc glfwGetClipboardString + * @ingroup platform + * + * @note The returned string must be valid until the next call to @ref + * _glfwPlatformGetClipboardString or @ref _glfwPlatformSetClipboardString. + */ +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window); + +/*! @copydoc glfwJoystickPresent + * @ingroup platform + */ +int _glfwPlatformJoystickPresent(int jid); + +/*! @copydoc glfwGetJoystickAxes + * @ingroup platform + */ +const float* _glfwPlatformGetJoystickAxes(int jid, int* count); + +/*! @copydoc glfwGetJoystickButtons + * @ingroup platform + */ +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count); + +/*! @copydoc glfwGetJoystickName + * @ingroup platform + */ +const char* _glfwPlatformGetJoystickName(int jid); + +/*! @copydoc glfwGetTimerValue + * @ingroup platform + */ +uint64_t _glfwPlatformGetTimerValue(void); + +/*! @copydoc glfwGetTimerFrequency + * @ingroup platform + */ +uint64_t _glfwPlatformGetTimerFrequency(void); + +/*! @ingroup platform + */ +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + +/*! @ingroup platform + */ +void _glfwPlatformDestroyWindow(_GLFWwindow* window); + +/*! @copydoc glfwSetWindowTitle + * @ingroup platform + */ +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title); + +/*! @copydoc glfwSetWindowIcon + * @ingroup platform + */ +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, int count, const GLFWimage* images); + +/*! @copydoc glfwGetWindowPos + * @ingroup platform + */ +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos); + +/*! @copydoc glfwSetWindowPos + * @ingroup platform + */ +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos); + +/*! @copydoc glfwGetWindowSize + * @ingroup platform + */ +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height); + +/*! @copydoc glfwSetWindowSize + * @ingroup platform + */ +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height); + +/*! @copydoc glfwSetWindowSizeLimits + * @ingroup platform + */ +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @copydoc glfwSetWindowAspectRatio + * @ingroup platform + */ +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom); + +/*! @copydoc glfwGetFramebufferSize + * @ingroup platform + */ +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height); + +/*! @copydoc glfwGetWindowFrameSize + * @ingroup platform + */ +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, int* left, int* top, int* right, int* bottom); + +/*! @copydoc glfwIconifyWindow + * @ingroup platform + */ +void _glfwPlatformIconifyWindow(_GLFWwindow* window); + +/*! @copydoc glfwRestoreWindow + * @ingroup platform + */ +void _glfwPlatformRestoreWindow(_GLFWwindow* window); + +/*! @copydoc glfwMaximizeWindow + * @ingroup platform + */ +void _glfwPlatformMaximizeWindow(_GLFWwindow* window); + +/*! @copydoc glfwShowWindow + * @ingroup platform + */ +void _glfwPlatformShowWindow(_GLFWwindow* window); + +/*! @copydoc glfwHideWindow + * @ingroup platform + */ +void _glfwPlatformHideWindow(_GLFWwindow* window); + +/*! @copydoc glfwFocusWindow + * @ingroup platform + */ +void _glfwPlatformFocusWindow(_GLFWwindow* window); + +/*! @copydoc glfwSetWindowMonitor + * @ingroup platform + */ +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, _GLFWmonitor* monitor, int xpos, int ypos, int width, int height, int refreshRate); + +/*! @brief Returns whether the window is focused. + * @ingroup platform + */ +int _glfwPlatformWindowFocused(_GLFWwindow* window); + +/*! @brief Returns whether the window is iconified. + * @ingroup platform + */ +int _glfwPlatformWindowIconified(_GLFWwindow* window); + +/*! @brief Returns whether the window is visible. + * @ingroup platform + */ +int _glfwPlatformWindowVisible(_GLFWwindow* window); + +/*! @brief Returns whether the window is maximized. + * @ingroup platform + */ +int _glfwPlatformWindowMaximized(_GLFWwindow* window); + +/*! @copydoc glfwPollEvents + * @ingroup platform + */ +void _glfwPlatformPollEvents(void); + +/*! @copydoc glfwWaitEvents + * @ingroup platform + */ +void _glfwPlatformWaitEvents(void); + +/*! @copydoc glfwWaitEventsTimeout + * @ingroup platform + */ +void _glfwPlatformWaitEventsTimeout(double timeout); + +/*! @copydoc glfwPostEmptyEvent + * @ingroup platform + */ +void _glfwPlatformPostEmptyEvent(void); + +/*! @ingroup platform + */ +void _glfwPlatformSetCurrentContext(_GLFWwindow* context); + +/*! @copydoc glfwGetCurrentContext + * @ingroup platform + */ +_GLFWwindow* _glfwPlatformGetCurrentContext(void); + +/*! @copydoc glfwCreateCursor + * @ingroup platform + */ +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot); + +/*! @copydoc glfwCreateStandardCursor + * @ingroup platform + */ +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape); + +/*! @copydoc glfwDestroyCursor + * @ingroup platform + */ +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor); + +/*! @copydoc glfwSetCursor + * @ingroup platform + */ +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor); + +/*! @ingroup platform + */ +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions); + +/*! @ingroup platform + */ +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @ingroup platform + */ +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, _GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + + +//======================================================================== +// Event API functions +//======================================================================== + +/*! @brief Notifies shared code of a window focus event. + * @param[in] window The window that received the event. + * @param[in] focused `GLFW_TRUE` if the window received focus, or `GLFW_FALSE` + * if it lost focus. + * @ingroup event + */ +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused); + +/*! @brief Notifies shared code of a window movement event. + * @param[in] window The window that received the event. + * @param[in] xpos The new x-coordinate of the client area of the window. + * @param[in] ypos The new y-coordinate of the client area of the window. + * @ingroup event + */ +void _glfwInputWindowPos(_GLFWwindow* window, int xpos, int ypos); + +/*! @brief Notifies shared code of a window resize event. + * @param[in] window The window that received the event. + * @param[in] width The new width of the client area of the window. + * @param[in] height The new height of the client area of the window. + * @ingroup event + */ +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height); + +/*! @brief Notifies shared code of a framebuffer resize event. + * @param[in] window The window that received the event. + * @param[in] width The new width, in pixels, of the framebuffer. + * @param[in] height The new height, in pixels, of the framebuffer. + * @ingroup event + */ +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height); + +/*! @brief Notifies shared code of a window iconification event. + * @param[in] window The window that received the event. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. + * @ingroup event + */ +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified); + +/*! @brief Notifies shared code of a window maximization event. + * @param[in] window The window that received the event. + * @param[in] maximized `GLFW_TRUE` if the window was maximized, or + * `GLFW_FALSE` if it was restored. + * @ingroup event + */ +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized); + +/*! @brief Notifies shared code of a window damage event. + * @param[in] window The window that received the event. + */ +void _glfwInputWindowDamage(_GLFWwindow* window); + +/*! @brief Notifies shared code of a window close request event + * @param[in] window The window that received the event. + * @ingroup event + */ +void _glfwInputWindowCloseRequest(_GLFWwindow* window); + +void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor); + +/*! @brief Notifies shared code of a physical key event. + * @param[in] window The window that received the event. + * @param[in] key The key that was pressed or released. + * @param[in] scancode The system-specific scan code of the key. + * @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE. + * @param[in] mods The modifiers pressed when the event was generated. + * @ingroup event + */ +void _glfwInputKey(_GLFWwindow* window, int key, int scancode, int action, int mods); + +/*! @brief Notifies shared code of a Unicode character input event. + * @param[in] window The window that received the event. + * @param[in] codepoint The Unicode code point of the input character. + * @param[in] mods Bit field describing which modifier keys were held down. + * @param[in] plain `GLFW_TRUE` if the character is regular text input, or + * `GLFW_FALSE` otherwise. + * @ingroup event + */ +void _glfwInputChar(_GLFWwindow* window, unsigned int codepoint, int mods, GLFWbool plain); + +/*! @brief Notifies shared code of a scroll event. + * @param[in] window The window that received the event. + * @param[in] xoffset The scroll offset along the x-axis. + * @param[in] yoffset The scroll offset along the y-axis. + * @ingroup event + */ +void _glfwInputScroll(_GLFWwindow* window, double xoffset, double yoffset); + +/*! @brief Notifies shared code of a mouse button click event. + * @param[in] window The window that received the event. + * @param[in] button The button that was pressed or released. + * @param[in] action @ref GLFW_PRESS or @ref GLFW_RELEASE. + * @ingroup event + */ +void _glfwInputMouseClick(_GLFWwindow* window, int button, int action, int mods); + +/*! @brief Notifies shared code of a cursor motion event. + * @param[in] window The window that received the event. + * @param[in] xpos The new x-coordinate of the cursor, relative to the left + * edge of the client area of the window. + * @param[in] ypos The new y-coordinate of the cursor, relative to the top edge + * of the client area of the window. + * @ingroup event + */ +void _glfwInputCursorPos(_GLFWwindow* window, double xpos, double ypos); + +/*! @brief Notifies shared code of a cursor enter/leave event. + * @param[in] window The window that received the event. + * @param[in] entered `GLFW_TRUE` if the cursor entered the client area of the + * window, or `GLFW_FALSE` if it left it. + * @ingroup event + */ +void _glfwInputCursorEnter(_GLFWwindow* window, GLFWbool entered); + +/*! @ingroup event + */ +void _glfwInputMonitorChange(void); + +/*! @ingroup event + */ +void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window); + +/*! @brief Notifies shared code of an error. + * @param[in] error The error code most suitable for the error. + * @param[in] format The `printf` style format string of the error + * description. + * @ingroup event + */ +#if defined(__GNUC__) +void _glfwInputError(int error, const char* format, ...) __attribute__((format(printf, 2, 3))); +#else +void _glfwInputError(int error, const char* format, ...); +#endif + +/*! @brief Notifies dropped object over window. + * @param[in] window The window that received the event. + * @param[in] count The number of dropped objects. + * @param[in] names The names of the dropped objects. + * @ingroup event + */ +void _glfwInputDrop(_GLFWwindow* window, int count, const char** names); + +/*! @brief Notifies shared code of a joystick connection/disconnection event. + * @param[in] jid The joystick that was connected or disconnected. + * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. + * @ingroup event + */ +void _glfwInputJoystickChange(int jid, int event); + + +//======================================================================== +// Utility functions +//======================================================================== + +/*! @ingroup utility + */ +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired); + +/*! @brief Performs lexical comparison between two @ref GLFWvidmode structures. + * @ingroup utility + */ +int _glfwCompareVideoModes(const GLFWvidmode* first, const GLFWvidmode* second); + +/*! @brief Splits a color depth into red, green and blue bit depths. + * @ingroup utility + */ +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue); + +/*! @brief Searches an extension string for the specified extension. + * @param[in] string The extension string to search. + * @param[in] extensions The extension to search for. + * @return `GLFW_TRUE` if the extension was found, or `GLFW_FALSE` otherwise. + * @ingroup utility + */ +GLFWbool _glfwStringInExtensionString(const char* string, const char* extensions); + +/*! @brief Chooses the framebuffer config that best matches the desired one. + * @param[in] desired The desired framebuffer config. + * @param[in] alternatives The framebuffer configs supported by the system. + * @param[in] count The number of entries in the alternatives array. + * @return The framebuffer config most closely matching the desired one, or @c + * NULL if none fulfilled the hard constraints of the desired values. + * @ingroup utility + */ +const _GLFWfbconfig* _glfwChooseFBConfig(const _GLFWfbconfig* desired, + const _GLFWfbconfig* alternatives, + unsigned int count); + +/*! @brief Retrieves the attributes of the current context. + * @param[in] ctxconfig The desired context attributes. + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if the context is + * unusable. + * @ingroup utility + */ +GLFWbool _glfwRefreshContextAttribs(const _GLFWctxconfig* ctxconfig); + +/*! @brief Checks whether the desired context attributes are valid. + * @param[in] ctxconfig The context attributes to check. + * @return `GLFW_TRUE` if the context attributes are valid, or `GLFW_FALSE` + * otherwise. + * @ingroup utility + * + * This function checks things like whether the specified client API version + * exists and whether all relevant options have supported and non-conflicting + * values. + */ +GLFWbool _glfwIsValidContextConfig(const _GLFWctxconfig* ctxconfig); + +/*! @ingroup utility + */ +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size); + +/*! @ingroup utility + */ +void _glfwFreeGammaArrays(GLFWgammaramp* ramp); + +/*! @brief Allocates and returns a monitor object with the specified name + * and dimensions. + * @param[in] name The name of the monitor. + * @param[in] widthMM The width, in mm, of the monitor's display area. + * @param[in] heightMM The height, in mm, of the monitor's display area. + * @return The newly created object. + * @ingroup utility + */ +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM); + +/*! @brief Frees a monitor object and any data associated with it. + * @ingroup utility + */ +void _glfwFreeMonitor(_GLFWmonitor* monitor); + +/*! @ingroup utility + */ +void _glfwFreeMonitors(_GLFWmonitor** monitors, int count); + +/*! @ingroup utility + */ +GLFWbool _glfwIsPrintable(int key); + +/*! @ingroup utility + */ +GLFWbool _glfwInitVulkan(void); + +/*! @ingroup utility + */ +void _glfwTerminateVulkan(void); + +/*! @ingroup utility + */ +const char* _glfwGetVulkanResultString(VkResult result); + +#endif // _glfw3_internal_h_ diff --git a/apps/exampleViewer/common/glfw/src/linux_joystick.c b/apps/exampleViewer/common/glfw/src/linux_joystick.c new file mode 100644 index 0000000000..d5b9a28efd --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/linux_joystick.c @@ -0,0 +1,341 @@ +//======================================================================== +// GLFW 3.3 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#if defined(__linux__) +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif // __linux__ + + +// Attempt to open the specified joystick device +// +#if defined(__linux__) +static GLFWbool openJoystickDevice(const char* path) +{ + char axisCount, buttonCount; + char name[256] = ""; + int jid, fd, version; + _GLFWjoystickLinux* js; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.linux_js.js[jid].present) + continue; + + if (strcmp(_glfw.linux_js.js[jid].path, path) == 0) + return GLFW_FALSE; + } + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.linux_js.js[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return GLFW_FALSE; + + fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd == -1) + return GLFW_FALSE; + + // Verify that the joystick driver version is at least 1.0 + ioctl(fd, JSIOCGVERSION, &version); + if (version < 0x010000) + { + // It's an old 0.x interface (we don't support it) + close(fd); + return GLFW_FALSE; + } + + if (ioctl(fd, JSIOCGNAME(sizeof(name)), name) < 0) + strncpy(name, "Unknown", sizeof(name)); + + js = _glfw.linux_js.js + jid; + js->present = GLFW_TRUE; + js->name = strdup(name); + js->path = strdup(path); + js->fd = fd; + + ioctl(fd, JSIOCGAXES, &axisCount); + js->axisCount = (int) axisCount; + js->axes = calloc(axisCount, sizeof(float)); + + ioctl(fd, JSIOCGBUTTONS, &buttonCount); + js->buttonCount = (int) buttonCount; + js->buttons = calloc(buttonCount, 1); + + _glfwInputJoystickChange(jid, GLFW_CONNECTED); + return GLFW_TRUE; +} +#endif // __linux__ + +// Polls for and processes events the specified joystick +// +static GLFWbool pollJoystickEvents(_GLFWjoystickLinux* js) +{ +#if defined(__linux__) + _glfwPollJoystickEvents(); + + if (!js->present) + return GLFW_FALSE; + + // Read all queued events (non-blocking) + for (;;) + { + struct js_event e; + + errno = 0; + if (read(js->fd, &e, sizeof(e)) < 0) + { + // Reset the joystick slot if the device was disconnected + if (errno == ENODEV) + { + free(js->axes); + free(js->buttons); + free(js->name); + free(js->path); + + memset(js, 0, sizeof(_GLFWjoystickLinux)); + + _glfwInputJoystickChange(js - _glfw.linux_js.js, + GLFW_DISCONNECTED); + } + + break; + } + + // Clear the initial-state bit + e.type &= ~JS_EVENT_INIT; + + if (e.type == JS_EVENT_AXIS) + js->axes[e.number] = (float) e.value / 32767.0f; + else if (e.type == JS_EVENT_BUTTON) + js->buttons[e.number] = e.value ? GLFW_PRESS : GLFW_RELEASE; + } +#endif // __linux__ + return js->present; +} + +// Lexically compare joysticks by name; used by qsort +// +#if defined(__linux__) +static int compareJoysticks(const void* fp, const void* sp) +{ + const _GLFWjoystickLinux* fj = fp; + const _GLFWjoystickLinux* sj = sp; + return strcmp(fj->path, sj->path); +} +#endif // __linux__ + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +GLFWbool _glfwInitJoysticksLinux(void) +{ +#if defined(__linux__) + DIR* dir; + int count = 0; + const char* dirname = "/dev/input"; + + _glfw.linux_js.inotify = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (_glfw.linux_js.inotify == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to initialize inotify: %s", + strerror(errno)); + return GLFW_FALSE; + } + + // HACK: Register for IN_ATTRIB as well to get notified when udev is done + // This works well in practice but the true way is libudev + + _glfw.linux_js.watch = inotify_add_watch(_glfw.linux_js.inotify, + dirname, + IN_CREATE | IN_ATTRIB); + if (_glfw.linux_js.watch == -1) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to watch for joystick connections in %s: %s", + dirname, + strerror(errno)); + // Continue without device connection notifications + } + + if (regcomp(&_glfw.linux_js.regex, "^js[0-9]\\+$", 0) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Linux: Failed to compile regex"); + return GLFW_FALSE; + } + + dir = opendir(dirname); + if (dir) + { + struct dirent* entry; + + while ((entry = readdir(dir))) + { + char path[20]; + regmatch_t match; + + if (regexec(&_glfw.linux_js.regex, entry->d_name, 1, &match, 0) != 0) + continue; + + snprintf(path, sizeof(path), "%s/%s", dirname, entry->d_name); + if (openJoystickDevice(path)) + count++; + } + + closedir(dir); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Linux: Failed to open joystick device directory %s: %s", + dirname, + strerror(errno)); + // Continue with no joysticks detected + } + + qsort(_glfw.linux_js.js, count, sizeof(_GLFWjoystickLinux), compareJoysticks); +#endif // __linux__ + + return GLFW_TRUE; +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksLinux(void) +{ +#if defined(__linux__) + int i; + + for (i = 0; i <= GLFW_JOYSTICK_LAST; i++) + { + if (_glfw.linux_js.js[i].present) + { + close(_glfw.linux_js.js[i].fd); + free(_glfw.linux_js.js[i].axes); + free(_glfw.linux_js.js[i].buttons); + free(_glfw.linux_js.js[i].name); + free(_glfw.linux_js.js[i].path); + } + } + + regfree(&_glfw.linux_js.regex); + + if (_glfw.linux_js.inotify > 0) + { + if (_glfw.linux_js.watch > 0) + inotify_rm_watch(_glfw.linux_js.inotify, _glfw.linux_js.watch); + + close(_glfw.linux_js.inotify); + } +#endif // __linux__ +} + +void _glfwPollJoystickEvents(void) +{ +#if defined(__linux__) + ssize_t offset = 0; + char buffer[16384]; + + const ssize_t size = read(_glfw.linux_js.inotify, buffer, sizeof(buffer)); + + while (size > offset) + { + regmatch_t match; + const struct inotify_event* e = (struct inotify_event*) (buffer + offset); + + if (regexec(&_glfw.linux_js.regex, e->name, 1, &match, 0) == 0) + { + char path[20]; + snprintf(path, sizeof(path), "/dev/input/%s", e->name); + openJoystickDevice(path); + } + + offset += sizeof(struct inotify_event) + e->len; + } +#endif +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformJoystickPresent(int jid) +{ + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; + return pollJoystickEvents(js); +} + +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) +{ + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; + if (!pollJoystickEvents(js)) + return NULL; + + *count = js->axisCount; + return js->axes; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) +{ + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; + if (!pollJoystickEvents(js)) + return NULL; + + *count = js->buttonCount; + return js->buttons; +} + +const char* _glfwPlatformGetJoystickName(int jid) +{ + _GLFWjoystickLinux* js = _glfw.linux_js.js + jid; + if (!pollJoystickEvents(js)) + return NULL; + + return js->name; +} + diff --git a/apps/exampleViewer/common/glfw/src/linux_joystick.h b/apps/exampleViewer/common/glfw/src/linux_joystick.h new file mode 100644 index 0000000000..4187b13704 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/linux_joystick.h @@ -0,0 +1,68 @@ +//======================================================================== +// GLFW 3.3 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_linux_joystick_h_ +#define _glfw3_linux_joystick_h_ + +#include + +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE _GLFWjoylistLinux linux_js + + +// Linux-specific joystick data +// +typedef struct _GLFWjoystickLinux +{ + GLFWbool present; + int fd; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + char* name; + char* path; +} _GLFWjoystickLinux; + +// Linux-specific joystick API data +// +typedef struct _GLFWjoylistLinux +{ + _GLFWjoystickLinux js[GLFW_JOYSTICK_LAST + 1]; + +#if defined(__linux__) + int inotify; + int watch; + regex_t regex; +#endif /*__linux__*/ +} _GLFWjoylistLinux; + + +GLFWbool _glfwInitJoysticksLinux(void); +void _glfwTerminateJoysticksLinux(void); + +void _glfwPollJoystickEvents(void); + +#endif // _glfw3_linux_joystick_h_ diff --git a/apps/exampleViewer/common/glfw/src/mir_init.c b/apps/exampleViewer/common/glfw/src/mir_init.c new file mode 100644 index 0000000000..0d71f07ed6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/mir_init.c @@ -0,0 +1,248 @@ +//======================================================================== +// GLFW 3.3 Mir - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014-2015 Brandon Schaefer +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.mir.keycodes, -1, sizeof(_glfw.mir.keycodes)); + memset(_glfw.mir.scancodes, -1, sizeof(_glfw.mir.scancodes)); + + _glfw.mir.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.mir.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.mir.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.mir.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.mir.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.mir.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.mir.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.mir.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.mir.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.mir.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.mir.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.mir.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.mir.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.mir.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.mir.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.mir.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.mir.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.mir.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.mir.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.mir.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.mir.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.mir.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.mir.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.mir.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.mir.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.mir.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.mir.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.mir.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.mir.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.mir.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.mir.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.mir.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.mir.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.mir.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.mir.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.mir.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.mir.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.mir.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.mir.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.mir.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.mir.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.mir.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.mir.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.mir.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.mir.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.mir.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.mir.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.mir.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.mir.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.mir.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.mir.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.mir.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.mir.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.mir.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.mir.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.mir.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.mir.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.mir.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.mir.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.mir.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.mir.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.mir.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.mir.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.mir.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.mir.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.mir.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.mir.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.mir.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.mir.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.mir.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.mir.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.mir.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.mir.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.mir.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.mir.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.mir.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.mir.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.mir.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.mir.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.mir.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.mir.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.mir.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.mir.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.mir.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.mir.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.mir.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.mir.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.mir.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.mir.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.mir.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.mir.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.mir.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.mir.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.mir.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.mir.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.mir.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.mir.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.mir.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.mir.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.mir.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.mir.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.mir.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.mir.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.mir.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.mir.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.mir.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.mir.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.mir.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.mir.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.mir.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.mir.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.mir.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.mir.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.mir.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.mir.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.mir.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.mir.keycodes[scancode] > 0) + _glfw.mir.scancodes[_glfw.mir.keycodes[scancode]] = scancode; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + int error; + + _glfw.mir.connection = mir_connect_sync(NULL, __PRETTY_FUNCTION__); + + if (!mir_connection_is_valid(_glfw.mir.connection)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unable to connect to server: %s", + mir_connection_get_error_message(_glfw.mir.connection)); + + return GLFW_FALSE; + } + + _glfw.mir.display = + mir_connection_get_egl_native_display(_glfw.mir.connection); + + createKeyTables(); + + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; + + if (!_glfwInitJoysticksLinux()) + return GLFW_FALSE; + + _glfwInitTimerPOSIX(); + + // Need the default conf for when we set a NULL cursor + _glfw.mir.defaultConf = mir_cursor_configuration_from_name(mir_default_cursor_name); + _glfw.mir.disabledConf = mir_cursor_configuration_from_name(mir_disabled_cursor_name); + + _glfw.mir.eventQueue = calloc(1, sizeof(EventQueue)); + _glfwInitEventQueueMir(_glfw.mir.eventQueue); + + error = pthread_mutex_init(&_glfw.mir.eventMutex, NULL); + if (error) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Failed to create event mutex: %s", + strerror(error)); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateEGL(); + _glfwTerminateJoysticksLinux(); + _glfwTerminateThreadLocalStoragePOSIX(); + + _glfwDeleteEventQueueMir(_glfw.mir.eventQueue); + + pthread_mutex_destroy(&_glfw.mir.eventMutex); + + mir_connection_release(_glfw.mir.connection); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Mir EGL" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif +#if defined(__linux__) + " /dev/js" +#endif +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} + diff --git a/apps/exampleViewer/common/glfw/src/mir_monitor.c b/apps/exampleViewer/common/glfw/src/mir_monitor.c new file mode 100644 index 0000000000..0cb4438a15 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/mir_monitor.c @@ -0,0 +1,182 @@ +//======================================================================== +// GLFW 3.3 Mir - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014-2015 Brandon Schaefer +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + int i, found = 0; + _GLFWmonitor** monitors = NULL; + MirDisplayConfiguration* displayConfig = + mir_connection_create_display_config(_glfw.mir.connection); + + *count = 0; + + for (i = 0; i < displayConfig->num_outputs; i++) + { + const MirDisplayOutput* out = displayConfig->outputs + i; + + if (out->used && + out->connected && + out->num_modes && + out->current_mode < out->num_modes) + { + found++; + monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); + monitors[i] = _glfwAllocMonitor("Unknown", + out->physical_width_mm, + out->physical_height_mm); + + monitors[i]->mir.x = out->position_x; + monitors[i]->mir.y = out->position_y; + monitors[i]->mir.outputId = out->output_id; + monitors[i]->mir.curMode = out->current_mode; + + monitors[i]->modes = _glfwPlatformGetVideoModes(monitors[i], + &monitors[i]->modeCount); + } + } + + mir_display_config_destroy(displayConfig); + + *count = found; + return monitors; +} + +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + return first->mir.outputId == second->mir.outputId; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (xpos) + *xpos = monitor->mir.x; + if (ypos) + *ypos = monitor->mir.y; +} + +void FillInRGBBitsFromPixelFormat(GLFWvidmode* mode, const MirPixelFormat pf) +{ + switch (pf) + { + case mir_pixel_format_rgb_565: + mode->redBits = 5; + mode->greenBits = 6; + mode->blueBits = 5; + break; + case mir_pixel_format_rgba_5551: + mode->redBits = 5; + mode->greenBits = 5; + mode->blueBits = 5; + break; + case mir_pixel_format_rgba_4444: + mode->redBits = 4; + mode->greenBits = 4; + mode->blueBits = 4; + break; + case mir_pixel_format_abgr_8888: + case mir_pixel_format_xbgr_8888: + case mir_pixel_format_argb_8888: + case mir_pixel_format_xrgb_8888: + case mir_pixel_format_bgr_888: + case mir_pixel_format_rgb_888: + default: + mode->redBits = 8; + mode->greenBits = 8; + mode->blueBits = 8; + break; + } +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + int i; + GLFWvidmode* modes = NULL; + MirDisplayConfiguration* displayConfig = + mir_connection_create_display_config(_glfw.mir.connection); + + for (i = 0; i < displayConfig->num_outputs; i++) + { + const MirDisplayOutput* out = displayConfig->outputs + i; + if (out->output_id != monitor->mir.outputId) + continue; + + modes = calloc(out->num_modes, sizeof(GLFWvidmode)); + + for (*found = 0; *found < out->num_modes; (*found)++) + { + modes[*found].width = out->modes[*found].horizontal_resolution; + modes[*found].height = out->modes[*found].vertical_resolution; + modes[*found].refreshRate = out->modes[*found].refresh_rate; + + FillInRGBBitsFromPixelFormat(&modes[*found], out->output_formats[*found]); + } + + break; + } + + mir_display_config_destroy(displayConfig); + + return modes; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + *mode = monitor->modes[monitor->mir.curMode]; +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwGetMirMonitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return monitor->mir.outputId; +} + diff --git a/apps/exampleViewer/common/glfw/src/mir_platform.h b/apps/exampleViewer/common/glfw/src/mir_platform.h new file mode 100644 index 0000000000..39add6eeb6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/mir_platform.h @@ -0,0 +1,136 @@ +//======================================================================== +// GLFW 3.3 Mir - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014-2015 Brandon Schaefer +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_mir_platform_h_ +#define _glfw3_mir_platform_h_ + +#include +#include +#include + +#include + +typedef VkFlags VkMirSurfaceCreateFlagsKHR; + +typedef struct VkMirSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkMirSurfaceCreateFlagsKHR flags; + MirConnection* connection; + MirSurface* mirSurface; +} VkMirSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateMirSurfaceKHR)(VkInstance,const VkMirSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceMirPresentationSupportKHR)(VkPhysicalDevice,uint32_t,MirConnection*); + +#include "posix_tls.h" +#include "posix_time.h" +#include "linux_joystick.h" +#include "xkb_unicode.h" +#include "egl_context.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->mir.window) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.mir.display) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowMir mir +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorMir mir +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryMir mir +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorMir mir + +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE + + +// Mir-specific Event Queue +// +typedef struct EventQueue +{ + TAILQ_HEAD(, EventNode) head; +} EventQueue; + +// Mir-specific per-window data +// +typedef struct _GLFWwindowMir +{ + MirSurface* surface; + int width; + int height; + MirEGLNativeWindowType window; + _GLFWcursor* currentCursor; + +} _GLFWwindowMir; + +// Mir-specific per-monitor data +// +typedef struct _GLFWmonitorMir +{ + int curMode; + int outputId; + int x; + int y; + +} _GLFWmonitorMir; + +// Mir-specific global data +// +typedef struct _GLFWlibraryMir +{ + MirConnection* connection; + MirEGLNativeDisplayType display; + MirCursorConfiguration* defaultConf; + MirCursorConfiguration* disabledConf; + EventQueue* eventQueue; + + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; + + pthread_mutex_t eventMutex; + pthread_cond_t eventCond; + + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + +} _GLFWlibraryMir; + +// Mir-specific per-cursor data +// TODO: Only system cursors are implemented in Mir atm. Need to wait for support. +// +typedef struct _GLFWcursorMir +{ + MirCursorConfiguration* conf; + MirBufferStream* customCursor; +} _GLFWcursorMir; + + +extern void _glfwInitEventQueueMir(EventQueue* queue); +extern void _glfwDeleteEventQueueMir(EventQueue* queue); + +#endif // _glfw3_mir_platform_h_ diff --git a/apps/exampleViewer/common/glfw/src/mir_window.c b/apps/exampleViewer/common/glfw/src/mir_window.c new file mode 100644 index 0000000000..7d2c276ade --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/mir_window.c @@ -0,0 +1,910 @@ +//======================================================================== +// GLFW 3.3 Mir - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014-2015 Brandon Schaefer +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + + +typedef struct EventNode +{ + TAILQ_ENTRY(EventNode) entries; + const MirEvent* event; + _GLFWwindow* window; +} EventNode; + +static void deleteNode(EventQueue* queue, EventNode* node) +{ + mir_event_unref(node->event); + free(node); +} + +static GLFWbool emptyEventQueue(EventQueue* queue) +{ + return queue->head.tqh_first == NULL; +} + +// TODO The mir_event_ref is not supposed to be used but ... its needed +// in this case. Need to wait until we can read from an FD set up by mir +// for single threaded event handling. +static EventNode* newEventNode(const MirEvent* event, _GLFWwindow* context) +{ + EventNode* newNode = calloc(1, sizeof(EventNode)); + newNode->event = mir_event_ref(event); + newNode->window = context; + + return newNode; +} + +static void enqueueEvent(const MirEvent* event, _GLFWwindow* context) +{ + pthread_mutex_lock(&_glfw.mir.eventMutex); + + EventNode* newNode = newEventNode(event, context); + TAILQ_INSERT_TAIL(&_glfw.mir.eventQueue->head, newNode, entries); + + pthread_cond_signal(&_glfw.mir.eventCond); + + pthread_mutex_unlock(&_glfw.mir.eventMutex); +} + +static EventNode* dequeueEvent(EventQueue* queue) +{ + EventNode* node = NULL; + + pthread_mutex_lock(&_glfw.mir.eventMutex); + + node = queue->head.tqh_first; + + if (node) + TAILQ_REMOVE(&queue->head, node, entries); + + pthread_mutex_unlock(&_glfw.mir.eventMutex); + + return node; +} + +/* FIXME Soon to be changed upstream mir! So we can use an egl config to figure out + the best pixel format! +*/ +static MirPixelFormat findValidPixelFormat(void) +{ + unsigned int i, validFormats, mirPixelFormats = 32; + MirPixelFormat formats[mir_pixel_formats]; + + mir_connection_get_available_surface_formats(_glfw.mir.connection, formats, + mirPixelFormats, &validFormats); + + for (i = 0; i < validFormats; i++) + { + if (formats[i] == mir_pixel_format_abgr_8888 || + formats[i] == mir_pixel_format_xbgr_8888 || + formats[i] == mir_pixel_format_argb_8888 || + formats[i] == mir_pixel_format_xrgb_8888) + { + return formats[i]; + } + } + + return mir_pixel_format_invalid; +} + +static int mirModToGLFWMod(uint32_t mods) +{ + int publicMods = 0x0; + + if (mods & mir_input_event_modifier_alt) + publicMods |= GLFW_MOD_ALT; + else if (mods & mir_input_event_modifier_shift) + publicMods |= GLFW_MOD_SHIFT; + else if (mods & mir_input_event_modifier_ctrl) + publicMods |= GLFW_MOD_CONTROL; + else if (mods & mir_input_event_modifier_meta) + publicMods |= GLFW_MOD_SUPER; + + return publicMods; +} + +static int toGLFWKeyCode(uint32_t key) +{ + if (key < sizeof(_glfw.mir.keycodes) / sizeof(_glfw.mir.keycodes[0])) + return _glfw.mir.keycodes[key]; + + return GLFW_KEY_UNKNOWN; +} + +static void handleKeyEvent(const MirKeyboardEvent* key_event, _GLFWwindow* window) +{ + const int action = mir_keyboard_event_action (key_event); + const int scan_code = mir_keyboard_event_scan_code(key_event); + const int key_code = mir_keyboard_event_key_code (key_event); + const int modifiers = mir_keyboard_event_modifiers(key_event); + + const int pressed = action == mir_keyboard_action_up ? GLFW_RELEASE : GLFW_PRESS; + const int mods = mirModToGLFWMod(modifiers); + const long text = _glfwKeySym2Unicode(key_code); + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + + _glfwInputKey(window, toGLFWKeyCode(scan_code), scan_code, pressed, mods); + + if (text != -1) + _glfwInputChar(window, text, mods, plain); +} + +static void handlePointerButton(_GLFWwindow* window, + int pressed, + const MirPointerEvent* pointer_event) +{ + int mods = mir_pointer_event_modifiers(pointer_event); + const int publicMods = mirModToGLFWMod(mods); + MirPointerButton button = mir_pointer_button_primary; + static uint32_t oldButtonStates = 0; + uint32_t newButtonStates = mir_pointer_event_buttons(pointer_event); + int publicButton = GLFW_MOUSE_BUTTON_LEFT; + + // XOR our old button states our new states to figure out what was added or removed + button = newButtonStates ^ oldButtonStates; + + switch (button) + { + case mir_pointer_button_primary: + publicButton = GLFW_MOUSE_BUTTON_LEFT; + break; + case mir_pointer_button_secondary: + publicButton = GLFW_MOUSE_BUTTON_RIGHT; + break; + case mir_pointer_button_tertiary: + publicButton = GLFW_MOUSE_BUTTON_MIDDLE; + break; + case mir_pointer_button_forward: + // FIXME What is the forward button? + publicButton = GLFW_MOUSE_BUTTON_4; + break; + case mir_pointer_button_back: + // FIXME What is the back button? + publicButton = GLFW_MOUSE_BUTTON_5; + break; + default: + break; + } + + oldButtonStates = newButtonStates; + + _glfwInputMouseClick(window, publicButton, pressed, publicMods); +} + +static void handlePointerMotion(_GLFWwindow* window, + const MirPointerEvent* pointer_event) +{ + const int hscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_hscroll); + const int vscroll = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_vscroll); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (_glfw.mir.disabledCursorWindow != window) + return; + + const int dx = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_x); + const int dy = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_relative_y); + const int current_x = window->virtualCursorPosX; + const int current_y = window->virtualCursorPosY; + + _glfwInputCursorPos(window, dx + current_x, dy + current_y); + } + else + { + const int x = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_x); + const int y = mir_pointer_event_axis_value(pointer_event, mir_pointer_axis_y); + + _glfwInputCursorPos(window, x, y); + } + + if (hscroll != 0 || vscroll != 0) + _glfwInputScroll(window, hscroll, vscroll); +} + +static void handlePointerEvent(const MirPointerEvent* pointer_event, + _GLFWwindow* window) +{ + int action = mir_pointer_event_action(pointer_event); + + switch (action) + { + case mir_pointer_action_button_down: + handlePointerButton(window, GLFW_PRESS, pointer_event); + break; + case mir_pointer_action_button_up: + handlePointerButton(window, GLFW_RELEASE, pointer_event); + break; + case mir_pointer_action_motion: + handlePointerMotion(window, pointer_event); + break; + case mir_pointer_action_enter: + case mir_pointer_action_leave: + break; + default: + break; + } +} + +static void handleInput(const MirInputEvent* input_event, _GLFWwindow* window) +{ + int type = mir_input_event_get_type(input_event); + + switch (type) + { + case mir_input_event_type_key: + handleKeyEvent(mir_input_event_get_keyboard_event(input_event), window); + break; + case mir_input_event_type_pointer: + handlePointerEvent(mir_input_event_get_pointer_event(input_event), window); + break; + default: + break; + } +} + +static void handleEvent(const MirEvent* event, _GLFWwindow* window) +{ + int type = mir_event_get_type(event); + + switch (type) + { + case mir_event_type_input: + handleInput(mir_event_get_input_event(event), window); + break; + default: + break; + } +} + +static void addNewEvent(MirSurface* surface, const MirEvent* event, void* context) +{ + enqueueEvent(event, context); +} + +static GLFWbool createSurface(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + MirBufferUsage buffer_usage = mir_buffer_usage_hardware; + MirPixelFormat pixel_format = findValidPixelFormat(); + + if (pixel_format == mir_pixel_format_invalid) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unable to find a correct pixel format"); + return GLFW_FALSE; + } + + spec = mir_connection_create_spec_for_normal_surface(_glfw.mir.connection, + window->mir.width, + window->mir.height, + pixel_format); + + mir_surface_spec_set_buffer_usage(spec, buffer_usage); + mir_surface_spec_set_name(spec, "MirSurface"); + + window->mir.surface = mir_surface_create_sync(spec); + mir_surface_spec_release(spec); + + if (!mir_surface_is_valid(window->mir.surface)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unable to create surface: %s", + mir_surface_get_error_message(window->mir.surface)); + + return GLFW_FALSE; + } + + mir_surface_set_event_handler(window->mir.surface, addNewEvent, window); + + return GLFW_TRUE; +} + +static void setSurfaceConfinement(_GLFWwindow* window, MirPointerConfinementState state) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_pointer_confinement(spec, state); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInitEventQueueMir(EventQueue* queue) +{ + TAILQ_INIT(&queue->head); +} + +void _glfwDeleteEventQueueMir(EventQueue* queue) +{ + if (queue) + { + EventNode* node, *node_next; + node = queue->head.tqh_first; + + while (node != NULL) + { + node_next = node->entries.tqe_next; + + TAILQ_REMOVE(&queue->head, node, entries); + deleteNode(queue, node); + + node = node_next; + } + + free(queue); + } +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (window->monitor) + { + GLFWvidmode mode; + _glfwPlatformGetVideoMode(window->monitor, &mode); + + mir_surface_set_state(window->mir.surface, mir_surface_state_fullscreen); + + if (wndconfig->width > mode.width || wndconfig->height > mode.height) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Requested surface size too large: %ix%i", + wndconfig->width, wndconfig->height); + + return GLFW_FALSE; + } + } + + window->mir.width = wndconfig->width; + window->mir.height = wndconfig->height; + window->mir.currentCursor = NULL; + + if (!createSurface(window)) + return GLFW_FALSE; + + window->mir.window = mir_buffer_stream_get_egl_native_window( + mir_surface_get_buffer_stream(window->mir.surface)); + + if (ctxconfig->client != GLFW_NO_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (_glfw.mir.disabledCursorWindow == window) + _glfw.mir.disabledCursorWindow = NULL; + + if (mir_surface_is_valid(window->mir.surface)) + { + mir_surface_release_sync(window->mir.surface); + window->mir.surface = NULL; + } + + if (window->context.destroy) + window->context.destroy(window); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + MirSurfaceSpec* spec; + const char* e_title = title ? title : ""; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_name(spec, e_title); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_width (spec, width); + mir_surface_spec_set_height(spec, height); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->mir.width; + if (height) + *height = window->mir.height; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_state(spec, mir_surface_state_minimized); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_state(spec, mir_surface_state_restored); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_state(spec, mir_surface_state_maximized); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_state(spec, mir_surface_state_hidden); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + MirSurfaceSpec* spec; + + spec = mir_connection_create_spec_for_changes(_glfw.mir.connection); + mir_surface_spec_set_state(spec, mir_surface_state_restored); + + mir_surface_apply_spec(window->mir.surface, spec); + mir_surface_spec_release(spec); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return mir_surface_get_focus(window->mir.surface) == mir_surface_focused; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return mir_surface_get_visibility(window->mir.surface) == mir_surface_visibility_exposed; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return mir_surface_get_state(window->mir.surface) == mir_surface_state_maximized; +} + +void _glfwPlatformPollEvents(void) +{ + EventNode* node = NULL; + + while ((node = dequeueEvent(_glfw.mir.eventQueue))) + { + handleEvent(node->event, node->window); + deleteNode(_glfw.mir.eventQueue, node); + } +} + +void _glfwPlatformWaitEvents(void) +{ + pthread_mutex_lock(&_glfw.mir.eventMutex); + + if (emptyEventQueue(_glfw.mir.eventQueue)) + pthread_cond_wait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex); + + pthread_mutex_unlock(&_glfw.mir.eventMutex); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + pthread_mutex_lock(&_glfw.mir.eventMutex); + + if (emptyEventQueue(_glfw.mir.eventQueue)) + { + struct timespec time; + clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += (long) timeout; + time.tv_nsec += (long) ((timeout - (long) timeout) * 1e9); + pthread_cond_timedwait(&_glfw.mir.eventCond, &_glfw.mir.eventMutex, &time); + } + + pthread_mutex_unlock(&_glfw.mir.eventMutex); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->mir.width; + if (height) + *height = window->mir.height; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + MirBufferStream* stream; + MirPixelFormat pixel_format = findValidPixelFormat(); + + int i_w = image->width; + int i_h = image->height; + + if (pixel_format == mir_pixel_format_invalid) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unable to find a correct pixel format"); + return GLFW_FALSE; + } + + stream = mir_connection_create_buffer_stream_sync(_glfw.mir.connection, + i_w, i_h, + pixel_format, + mir_buffer_usage_software); + + cursor->mir.conf = mir_cursor_configuration_from_buffer_stream(stream, xhot, yhot); + + char* dest; + unsigned char *pixels; + int i, r_stride, bytes_per_pixel, bytes_per_row; + + MirGraphicsRegion region; + mir_buffer_stream_get_graphics_region(stream, ®ion); + + // FIXME Figure this out based on the current_pf + bytes_per_pixel = 4; + bytes_per_row = bytes_per_pixel * i_w; + + dest = region.vaddr; + pixels = image->pixels; + + r_stride = region.stride; + + for (i = 0; i < i_h; i++) + { + memcpy(dest, pixels, bytes_per_row); + dest += r_stride; + pixels += r_stride; + } + + cursor->mir.customCursor = stream; + + return GLFW_TRUE; +} + +const char* getSystemCursorName(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return mir_arrow_cursor_name; + case GLFW_IBEAM_CURSOR: + return mir_caret_cursor_name; + case GLFW_CROSSHAIR_CURSOR: + return mir_crosshair_cursor_name; + case GLFW_HAND_CURSOR: + return mir_open_hand_cursor_name; + case GLFW_HRESIZE_CURSOR: + return mir_horizontal_resize_cursor_name; + case GLFW_VRESIZE_CURSOR: + return mir_vertical_resize_cursor_name; + } + + return NULL; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + const char* cursor_name = getSystemCursorName(shape); + + if (cursor_name) + { + cursor->mir.conf = mir_cursor_configuration_from_name(cursor_name); + cursor->mir.customCursor = NULL; + + return GLFW_TRUE; + } + + return GLFW_FALSE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->mir.conf) + mir_cursor_configuration_destroy(cursor->mir.conf); + if (cursor->mir.customCursor) + mir_buffer_stream_release_sync(cursor->mir.customCursor); +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (cursor && cursor->mir.conf) + { + window->mir.currentCursor = cursor; + + mir_wait_for(mir_surface_configure_cursor(window->mir.surface, cursor->mir.conf)); + if (cursor->mir.customCursor) + { + mir_buffer_stream_swap_buffers_sync(cursor->mir.customCursor); + } + } + else + { + mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.defaultConf)); + } +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + _glfw.mir.disabledCursorWindow = window; + setSurfaceConfinement(window, mir_pointer_confined_to_surface); + mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.disabledConf)); + } + else + { + // If we were disabled before lets undo that! + if (_glfw.mir.disabledCursorWindow == window) + { + _glfw.mir.disabledCursorWindow = NULL; + setSurfaceConfinement(window, mir_pointer_unconfined); + } + + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + _glfwPlatformSetCursor(window, window->mir.currentCursor); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + mir_wait_for(mir_surface_configure_cursor(window->mir.surface, _glfw.mir.disabledConf)); + } + } +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + return NULL; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.mir.scancodes[key]; +} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Unsupported function %s", __PRETTY_FUNCTION__); + + return NULL; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_mir_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_mir_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + PFN_vkGetPhysicalDeviceMirPresentationSupportKHR vkGetPhysicalDeviceMirPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceMirPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceMirPresentationSupportKHR"); + if (!vkGetPhysicalDeviceMirPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceMirPresentationSupportKHR(device, + queuefamily, + _glfw.mir.connection); +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkMirSurfaceCreateInfoKHR sci; + PFN_vkCreateMirSurfaceKHR vkCreateMirSurfaceKHR; + + vkCreateMirSurfaceKHR = (PFN_vkCreateMirSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateMirSurfaceKHR"); + if (!vkCreateMirSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Mir: Vulkan instance missing VK_KHR_mir_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_MIR_SURFACE_CREATE_INFO_KHR; + sci.connection = _glfw.mir.connection; + sci.mirSurface = window->mir.surface; + + err = vkCreateMirSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Mir: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI MirConnection* glfwGetMirDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.mir.connection; +} + +GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->mir.surface; +} + diff --git a/apps/exampleViewer/common/glfw/src/monitor.c b/apps/exampleViewer/common/glfw/src/monitor.c new file mode 100644 index 0000000000..df32a3c48f --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/monitor.c @@ -0,0 +1,477 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include + + +// Lexically compare video modes, used by qsort +// +static int compareVideoModes(const void* fp, const void* sp) +{ + const GLFWvidmode* fm = fp; + const GLFWvidmode* sm = sp; + const int fbpp = fm->redBits + fm->greenBits + fm->blueBits; + const int sbpp = sm->redBits + sm->greenBits + sm->blueBits; + const int farea = fm->width * fm->height; + const int sarea = sm->width * sm->height; + + // First sort on color bits per pixel + if (fbpp != sbpp) + return fbpp - sbpp; + + // Then sort on screen area + if (farea != sarea) + return farea - sarea; + + // Lastly sort on refresh rate + return fm->refreshRate - sm->refreshRate; +} + +// Retrieves the available modes for the specified monitor +// +static GLFWbool refreshVideoModes(_GLFWmonitor* monitor) +{ + int modeCount; + GLFWvidmode* modes; + + if (monitor->modes) + return GLFW_TRUE; + + modes = _glfwPlatformGetVideoModes(monitor, &modeCount); + if (!modes) + return GLFW_FALSE; + + qsort(modes, modeCount, sizeof(GLFWvidmode), compareVideoModes); + + free(monitor->modes); + monitor->modes = modes; + monitor->modeCount = modeCount; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputMonitorChange(void) +{ + int i, j, monitorCount = _glfw.monitorCount; + _GLFWmonitor** monitors = _glfw.monitors; + + _glfw.monitors = _glfwPlatformGetMonitors(&_glfw.monitorCount); + + // Re-use still connected monitor objects + + for (i = 0; i < _glfw.monitorCount; i++) + { + for (j = 0; j < monitorCount; j++) + { + if (_glfwPlatformIsSameMonitor(_glfw.monitors[i], monitors[j])) + { + _glfwFreeMonitor(_glfw.monitors[i]); + _glfw.monitors[i] = monitors[j]; + break; + } + } + } + + // Find and report disconnected monitors (not in the new list) + + for (i = 0; i < monitorCount; i++) + { + _GLFWwindow* window; + + for (j = 0; j < _glfw.monitorCount; j++) + { + if (monitors[i] == _glfw.monitors[j]) + break; + } + + if (j < _glfw.monitorCount) + continue; + + for (window = _glfw.windowListHead; window; window = window->next) + { + if (window->monitor == monitors[i]) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetWindowMonitor(window, NULL, 0, 0, width, height, 0); + } + } + + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) monitors[i], GLFW_DISCONNECTED); + } + + // Find and report newly connected monitors (not in the old list) + // Re-used monitor objects are then removed from the old list to avoid + // having them destroyed at the end of this function + + for (i = 0; i < _glfw.monitorCount; i++) + { + for (j = 0; j < monitorCount; j++) + { + if (_glfw.monitors[i] == monitors[j]) + { + monitors[j] = NULL; + break; + } + } + + if (j < monitorCount) + continue; + + if (_glfw.callbacks.monitor) + _glfw.callbacks.monitor((GLFWmonitor*) _glfw.monitors[i], GLFW_CONNECTED); + } + + _glfwFreeMonitors(monitors, monitorCount); +} + +void _glfwInputMonitorWindowChange(_GLFWmonitor* monitor, _GLFWwindow* window) +{ + monitor->window = window; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor* _glfwAllocMonitor(const char* name, int widthMM, int heightMM) +{ + _GLFWmonitor* monitor = calloc(1, sizeof(_GLFWmonitor)); + monitor->name = strdup(name); + monitor->widthMM = widthMM; + monitor->heightMM = heightMM; + + return monitor; +} + +void _glfwFreeMonitor(_GLFWmonitor* monitor) +{ + if (monitor == NULL) + return; + + _glfwFreeGammaArrays(&monitor->originalRamp); + _glfwFreeGammaArrays(&monitor->currentRamp); + + free(monitor->modes); + free(monitor->name); + free(monitor); +} + +void _glfwAllocGammaArrays(GLFWgammaramp* ramp, unsigned int size) +{ + ramp->red = calloc(size, sizeof(unsigned short)); + ramp->green = calloc(size, sizeof(unsigned short)); + ramp->blue = calloc(size, sizeof(unsigned short)); + ramp->size = size; +} + +void _glfwFreeGammaArrays(GLFWgammaramp* ramp) +{ + free(ramp->red); + free(ramp->green); + free(ramp->blue); + + memset(ramp, 0, sizeof(GLFWgammaramp)); +} + +void _glfwFreeMonitors(_GLFWmonitor** monitors, int count) +{ + int i; + + for (i = 0; i < count; i++) + _glfwFreeMonitor(monitors[i]); + + free(monitors); +} + +const GLFWvidmode* _glfwChooseVideoMode(_GLFWmonitor* monitor, + const GLFWvidmode* desired) +{ + int i; + unsigned int sizeDiff, leastSizeDiff = UINT_MAX; + unsigned int rateDiff, leastRateDiff = UINT_MAX; + unsigned int colorDiff, leastColorDiff = UINT_MAX; + const GLFWvidmode* current; + const GLFWvidmode* closest = NULL; + + if (!refreshVideoModes(monitor)) + return NULL; + + for (i = 0; i < monitor->modeCount; i++) + { + current = monitor->modes + i; + + colorDiff = 0; + + if (desired->redBits != GLFW_DONT_CARE) + colorDiff += abs(current->redBits - desired->redBits); + if (desired->greenBits != GLFW_DONT_CARE) + colorDiff += abs(current->greenBits - desired->greenBits); + if (desired->blueBits != GLFW_DONT_CARE) + colorDiff += abs(current->blueBits - desired->blueBits); + + sizeDiff = abs((current->width - desired->width) * + (current->width - desired->width) + + (current->height - desired->height) * + (current->height - desired->height)); + + if (desired->refreshRate != GLFW_DONT_CARE) + rateDiff = abs(current->refreshRate - desired->refreshRate); + else + rateDiff = UINT_MAX - current->refreshRate; + + if ((colorDiff < leastColorDiff) || + (colorDiff == leastColorDiff && sizeDiff < leastSizeDiff) || + (colorDiff == leastColorDiff && sizeDiff == leastSizeDiff && rateDiff < leastRateDiff)) + { + closest = current; + leastSizeDiff = sizeDiff; + leastRateDiff = rateDiff; + leastColorDiff = colorDiff; + } + } + + return closest; +} + +int _glfwCompareVideoModes(const GLFWvidmode* fm, const GLFWvidmode* sm) +{ + return compareVideoModes(fm, sm); +} + +void _glfwSplitBPP(int bpp, int* red, int* green, int* blue) +{ + int delta; + + // We assume that by 32 the user really meant 24 + if (bpp == 32) + bpp = 24; + + // Convert "bits per pixel" to red, green & blue sizes + + *red = *green = *blue = bpp / 3; + delta = bpp - (*red * 3); + if (delta >= 1) + *green = *green + 1; + + if (delta == 2) + *red = *red + 1; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWmonitor** glfwGetMonitors(int* count) +{ + assert(count != NULL); + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + *count = _glfw.monitorCount; + return (GLFWmonitor**) _glfw.monitors; +} + +GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfw.monitorCount) + return NULL; + + return (GLFWmonitor*) _glfw.monitors[0]; +} + +GLFWAPI void glfwGetMonitorPos(GLFWmonitor* handle, int* xpos, int* ypos) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformGetMonitorPos(monitor, xpos, ypos); +} + +GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* handle, int* widthMM, int* heightMM) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + if (widthMM) + *widthMM = 0; + if (heightMM) + *heightMM = 0; + + _GLFW_REQUIRE_INIT(); + + if (widthMM) + *widthMM = monitor->widthMM; + if (heightMM) + *heightMM = monitor->heightMM; +} + +GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->name; +} + +GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(_glfw.callbacks.monitor, cbfun); + return cbfun; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* handle, int* count) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(count != NULL); + + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!refreshVideoModes(monitor)) + return NULL; + + *count = monitor->modeCount; + return monitor->modes; +} + +GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwPlatformGetVideoMode(monitor, &monitor->currentMode); + return &monitor->currentMode; +} + +GLFWAPI void glfwSetGamma(GLFWmonitor* handle, float gamma) +{ + int i; + unsigned short values[256]; + GLFWgammaramp ramp; + + _GLFW_REQUIRE_INIT(); + + if (gamma != gamma || gamma <= 0.f || gamma > FLT_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid gamma value %f", gamma); + return; + } + + for (i = 0; i < 256; i++) + { + double value; + + // Calculate intensity + value = i / 255.0; + // Apply gamma curve + value = pow(value, 1.0 / gamma) * 65535.0 + 0.5; + + // Clamp to value range + if (value > 65535.0) + value = 65535.0; + + values[i] = (unsigned short) value; + } + + ramp.red = values; + ramp.green = values; + ramp.blue = values; + ramp.size = 256; + + glfwSetGammaRamp(handle, &ramp); +} + +GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + _glfwFreeGammaArrays(&monitor->currentRamp); + _glfwPlatformGetGammaRamp(monitor, &monitor->currentRamp); + + return &monitor->currentRamp; +} + +GLFWAPI void glfwSetGammaRamp(GLFWmonitor* handle, const GLFWgammaramp* ramp) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + assert(monitor != NULL); + assert(ramp != NULL); + assert(ramp->red != NULL); + assert(ramp->green != NULL); + assert(ramp->blue != NULL); + + if (ramp->size <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid gamma ramp size %i", + ramp->size); + return; + } + + _GLFW_REQUIRE_INIT(); + + if (!monitor->originalRamp.size) + _glfwPlatformGetGammaRamp(monitor, &monitor->originalRamp); + + _glfwPlatformSetGammaRamp(monitor, ramp); +} + diff --git a/apps/exampleViewer/common/glfw/src/nsgl_context.h b/apps/exampleViewer/common/glfw/src/nsgl_context.h new file mode 100644 index 0000000000..62b194c897 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/nsgl_context.h @@ -0,0 +1,60 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_nsgl_context_h_ +#define _glfw3_nsgl_context_h_ + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextNSGL nsgl +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryNSGL nsgl + + +// NSGL-specific per-context data +// +typedef struct _GLFWcontextNSGL +{ + id pixelFormat; + id object; + +} _GLFWcontextNSGL; + +// NSGL-specific global data +// +typedef struct _GLFWlibraryNSGL +{ + // dlopen handle for OpenGL.framework (for glfwGetProcAddress) + CFBundleRef framework; + +} _GLFWlibraryNSGL; + + +GLFWbool _glfwInitNSGL(void); +void _glfwTerminateNSGL(void); +GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); +void _glfwDestroyContextNSGL(_GLFWwindow* window); + +#endif // _glfw3_nsgl_context_h_ diff --git a/apps/exampleViewer/common/glfw/src/nsgl_context.m b/apps/exampleViewer/common/glfw/src/nsgl_context.m new file mode 100644 index 0000000000..eea4e50adf --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/nsgl_context.m @@ -0,0 +1,316 @@ +//======================================================================== +// GLFW 3.3 macOS - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2009-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +static void makeContextCurrentNSGL(_GLFWwindow* window) +{ + if (window) + [window->context.nsgl.object makeCurrentContext]; + else + [NSOpenGLContext clearCurrentContext]; + + _glfwPlatformSetCurrentContext(window); +} + +static void swapBuffersNSGL(_GLFWwindow* window) +{ + // ARP appears to be unnecessary, but this is future-proof + [window->context.nsgl.object flushBuffer]; +} + +static void swapIntervalNSGL(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + GLint sync = interval; + [window->context.nsgl.object setValues:&sync + forParameter:NSOpenGLCPSwapInterval]; +} + +static int extensionSupportedNSGL(const char* extension) +{ + // There are no NSGL extensions + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressNSGL(const char* procname) +{ + CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, + procname, + kCFStringEncodingASCII); + + GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, + symbolName); + + CFRelease(symbolName); + + return symbol; +} + +// Destroy the OpenGL context +// +static void destroyContextNSGL(_GLFWwindow* window) +{ + [window->context.nsgl.pixelFormat release]; + window->context.nsgl.pixelFormat = nil; + + [window->context.nsgl.object release]; + window->context.nsgl.object = nil; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize OpenGL support +// +GLFWbool _glfwInitNSGL(void) +{ + if (_glfw.nsgl.framework) + return GLFW_TRUE; + + _glfw.nsgl.framework = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); + if (_glfw.nsgl.framework == NULL) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "NSGL: Failed to locate OpenGL framework"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Terminate OpenGL support +// +void _glfwTerminateNSGL(void) +{ +} + +// Create the OpenGL context +// +GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + unsigned int attributeCount = 0; + + if (ctxconfig->client == GLFW_OPENGL_ES_API) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "NSGL: OpenGL ES is not available on macOS"); + return GLFW_FALSE; + } + + if (ctxconfig->major == 3 && ctxconfig->minor < 2) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1"); + return GLFW_FALSE; + } + + if (ctxconfig->major > 2) + { + if (!ctxconfig->forward) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: The targeted version of macOS only supports forward-compatible contexts for OpenGL 3.2 and above"); + return GLFW_FALSE; + } + + if (ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: The targeted version of macOS only supports core profile contexts for OpenGL 3.2 and above"); + return GLFW_FALSE; + } + } + + // Context robustness modes (GL_KHR_robustness) are not yet supported on + // macOS but are not a hard constraint, so ignore and continue + + // Context release behaviors (GL_KHR_context_flush_control) are not yet + // supported on macOS but are not a hard constraint, so ignore and continue + +#define ADD_ATTR(x) { attributes[attributeCount++] = x; } +#define ADD_ATTR2(x, y) { ADD_ATTR(x); ADD_ATTR(y); } + + // Arbitrary array size here + NSOpenGLPixelFormatAttribute attributes[40]; + + ADD_ATTR(NSOpenGLPFAAccelerated); + ADD_ATTR(NSOpenGLPFAClosestPolicy); + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 + if (ctxconfig->major >= 4) + { + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); + } + else +#endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ + if (ctxconfig->major >= 3) + { + ADD_ATTR2(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); + } + + if (ctxconfig->major <= 2) + { + if (fbconfig->auxBuffers != GLFW_DONT_CARE) + ADD_ATTR2(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); + + if (fbconfig->accumRedBits != GLFW_DONT_CARE && + fbconfig->accumGreenBits != GLFW_DONT_CARE && + fbconfig->accumBlueBits != GLFW_DONT_CARE && + fbconfig->accumAlphaBits != GLFW_DONT_CARE) + { + const int accumBits = fbconfig->accumRedBits + + fbconfig->accumGreenBits + + fbconfig->accumBlueBits + + fbconfig->accumAlphaBits; + + ADD_ATTR2(NSOpenGLPFAAccumSize, accumBits); + } + } + + if (fbconfig->redBits != GLFW_DONT_CARE && + fbconfig->greenBits != GLFW_DONT_CARE && + fbconfig->blueBits != GLFW_DONT_CARE) + { + int colorBits = fbconfig->redBits + + fbconfig->greenBits + + fbconfig->blueBits; + + // macOS needs non-zero color size, so set reasonable values + if (colorBits == 0) + colorBits = 24; + else if (colorBits < 15) + colorBits = 15; + + ADD_ATTR2(NSOpenGLPFAColorSize, colorBits); + } + + if (fbconfig->alphaBits != GLFW_DONT_CARE) + ADD_ATTR2(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); + + if (fbconfig->depthBits != GLFW_DONT_CARE) + ADD_ATTR2(NSOpenGLPFADepthSize, fbconfig->depthBits); + + if (fbconfig->stencilBits != GLFW_DONT_CARE) + ADD_ATTR2(NSOpenGLPFAStencilSize, fbconfig->stencilBits); + + if (fbconfig->stereo) + { +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Stereo rendering is deprecated"); + return GLFW_FALSE; +#else + ADD_ATTR(NSOpenGLPFAStereo); +#endif + } + + if (fbconfig->doublebuffer) + ADD_ATTR(NSOpenGLPFADoubleBuffer); + + if (fbconfig->samples != GLFW_DONT_CARE) + { + if (fbconfig->samples == 0) + { + ADD_ATTR2(NSOpenGLPFASampleBuffers, 0); + } + else + { + ADD_ATTR2(NSOpenGLPFASampleBuffers, 1); + ADD_ATTR2(NSOpenGLPFASamples, fbconfig->samples); + } + } + + // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB + // framebuffer, so there's no need (and no way) to request it + + ADD_ATTR(0); + +#undef ADD_ATTR +#undef ADD_ATTR2 + + window->context.nsgl.pixelFormat = + [[NSOpenGLPixelFormat alloc] initWithAttributes:attributes]; + if (window->context.nsgl.pixelFormat == nil) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "NSGL: Failed to find a suitable pixel format"); + return GLFW_FALSE; + } + + NSOpenGLContext* share = NULL; + + if (ctxconfig->share) + share = ctxconfig->share->context.nsgl.object; + + window->context.nsgl.object = + [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat + shareContext:share]; + if (window->context.nsgl.object == nil) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "NSGL: Failed to create OpenGL context"); + return GLFW_FALSE; + } + + [window->context.nsgl.object setView:window->ns.view]; + + window->context.makeCurrent = makeContextCurrentNSGL; + window->context.swapBuffers = swapBuffersNSGL; + window->context.swapInterval = swapIntervalNSGL; + window->context.extensionSupported = extensionSupportedNSGL; + window->context.getProcAddress = getProcAddressNSGL; + window->context.destroy = destroyContextNSGL; + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(nil); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.nsgl.object; +} + diff --git a/apps/exampleViewer/common/glfw/src/posix_time.c b/apps/exampleViewer/common/glfw/src/posix_time.c new file mode 100644 index 0000000000..368cbdf315 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/posix_time.c @@ -0,0 +1,85 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerPOSIX(void) +{ +#if defined(CLOCK_MONOTONIC) + struct timespec ts; + + if (clock_gettime(CLOCK_MONOTONIC, &ts) == 0) + { + _glfw.posix_time.monotonic = GLFW_TRUE; + _glfw.posix_time.frequency = 1000000000; + } + else +#endif + { + _glfw.posix_time.monotonic = GLFW_FALSE; + _glfw.posix_time.frequency = 1000000; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ +#if defined(CLOCK_MONOTONIC) + if (_glfw.posix_time.monotonic) + { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t) ts.tv_sec * (uint64_t) 1000000000 + (uint64_t) ts.tv_nsec; + } + else +#endif + { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t) tv.tv_sec * (uint64_t) 1000000 + (uint64_t) tv.tv_usec; + } +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.posix_time.frequency; +} + diff --git a/apps/exampleViewer/common/glfw/src/posix_time.h b/apps/exampleViewer/common/glfw/src/posix_time.h new file mode 100644 index 0000000000..4a23bfa3e0 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/posix_time.h @@ -0,0 +1,48 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_posix_time_h_ +#define _glfw3_posix_time_h_ + +#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimePOSIX posix_time + +#include + + +// POSIX-specific global timer data +// +typedef struct _GLFWtimePOSIX +{ + GLFWbool monotonic; + uint64_t frequency; + +} _GLFWtimePOSIX; + + +void _glfwInitTimerPOSIX(void); + +#endif // _glfw3_posix_time_h_ diff --git a/apps/exampleViewer/common/glfw/src/posix_tls.c b/apps/exampleViewer/common/glfw/src/posix_tls.c new file mode 100644 index 0000000000..3edade55ef --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/posix_tls.c @@ -0,0 +1,68 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitThreadLocalStoragePOSIX(void) +{ + if (pthread_key_create(&_glfw.posix_tls.context, NULL) != 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "POSIX: Failed to create context TLS"); + return GLFW_FALSE; + } + + _glfw.posix_tls.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwTerminateThreadLocalStoragePOSIX(void) +{ + if (_glfw.posix_tls.allocated) + pthread_key_delete(_glfw.posix_tls.context); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +{ + pthread_setspecific(_glfw.posix_tls.context, context); +} + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return pthread_getspecific(_glfw.posix_tls.context); +} + diff --git a/apps/exampleViewer/common/glfw/src/posix_tls.h b/apps/exampleViewer/common/glfw/src/posix_tls.h new file mode 100644 index 0000000000..f79ba987f6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/posix_tls.h @@ -0,0 +1,49 @@ +//======================================================================== +// GLFW 3.3 POSIX - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_posix_tls_h_ +#define _glfw3_posix_tls_h_ + +#include + +#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsPOSIX posix_tls + + +// POSIX-specific global TLS data +// +typedef struct _GLFWtlsPOSIX +{ + GLFWbool allocated; + pthread_key_t context; + +} _GLFWtlsPOSIX; + + +GLFWbool _glfwInitThreadLocalStoragePOSIX(void); +void _glfwTerminateThreadLocalStoragePOSIX(void); + +#endif // _glfw3_posix_tls_h_ diff --git a/apps/exampleViewer/common/glfw/src/vulkan.c b/apps/exampleViewer/common/glfw/src/vulkan.c new file mode 100644 index 0000000000..30f4b87414 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/vulkan.c @@ -0,0 +1,297 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitVulkan(void) +{ + VkResult err; + VkExtensionProperties* ep; + uint32_t i, count; + +#if !defined(_GLFW_VULKAN_STATIC) +#if defined(_GLFW_WIN32) + const char* name = "vulkan-1.dll"; +#else + const char* name = "libvulkan.so.1"; +#endif + + if (_glfw.vk.available) + return GLFW_TRUE; + + _glfw.vk.handle = _glfw_dlopen(name); + if (!_glfw.vk.handle) + return GLFW_FALSE; + + _glfw.vk.GetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) + _glfw_dlsym(_glfw.vk.handle, "vkGetInstanceProcAddr"); + if (!_glfw.vk.GetInstanceProcAddr) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Loader does not export vkGetInstanceProcAddr"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + _glfw.vk.EnumerateInstanceExtensionProperties = (PFN_vkEnumerateInstanceExtensionProperties) + vkGetInstanceProcAddr(NULL, "vkEnumerateInstanceExtensionProperties"); + if (!_glfw.vk.EnumerateInstanceExtensionProperties) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Failed to retrieve vkEnumerateInstanceExtensionProperties"); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } +#endif // _GLFW_VULKAN_STATIC + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, NULL); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Vulkan: Failed to query instance extension count: %s", + _glfwGetVulkanResultString(err)); + + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + ep = calloc(count, sizeof(VkExtensionProperties)); + + err = vkEnumerateInstanceExtensionProperties(NULL, &count, ep); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Vulkan: Failed to query instance extensions: %s", + _glfwGetVulkanResultString(err)); + + free(ep); + _glfwTerminateVulkan(); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + if (strcmp(ep[i].extensionName, "VK_KHR_surface") == 0) + _glfw.vk.KHR_surface = GLFW_TRUE; +#if defined(_GLFW_WIN32) + else if (strcmp(ep[i].extensionName, "VK_KHR_win32_surface") == 0) + _glfw.vk.KHR_win32_surface = GLFW_TRUE; +#elif defined(_GLFW_X11) + else if (strcmp(ep[i].extensionName, "VK_KHR_xlib_surface") == 0) + _glfw.vk.KHR_xlib_surface = GLFW_TRUE; + else if (strcmp(ep[i].extensionName, "VK_KHR_xcb_surface") == 0) + _glfw.vk.KHR_xcb_surface = GLFW_TRUE; +#elif defined(_GLFW_WAYLAND) + else if (strcmp(ep[i].extensionName, "VK_KHR_wayland_surface") == 0) + _glfw.vk.KHR_wayland_surface = GLFW_TRUE; +#elif defined(_GLFW_MIR) + else if (strcmp(ep[i].extensionName, "VK_KHR_mir_surface") == 0) + _glfw.vk.KHR_mir_surface = GLFW_TRUE; +#endif + } + + free(ep); + + _glfw.vk.available = GLFW_TRUE; + + _glfwPlatformGetRequiredInstanceExtensions(_glfw.vk.extensions); + + return GLFW_TRUE; +} + +void _glfwTerminateVulkan(void) +{ + if (_glfw.vk.handle) + _glfw_dlclose(_glfw.vk.handle); +} + +const char* _glfwGetVulkanResultString(VkResult result) +{ + switch (result) + { + case VK_SUCCESS: + return "Success"; + case VK_NOT_READY: + return "A fence or query has not yet completed"; + case VK_TIMEOUT: + return "A wait operation has not completed in the specified time"; + case VK_EVENT_SET: + return "An event is signaled"; + case VK_EVENT_RESET: + return "An event is unsignaled"; + case VK_INCOMPLETE: + return "A return array was too small for the result"; + case VK_ERROR_OUT_OF_HOST_MEMORY: + return "A host memory allocation has failed"; + case VK_ERROR_OUT_OF_DEVICE_MEMORY: + return "A device memory allocation has failed"; + case VK_ERROR_INITIALIZATION_FAILED: + return "Initialization of an object could not be completed for implementation-specific reasons"; + case VK_ERROR_DEVICE_LOST: + return "The logical or physical device has been lost"; + case VK_ERROR_MEMORY_MAP_FAILED: + return "Mapping of a memory object has failed"; + case VK_ERROR_LAYER_NOT_PRESENT: + return "A requested layer is not present or could not be loaded"; + case VK_ERROR_EXTENSION_NOT_PRESENT: + return "A requested extension is not supported"; + case VK_ERROR_FEATURE_NOT_PRESENT: + return "A requested feature is not supported"; + case VK_ERROR_INCOMPATIBLE_DRIVER: + return "The requested version of Vulkan is not supported by the driver or is otherwise incompatible"; + case VK_ERROR_TOO_MANY_OBJECTS: + return "Too many objects of the type have already been created"; + case VK_ERROR_FORMAT_NOT_SUPPORTED: + return "A requested format is not supported on this device"; + case VK_ERROR_SURFACE_LOST_KHR: + return "A surface is no longer available"; + case VK_SUBOPTIMAL_KHR: + return "A swapchain no longer matches the surface properties exactly, but can still be used"; + case VK_ERROR_OUT_OF_DATE_KHR: + return "A surface has changed in such a way that it is no longer compatible with the swapchain"; + case VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: + return "The display used by a swapchain does not use the same presentable image layout"; + case VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: + return "The requested window is already connected to a VkSurfaceKHR, or to some other non-Vulkan API"; + case VK_ERROR_VALIDATION_FAILED_EXT: + return "A validation layer found an error"; + default: + return "ERROR: UNKNOWN VULKAN ERROR"; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI int glfwVulkanSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + return _glfwInitVulkan(); +} + +GLFWAPI const char** glfwGetRequiredInstanceExtensions(uint32_t* count) +{ + *count = 0; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan()) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + return NULL; + } + + *count = 2; + return (const char**) _glfw.vk.extensions; +} + +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, + const char* procname) +{ + GLFWvkproc proc; + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (!_glfwInitVulkan()) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + return NULL; + } + + proc = (GLFWvkproc) vkGetInstanceProcAddr(instance, procname); + if (!proc) + proc = (GLFWvkproc) _glfw_dlsym(_glfw.vk.handle, procname); + + return proc; +} + +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(GLFW_FALSE); + + if (!_glfwInitVulkan()) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + return GLFW_FALSE; + } + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return GLFW_FALSE; + } + + return _glfwPlatformGetPhysicalDevicePresentationSupport(instance, + device, + queuefamily); +} + +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, + GLFWwindow* handle, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(surface != NULL); + + *surface = VK_NULL_HANDLE; + + _GLFW_REQUIRE_INIT_OR_RETURN(VK_ERROR_INITIALIZATION_FAILED); + + if (!_glfwInitVulkan()) + { + _glfwInputError(GLFW_API_UNAVAILABLE, "Vulkan: API not available"); + return VK_ERROR_INITIALIZATION_FAILED; + } + + if (!_glfw.vk.extensions[0]) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Vulkan: Window surface creation extensions not found"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + return _glfwPlatformCreateWindowSurface(instance, window, allocator, surface); +} + diff --git a/apps/exampleViewer/common/glfw/src/wgl_context.c b/apps/exampleViewer/common/glfw/src/wgl_context.c new file mode 100644 index 0000000000..677860de7a --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wgl_context.c @@ -0,0 +1,718 @@ +//======================================================================== +// GLFW 3.3 WGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + + +// Returns the specified attribute of the specified pixel format +// +static int getPixelFormatAttrib(_GLFWwindow* window, int pixelFormat, int attrib) +{ + int value = 0; + + assert(_glfw.wgl.ARB_pixel_format); + + if (!_glfw.wgl.GetPixelFormatAttribivARB(window->context.wgl.dc, + pixelFormat, + 0, 1, &attrib, &value)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve pixel format attribute %i", + attrib); + return 0; + } + + return value; +} + +// Return a list of available and usable framebuffer configs +// +static int choosePixelFormat(_GLFWwindow* window, const _GLFWfbconfig* desired) +{ + _GLFWfbconfig* usableConfigs; + const _GLFWfbconfig* closest; + int i, pixelFormat, nativeCount, usableCount; + + if (_glfw.wgl.ARB_pixel_format) + { + nativeCount = getPixelFormatAttrib(window, + 1, + WGL_NUMBER_PIXEL_FORMATS_ARB); + } + else + { + nativeCount = DescribePixelFormat(window->context.wgl.dc, + 1, + sizeof(PIXELFORMATDESCRIPTOR), + NULL); + } + + usableConfigs = calloc(nativeCount, sizeof(_GLFWfbconfig)); + usableCount = 0; + + for (i = 0; i < nativeCount; i++) + { + const int n = i + 1; + _GLFWfbconfig* u = usableConfigs + usableCount; + + if (_glfw.wgl.ARB_pixel_format) + { + // Get pixel format attributes through "modern" extension + + if (!getPixelFormatAttrib(window, n, WGL_SUPPORT_OPENGL_ARB) || + !getPixelFormatAttrib(window, n, WGL_DRAW_TO_WINDOW_ARB)) + { + continue; + } + + if (getPixelFormatAttrib(window, n, WGL_PIXEL_TYPE_ARB) != + WGL_TYPE_RGBA_ARB) + { + continue; + } + + if (getPixelFormatAttrib(window, n, WGL_ACCELERATION_ARB) == + WGL_NO_ACCELERATION_ARB) + { + continue; + } + + u->redBits = getPixelFormatAttrib(window, n, WGL_RED_BITS_ARB); + u->greenBits = getPixelFormatAttrib(window, n, WGL_GREEN_BITS_ARB); + u->blueBits = getPixelFormatAttrib(window, n, WGL_BLUE_BITS_ARB); + u->alphaBits = getPixelFormatAttrib(window, n, WGL_ALPHA_BITS_ARB); + + u->depthBits = getPixelFormatAttrib(window, n, WGL_DEPTH_BITS_ARB); + u->stencilBits = getPixelFormatAttrib(window, n, WGL_STENCIL_BITS_ARB); + + u->accumRedBits = getPixelFormatAttrib(window, n, WGL_ACCUM_RED_BITS_ARB); + u->accumGreenBits = getPixelFormatAttrib(window, n, WGL_ACCUM_GREEN_BITS_ARB); + u->accumBlueBits = getPixelFormatAttrib(window, n, WGL_ACCUM_BLUE_BITS_ARB); + u->accumAlphaBits = getPixelFormatAttrib(window, n, WGL_ACCUM_ALPHA_BITS_ARB); + + u->auxBuffers = getPixelFormatAttrib(window, n, WGL_AUX_BUFFERS_ARB); + + if (getPixelFormatAttrib(window, n, WGL_STEREO_ARB)) + u->stereo = GLFW_TRUE; + if (getPixelFormatAttrib(window, n, WGL_DOUBLE_BUFFER_ARB)) + u->doublebuffer = GLFW_TRUE; + + if (_glfw.wgl.ARB_multisample) + u->samples = getPixelFormatAttrib(window, n, WGL_SAMPLES_ARB); + + if (_glfw.wgl.ARB_framebuffer_sRGB || + _glfw.wgl.EXT_framebuffer_sRGB) + { + if (getPixelFormatAttrib(window, n, WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB)) + u->sRGB = GLFW_TRUE; + } + } + else + { + PIXELFORMATDESCRIPTOR pfd; + + // Get pixel format attributes through legacy PFDs + + if (!DescribePixelFormat(window->context.wgl.dc, + n, + sizeof(PIXELFORMATDESCRIPTOR), + &pfd)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_DRAW_TO_WINDOW) || + !(pfd.dwFlags & PFD_SUPPORT_OPENGL)) + { + continue; + } + + if (!(pfd.dwFlags & PFD_GENERIC_ACCELERATED) && + (pfd.dwFlags & PFD_GENERIC_FORMAT)) + { + continue; + } + + if (pfd.iPixelType != PFD_TYPE_RGBA) + continue; + + u->redBits = pfd.cRedBits; + u->greenBits = pfd.cGreenBits; + u->blueBits = pfd.cBlueBits; + u->alphaBits = pfd.cAlphaBits; + + u->depthBits = pfd.cDepthBits; + u->stencilBits = pfd.cStencilBits; + + u->accumRedBits = pfd.cAccumRedBits; + u->accumGreenBits = pfd.cAccumGreenBits; + u->accumBlueBits = pfd.cAccumBlueBits; + u->accumAlphaBits = pfd.cAccumAlphaBits; + + u->auxBuffers = pfd.cAuxBuffers; + + if (pfd.dwFlags & PFD_STEREO) + u->stereo = GLFW_TRUE; + if (pfd.dwFlags & PFD_DOUBLEBUFFER) + u->doublebuffer = GLFW_TRUE; + } + + u->handle = n; + usableCount++; + } + + if (!usableCount) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "WGL: The driver does not appear to support OpenGL"); + + free(usableConfigs); + return 0; + } + + closest = _glfwChooseFBConfig(desired, usableConfigs, usableCount); + if (!closest) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "WGL: Failed to find a suitable pixel format"); + + free(usableConfigs); + return 0; + } + + pixelFormat = (int) closest->handle; + free(usableConfigs); + + return pixelFormat; +} + +// Returns whether desktop compositing is enabled +// +static GLFWbool isCompositionEnabled(void) +{ + BOOL enabled; + + if (!_glfw_DwmIsCompositionEnabled) + return FALSE; + + if (_glfw_DwmIsCompositionEnabled(&enabled) != S_OK) + return FALSE; + + return enabled; +} + +static void makeContextCurrentWGL(_GLFWwindow* window) +{ + if (window) + { + if (wglMakeCurrent(window->context.wgl.dc, window->context.wgl.handle)) + _glfwPlatformSetCurrentContext(window); + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to make context current"); + _glfwPlatformSetCurrentContext(NULL); + } + } + else + { + if (!wglMakeCurrent(NULL, NULL)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to clear current context"); + } + + _glfwPlatformSetCurrentContext(NULL); + } +} + +static void swapBuffersWGL(_GLFWwindow* window) +{ + // HACK: Use DwmFlush when desktop composition is enabled + if (isCompositionEnabled() && !window->monitor) + { + int count = abs(window->context.wgl.interval); + while (count--) + _glfw_DwmFlush(); + } + + SwapBuffers(window->context.wgl.dc); +} + +static void swapIntervalWGL(int interval) +{ + _GLFWwindow* window = _glfwPlatformGetCurrentContext(); + + window->context.wgl.interval = interval; + + // HACK: Disable WGL swap interval when desktop composition is enabled to + // avoid interfering with DWM vsync + if (isCompositionEnabled() && !window->monitor) + interval = 0; + + if (_glfw.wgl.EXT_swap_control) + _glfw.wgl.SwapIntervalEXT(interval); +} + +static int extensionSupportedWGL(const char* extension) +{ + const char* extensions; + + if (_glfw.wgl.GetExtensionsStringEXT) + { + extensions = _glfw.wgl.GetExtensionsStringEXT(); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + } + + if (_glfw.wgl.GetExtensionsStringARB) + { + extensions = _glfw.wgl.GetExtensionsStringARB(wglGetCurrentDC()); + if (extensions) + { + if (_glfwStringInExtensionString(extension, extensions)) + return GLFW_TRUE; + } + } + + return GLFW_FALSE; +} + +static GLFWglproc getProcAddressWGL(const char* procname) +{ + const GLFWglproc proc = (GLFWglproc) wglGetProcAddress(procname); + if (proc) + return proc; + + return (GLFWglproc) GetProcAddress(_glfw.wgl.instance, procname); +} + +// Destroy the OpenGL context +// +static void destroyContextWGL(_GLFWwindow* window) +{ + if (window->context.wgl.handle) + { + wglDeleteContext(window->context.wgl.handle); + window->context.wgl.handle = NULL; + } +} + +// Initialize WGL-specific extensions +// +static void loadWGLExtensions(void) +{ + PIXELFORMATDESCRIPTOR pfd; + HGLRC rc; + HDC dc = GetDC(_glfw.win32.helperWindowHandle);; + + _glfw.wgl.extensionsLoaded = GLFW_TRUE; + + // NOTE: A dummy context has to be created for opengl32.dll to load the + // OpenGL ICD, from which we can then query WGL extensions + // NOTE: This code will accept the Microsoft GDI ICD; accelerated context + // creation failure occurs during manual pixel format enumeration + + ZeroMemory(&pfd, sizeof(pfd)); + pfd.nSize = sizeof(pfd); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + + if (!SetPixelFormat(dc, ChoosePixelFormat(dc, &pfd), &pfd)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to set pixel format for dummy context"); + return; + } + + rc = wglCreateContext(dc); + if (!rc) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to create dummy context"); + return; + } + + if (!wglMakeCurrent(dc, rc)) + { + wglDeleteContext(rc); + + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to make dummy context current"); + return; + } + + // NOTE: Functions must be loaded first as they're needed to retrieve the + // extension string that tells us whether the functions are supported + _glfw.wgl.GetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC) + wglGetProcAddress("wglGetExtensionsStringEXT"); + _glfw.wgl.GetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) + wglGetProcAddress("wglGetExtensionsStringARB"); + _glfw.wgl.CreateContextAttribsARB = (PFNWGLCREATECONTEXTATTRIBSARBPROC) + wglGetProcAddress("wglCreateContextAttribsARB"); + _glfw.wgl.SwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC) + wglGetProcAddress("wglSwapIntervalEXT"); + _glfw.wgl.GetPixelFormatAttribivARB = (PFNWGLGETPIXELFORMATATTRIBIVARBPROC) + wglGetProcAddress("wglGetPixelFormatAttribivARB"); + + // NOTE: WGL_ARB_extensions_string and WGL_EXT_extensions_string are not + // checked below as we are already using them + _glfw.wgl.ARB_multisample = + extensionSupportedWGL("WGL_ARB_multisample"); + _glfw.wgl.ARB_framebuffer_sRGB = + extensionSupportedWGL("WGL_ARB_framebuffer_sRGB"); + _glfw.wgl.EXT_framebuffer_sRGB = + extensionSupportedWGL("WGL_EXT_framebuffer_sRGB"); + _glfw.wgl.ARB_create_context = + extensionSupportedWGL("WGL_ARB_create_context"); + _glfw.wgl.ARB_create_context_profile = + extensionSupportedWGL("WGL_ARB_create_context_profile"); + _glfw.wgl.EXT_create_context_es2_profile = + extensionSupportedWGL("WGL_EXT_create_context_es2_profile"); + _glfw.wgl.ARB_create_context_robustness = + extensionSupportedWGL("WGL_ARB_create_context_robustness"); + _glfw.wgl.EXT_swap_control = + extensionSupportedWGL("WGL_EXT_swap_control"); + _glfw.wgl.ARB_pixel_format = + extensionSupportedWGL("WGL_ARB_pixel_format"); + _glfw.wgl.ARB_context_flush_control = + extensionSupportedWGL("WGL_ARB_context_flush_control"); + + wglMakeCurrent(dc, NULL); + wglDeleteContext(rc); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize WGL +// +GLFWbool _glfwInitWGL(void) +{ + if (_glfw.wgl.instance) + return GLFW_TRUE; + + _glfw.wgl.instance = LoadLibraryA("opengl32.dll"); + if (!_glfw.wgl.instance) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "WGL: Failed to load opengl32.dll"); + return GLFW_FALSE; + } + + _glfw.wgl.CreateContext = (WGLCREATECONTEXT_T) + GetProcAddress(_glfw.wgl.instance, "wglCreateContext"); + _glfw.wgl.DeleteContext = (WGLDELETECONTEXT_T) + GetProcAddress(_glfw.wgl.instance, "wglDeleteContext"); + _glfw.wgl.GetProcAddress = (WGLGETPROCADDRESS_T) + GetProcAddress(_glfw.wgl.instance, "wglGetProcAddress"); + _glfw.wgl.GetCurrentDC = (WGLGETCURRENTDC_T) + GetProcAddress(_glfw.wgl.instance, "wglGetCurrentDC"); + _glfw.wgl.MakeCurrent = (WGLMAKECURRENT_T) + GetProcAddress(_glfw.wgl.instance, "wglMakeCurrent"); + _glfw.wgl.ShareLists = (WGLSHARELISTS_T) + GetProcAddress(_glfw.wgl.instance, "wglShareLists"); + + return GLFW_TRUE; +} + +// Terminate WGL +// +void _glfwTerminateWGL(void) +{ + if (_glfw.wgl.instance) + FreeLibrary(_glfw.wgl.instance); +} + +#define setWGLattrib(attribName, attribValue) \ +{ \ + attribs[index++] = attribName; \ + attribs[index++] = attribValue; \ + assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ +} + +// Create the OpenGL or OpenGL ES context +// +GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + int attribs[40]; + int pixelFormat; + PIXELFORMATDESCRIPTOR pfd; + HGLRC share = NULL; + + if (!_glfw.wgl.extensionsLoaded) + loadWGLExtensions(); + + if (ctxconfig->share) + share = ctxconfig->share->context.wgl.handle; + + window->context.wgl.dc = GetDC(window->win32.handle); + if (!window->context.wgl.dc) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve DC for window"); + return GLFW_FALSE; + } + + pixelFormat = choosePixelFormat(window, fbconfig); + if (!pixelFormat) + return GLFW_FALSE; + + if (!DescribePixelFormat(window->context.wgl.dc, + pixelFormat, sizeof(pfd), &pfd)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to retrieve PFD for selected pixel format"); + return GLFW_FALSE; + } + + if (!SetPixelFormat(window->context.wgl.dc, pixelFormat, &pfd)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to set selected pixel format"); + return GLFW_FALSE; + } + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + { + if (!_glfw.wgl.ARB_create_context) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: A forward compatible OpenGL context requested but WGL_ARB_create_context is unavailable"); + return GLFW_FALSE; + } + } + + if (ctxconfig->profile) + { + if (!_glfw.wgl.ARB_create_context_profile) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: OpenGL profile requested but WGL_ARB_create_context_profile is unavailable"); + return GLFW_FALSE; + } + } + } + else + { + if (!_glfw.wgl.ARB_create_context || + !_glfw.wgl.ARB_create_context_profile || + !_glfw.wgl.EXT_create_context_es2_profile) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "WGL: OpenGL ES requested but WGL_ARB_create_context_es2_profile is unavailable"); + return GLFW_FALSE; + } + } + + if (_glfw.wgl.ARB_create_context) + { + int index = 0, mask = 0, flags = 0; + + if (ctxconfig->client == GLFW_OPENGL_API) + { + if (ctxconfig->forward) + flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB; + + if (ctxconfig->profile == GLFW_OPENGL_CORE_PROFILE) + mask |= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; + else if (ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) + mask |= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; + } + else + mask |= WGL_CONTEXT_ES2_PROFILE_BIT_EXT; + + if (ctxconfig->debug) + flags |= WGL_CONTEXT_DEBUG_BIT_ARB; + if (ctxconfig->noerror) + flags |= GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR; + + if (ctxconfig->robustness) + { + if (_glfw.wgl.ARB_create_context_robustness) + { + if (ctxconfig->robustness == GLFW_NO_RESET_NOTIFICATION) + { + setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_NO_RESET_NOTIFICATION_ARB); + } + else if (ctxconfig->robustness == GLFW_LOSE_CONTEXT_ON_RESET) + { + setWGLattrib(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, + WGL_LOSE_CONTEXT_ON_RESET_ARB); + } + + flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB; + } + } + + if (ctxconfig->release) + { + if (_glfw.wgl.ARB_context_flush_control) + { + if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_NONE) + { + setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB); + } + else if (ctxconfig->release == GLFW_RELEASE_BEHAVIOR_FLUSH) + { + setWGLattrib(WGL_CONTEXT_RELEASE_BEHAVIOR_ARB, + WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB); + } + } + } + + // NOTE: Only request an explicitly versioned context when necessary, as + // explicitly requesting version 1.0 does not always return the + // highest version supported by the driver + if (ctxconfig->major != 1 || ctxconfig->minor != 0) + { + setWGLattrib(WGL_CONTEXT_MAJOR_VERSION_ARB, ctxconfig->major); + setWGLattrib(WGL_CONTEXT_MINOR_VERSION_ARB, ctxconfig->minor); + } + + if (flags) + setWGLattrib(WGL_CONTEXT_FLAGS_ARB, flags); + + if (mask) + setWGLattrib(WGL_CONTEXT_PROFILE_MASK_ARB, mask); + + setWGLattrib(0, 0); + + window->context.wgl.handle = + _glfw.wgl.CreateContextAttribsARB(window->context.wgl.dc, + share, attribs); + if (!window->context.wgl.handle) + { + const DWORD error = GetLastError(); + + if (error == (0xc0070000 | ERROR_INVALID_VERSION_ARB)) + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Driver does not support OpenGL version %i.%i", + ctxconfig->major, + ctxconfig->minor); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Driver does not support OpenGL ES version %i.%i", + ctxconfig->major, + ctxconfig->minor); + } + } + else if (error == (0xc0070000 | ERROR_INVALID_PROFILE_ARB)) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Driver does not support the requested OpenGL profile"); + } + else + { + if (ctxconfig->client == GLFW_OPENGL_API) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); + } + else + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL ES context"); + } + } + + return GLFW_FALSE; + } + } + else + { + window->context.wgl.handle = wglCreateContext(window->context.wgl.dc); + if (!window->context.wgl.handle) + { + _glfwInputError(GLFW_VERSION_UNAVAILABLE, + "WGL: Failed to create OpenGL context"); + return GLFW_FALSE; + } + + if (share) + { + if (!wglShareLists(share, window->context.wgl.handle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "WGL: Failed to enable sharing with specified OpenGL context"); + return GLFW_FALSE; + } + } + } + + window->context.makeCurrent = makeContextCurrentWGL; + window->context.swapBuffers = swapBuffersWGL; + window->context.swapInterval = swapIntervalWGL; + window->context.extensionSupported = extensionSupportedWGL; + window->context.getProcAddress = getProcAddressWGL; + window->context.destroy = destroyContextWGL; + + return GLFW_TRUE; +} + +#undef setWGLattrib + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (window->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + + return window->context.wgl.handle; +} + diff --git a/apps/exampleViewer/common/glfw/src/wgl_context.h b/apps/exampleViewer/common/glfw/src/wgl_context.h new file mode 100644 index 0000000000..6d989232bc --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wgl_context.h @@ -0,0 +1,157 @@ +//======================================================================== +// GLFW 3.3 WGL - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_wgl_context_h_ +#define _glfw3_wgl_context_h_ + + +#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000 +#define WGL_SUPPORT_OPENGL_ARB 0x2010 +#define WGL_DRAW_TO_WINDOW_ARB 0x2001 +#define WGL_PIXEL_TYPE_ARB 0x2013 +#define WGL_TYPE_RGBA_ARB 0x202b +#define WGL_ACCELERATION_ARB 0x2003 +#define WGL_NO_ACCELERATION_ARB 0x2025 +#define WGL_RED_BITS_ARB 0x2015 +#define WGL_RED_SHIFT_ARB 0x2016 +#define WGL_GREEN_BITS_ARB 0x2017 +#define WGL_GREEN_SHIFT_ARB 0x2018 +#define WGL_BLUE_BITS_ARB 0x2019 +#define WGL_BLUE_SHIFT_ARB 0x201a +#define WGL_ALPHA_BITS_ARB 0x201b +#define WGL_ALPHA_SHIFT_ARB 0x201c +#define WGL_ACCUM_BITS_ARB 0x201d +#define WGL_ACCUM_RED_BITS_ARB 0x201e +#define WGL_ACCUM_GREEN_BITS_ARB 0x201f +#define WGL_ACCUM_BLUE_BITS_ARB 0x2020 +#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021 +#define WGL_DEPTH_BITS_ARB 0x2022 +#define WGL_STENCIL_BITS_ARB 0x2023 +#define WGL_AUX_BUFFERS_ARB 0x2024 +#define WGL_STEREO_ARB 0x2012 +#define WGL_DOUBLE_BUFFER_ARB 0x2011 +#define WGL_SAMPLES_ARB 0x2042 +#define WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB 0x20a9 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_ES2_PROFILE_BIT_EXT 0x00000004 +#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x00000004 +#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252 +#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256 +#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_ARB 0x2097 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB 0 +#define WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB 0x2098 + +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 + +typedef BOOL (WINAPI * PFNWGLSWAPINTERVALEXTPROC)(int); +typedef BOOL (WINAPI * PFNWGLGETPIXELFORMATATTRIBIVARBPROC)(HDC,int,int,UINT,const int*,int*); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGEXTPROC)(void); +typedef const char* (WINAPI * PFNWGLGETEXTENSIONSSTRINGARBPROC)(HDC); +typedef HGLRC (WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC)(HDC,HGLRC,const int*); + +typedef HGLRC (WINAPI * WGLCREATECONTEXT_T)(HDC); +typedef BOOL (WINAPI * WGLDELETECONTEXT_T)(HGLRC); +typedef PROC (WINAPI * WGLGETPROCADDRESS_T)(LPCSTR); +typedef HDC (WINAPI * WGLGETCURRENTDC_T)(void); +typedef BOOL (WINAPI * WGLMAKECURRENT_T)(HDC,HGLRC); +typedef BOOL (WINAPI * WGLSHARELISTS_T)(HGLRC,HGLRC); + +// opengl32.dll function pointer typedefs +#define wglCreateContext _glfw.wgl.CreateContext +#define wglDeleteContext _glfw.wgl.DeleteContext +#define wglGetProcAddress _glfw.wgl.GetProcAddress +#define wglGetCurrentDC _glfw.wgl.GetCurrentDC +#define wglMakeCurrent _glfw.wgl.MakeCurrent +#define wglShareLists _glfw.wgl.ShareLists + +#define _GLFW_RECREATION_NOT_NEEDED 0 +#define _GLFW_RECREATION_REQUIRED 1 +#define _GLFW_RECREATION_IMPOSSIBLE 2 + +#define _GLFW_PLATFORM_CONTEXT_STATE _GLFWcontextWGL wgl +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE _GLFWlibraryWGL wgl + + +// WGL-specific per-context data +// +typedef struct _GLFWcontextWGL +{ + HDC dc; + HGLRC handle; + int interval; + +} _GLFWcontextWGL; + +// WGL-specific global data +// +typedef struct _GLFWlibraryWGL +{ + HINSTANCE instance; + WGLCREATECONTEXT_T CreateContext; + WGLDELETECONTEXT_T DeleteContext; + WGLGETPROCADDRESS_T GetProcAddress; + WGLGETCURRENTDC_T GetCurrentDC; + WGLMAKECURRENT_T MakeCurrent; + WGLSHARELISTS_T ShareLists; + + GLFWbool extensionsLoaded; + + PFNWGLSWAPINTERVALEXTPROC SwapIntervalEXT; + PFNWGLGETPIXELFORMATATTRIBIVARBPROC GetPixelFormatAttribivARB; + PFNWGLGETEXTENSIONSSTRINGEXTPROC GetExtensionsStringEXT; + PFNWGLGETEXTENSIONSSTRINGARBPROC GetExtensionsStringARB; + PFNWGLCREATECONTEXTATTRIBSARBPROC CreateContextAttribsARB; + GLFWbool EXT_swap_control; + GLFWbool ARB_multisample; + GLFWbool ARB_framebuffer_sRGB; + GLFWbool EXT_framebuffer_sRGB; + GLFWbool ARB_pixel_format; + GLFWbool ARB_create_context; + GLFWbool ARB_create_context_profile; + GLFWbool EXT_create_context_es2_profile; + GLFWbool ARB_create_context_robustness; + GLFWbool ARB_context_flush_control; + +} _GLFWlibraryWGL; + + +GLFWbool _glfwInitWGL(void); +void _glfwTerminateWGL(void); +GLFWbool _glfwCreateContextWGL(_GLFWwindow* window, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig); + +#endif // _glfw3_wgl_context_h_ diff --git a/apps/exampleViewer/common/glfw/src/win32_init.c b/apps/exampleViewer/common/glfw/src/win32_init.c new file mode 100644 index 0000000000..4c0979daf7 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_init.c @@ -0,0 +1,473 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +#include +DEFINE_GUID(GUID_DEVINTERFACE_HID,0x4d1e55b2,0xf16f,0x11cf,0x88,0xcb,0x00,0x11,0x11,0x00,0x00,0x30); + +#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) + +// Applications exporting this symbol with this value will be automatically +// directed to the high-performance GPU on Nvidia Optimus systems with +// up-to-date drivers +// +__declspec(dllexport) DWORD NvOptimusEnablement = 1; + +// Applications exporting this symbol with this value will be automatically +// directed to the high-performance GPU on AMD PowerXpress systems with +// up-to-date drivers +// +__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; + +#endif // _GLFW_USE_HYBRID_HPG + +#if defined(_GLFW_BUILD_DLL) + +// GLFW DLL entry point +// +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved) +{ + return TRUE; +} + +#endif // _GLFW_BUILD_DLL + +// Load necessary libraries (DLLs) +// +static GLFWbool loadLibraries(void) +{ + _glfw.win32.winmm.instance = LoadLibraryA("winmm.dll"); + if (!_glfw.win32.winmm.instance) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load winmm.dll"); + return GLFW_FALSE; + } + + _glfw.win32.winmm.timeGetTime = (TIMEGETTIME_T) + GetProcAddress(_glfw.win32.winmm.instance, "timeGetTime"); + + _glfw.win32.user32.instance = LoadLibraryA("user32.dll"); + if (!_glfw.win32.user32.instance) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to load user32.dll"); + return GLFW_FALSE; + } + + _glfw.win32.user32.SetProcessDPIAware = (SETPROCESSDPIAWARE_T) + GetProcAddress(_glfw.win32.user32.instance, "SetProcessDPIAware"); + _glfw.win32.user32.ChangeWindowMessageFilterEx = (CHANGEWINDOWMESSAGEFILTEREX_T) + GetProcAddress(_glfw.win32.user32.instance, "ChangeWindowMessageFilterEx"); + + _glfw.win32.dinput8.instance = LoadLibraryA("dinput8.dll"); + if (_glfw.win32.dinput8.instance) + { + _glfw.win32.dinput8.DirectInput8Create = (DIRECTINPUT8CREATE_T) + GetProcAddress(_glfw.win32.dinput8.instance, "DirectInput8Create"); + } + + { + int i; + const char* names[] = + { + "xinput1_4.dll", + "xinput1_3.dll", + "xinput9_1_0.dll", + "xinput1_2.dll", + "xinput1_1.dll", + NULL + }; + + for (i = 0; names[i]; i++) + { + _glfw.win32.xinput.instance = LoadLibraryA(names[i]); + if (_glfw.win32.xinput.instance) + { + _glfw.win32.xinput.XInputGetCapabilities = (XINPUTGETCAPABILITIES_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetCapabilities"); + _glfw.win32.xinput.XInputGetState = (XINPUTGETSTATE_T) + GetProcAddress(_glfw.win32.xinput.instance, "XInputGetState"); + + break; + } + } + } + + _glfw.win32.dwmapi.instance = LoadLibraryA("dwmapi.dll"); + if (_glfw.win32.dwmapi.instance) + { + _glfw.win32.dwmapi.DwmIsCompositionEnabled = (DWMISCOMPOSITIONENABLED_T) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmIsCompositionEnabled"); + _glfw.win32.dwmapi.DwmFlush = (DWMFLUSH_T) + GetProcAddress(_glfw.win32.dwmapi.instance, "DwmFlush"); + } + + _glfw.win32.shcore.instance = LoadLibraryA("shcore.dll"); + if (_glfw.win32.shcore.instance) + { + _glfw.win32.shcore.SetProcessDpiAwareness = (SETPROCESSDPIAWARENESS_T) + GetProcAddress(_glfw.win32.shcore.instance, "SetProcessDpiAwareness"); + } + + return GLFW_TRUE; +} + +// Unload used libraries (DLLs) +// +static void freeLibraries(void) +{ + if (_glfw.win32.xinput.instance) + FreeLibrary(_glfw.win32.xinput.instance); + + if (_glfw.win32.dinput8.instance) + FreeLibrary(_glfw.win32.dinput8.instance); + + if (_glfw.win32.winmm.instance) + FreeLibrary(_glfw.win32.winmm.instance); + + if (_glfw.win32.user32.instance) + FreeLibrary(_glfw.win32.user32.instance); + + if (_glfw.win32.dwmapi.instance) + FreeLibrary(_glfw.win32.dwmapi.instance); + + if (_glfw.win32.shcore.instance) + FreeLibrary(_glfw.win32.shcore.instance); +} + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.win32.keycodes, -1, sizeof(_glfw.win32.keycodes)); + memset(_glfw.win32.scancodes, -1, sizeof(_glfw.win32.scancodes)); + + _glfw.win32.keycodes[0x00B] = GLFW_KEY_0; + _glfw.win32.keycodes[0x002] = GLFW_KEY_1; + _glfw.win32.keycodes[0x003] = GLFW_KEY_2; + _glfw.win32.keycodes[0x004] = GLFW_KEY_3; + _glfw.win32.keycodes[0x005] = GLFW_KEY_4; + _glfw.win32.keycodes[0x006] = GLFW_KEY_5; + _glfw.win32.keycodes[0x007] = GLFW_KEY_6; + _glfw.win32.keycodes[0x008] = GLFW_KEY_7; + _glfw.win32.keycodes[0x009] = GLFW_KEY_8; + _glfw.win32.keycodes[0x00A] = GLFW_KEY_9; + _glfw.win32.keycodes[0x01E] = GLFW_KEY_A; + _glfw.win32.keycodes[0x030] = GLFW_KEY_B; + _glfw.win32.keycodes[0x02E] = GLFW_KEY_C; + _glfw.win32.keycodes[0x020] = GLFW_KEY_D; + _glfw.win32.keycodes[0x012] = GLFW_KEY_E; + _glfw.win32.keycodes[0x021] = GLFW_KEY_F; + _glfw.win32.keycodes[0x022] = GLFW_KEY_G; + _glfw.win32.keycodes[0x023] = GLFW_KEY_H; + _glfw.win32.keycodes[0x017] = GLFW_KEY_I; + _glfw.win32.keycodes[0x024] = GLFW_KEY_J; + _glfw.win32.keycodes[0x025] = GLFW_KEY_K; + _glfw.win32.keycodes[0x026] = GLFW_KEY_L; + _glfw.win32.keycodes[0x032] = GLFW_KEY_M; + _glfw.win32.keycodes[0x031] = GLFW_KEY_N; + _glfw.win32.keycodes[0x018] = GLFW_KEY_O; + _glfw.win32.keycodes[0x019] = GLFW_KEY_P; + _glfw.win32.keycodes[0x010] = GLFW_KEY_Q; + _glfw.win32.keycodes[0x013] = GLFW_KEY_R; + _glfw.win32.keycodes[0x01F] = GLFW_KEY_S; + _glfw.win32.keycodes[0x014] = GLFW_KEY_T; + _glfw.win32.keycodes[0x016] = GLFW_KEY_U; + _glfw.win32.keycodes[0x02F] = GLFW_KEY_V; + _glfw.win32.keycodes[0x011] = GLFW_KEY_W; + _glfw.win32.keycodes[0x02D] = GLFW_KEY_X; + _glfw.win32.keycodes[0x015] = GLFW_KEY_Y; + _glfw.win32.keycodes[0x02C] = GLFW_KEY_Z; + + _glfw.win32.keycodes[0x028] = GLFW_KEY_APOSTROPHE; + _glfw.win32.keycodes[0x02B] = GLFW_KEY_BACKSLASH; + _glfw.win32.keycodes[0x033] = GLFW_KEY_COMMA; + _glfw.win32.keycodes[0x00D] = GLFW_KEY_EQUAL; + _glfw.win32.keycodes[0x029] = GLFW_KEY_GRAVE_ACCENT; + _glfw.win32.keycodes[0x01A] = GLFW_KEY_LEFT_BRACKET; + _glfw.win32.keycodes[0x00C] = GLFW_KEY_MINUS; + _glfw.win32.keycodes[0x034] = GLFW_KEY_PERIOD; + _glfw.win32.keycodes[0x01B] = GLFW_KEY_RIGHT_BRACKET; + _glfw.win32.keycodes[0x027] = GLFW_KEY_SEMICOLON; + _glfw.win32.keycodes[0x035] = GLFW_KEY_SLASH; + _glfw.win32.keycodes[0x056] = GLFW_KEY_WORLD_2; + + _glfw.win32.keycodes[0x00E] = GLFW_KEY_BACKSPACE; + _glfw.win32.keycodes[0x153] = GLFW_KEY_DELETE; + _glfw.win32.keycodes[0x14F] = GLFW_KEY_END; + _glfw.win32.keycodes[0x01C] = GLFW_KEY_ENTER; + _glfw.win32.keycodes[0x001] = GLFW_KEY_ESCAPE; + _glfw.win32.keycodes[0x147] = GLFW_KEY_HOME; + _glfw.win32.keycodes[0x152] = GLFW_KEY_INSERT; + _glfw.win32.keycodes[0x15D] = GLFW_KEY_MENU; + _glfw.win32.keycodes[0x151] = GLFW_KEY_PAGE_DOWN; + _glfw.win32.keycodes[0x149] = GLFW_KEY_PAGE_UP; + _glfw.win32.keycodes[0x045] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x146] = GLFW_KEY_PAUSE; + _glfw.win32.keycodes[0x039] = GLFW_KEY_SPACE; + _glfw.win32.keycodes[0x00F] = GLFW_KEY_TAB; + _glfw.win32.keycodes[0x03A] = GLFW_KEY_CAPS_LOCK; + _glfw.win32.keycodes[0x145] = GLFW_KEY_NUM_LOCK; + _glfw.win32.keycodes[0x046] = GLFW_KEY_SCROLL_LOCK; + _glfw.win32.keycodes[0x03B] = GLFW_KEY_F1; + _glfw.win32.keycodes[0x03C] = GLFW_KEY_F2; + _glfw.win32.keycodes[0x03D] = GLFW_KEY_F3; + _glfw.win32.keycodes[0x03E] = GLFW_KEY_F4; + _glfw.win32.keycodes[0x03F] = GLFW_KEY_F5; + _glfw.win32.keycodes[0x040] = GLFW_KEY_F6; + _glfw.win32.keycodes[0x041] = GLFW_KEY_F7; + _glfw.win32.keycodes[0x042] = GLFW_KEY_F8; + _glfw.win32.keycodes[0x043] = GLFW_KEY_F9; + _glfw.win32.keycodes[0x044] = GLFW_KEY_F10; + _glfw.win32.keycodes[0x057] = GLFW_KEY_F11; + _glfw.win32.keycodes[0x058] = GLFW_KEY_F12; + _glfw.win32.keycodes[0x064] = GLFW_KEY_F13; + _glfw.win32.keycodes[0x065] = GLFW_KEY_F14; + _glfw.win32.keycodes[0x066] = GLFW_KEY_F15; + _glfw.win32.keycodes[0x067] = GLFW_KEY_F16; + _glfw.win32.keycodes[0x068] = GLFW_KEY_F17; + _glfw.win32.keycodes[0x069] = GLFW_KEY_F18; + _glfw.win32.keycodes[0x06A] = GLFW_KEY_F19; + _glfw.win32.keycodes[0x06B] = GLFW_KEY_F20; + _glfw.win32.keycodes[0x06C] = GLFW_KEY_F21; + _glfw.win32.keycodes[0x06D] = GLFW_KEY_F22; + _glfw.win32.keycodes[0x06E] = GLFW_KEY_F23; + _glfw.win32.keycodes[0x076] = GLFW_KEY_F24; + _glfw.win32.keycodes[0x038] = GLFW_KEY_LEFT_ALT; + _glfw.win32.keycodes[0x01D] = GLFW_KEY_LEFT_CONTROL; + _glfw.win32.keycodes[0x02A] = GLFW_KEY_LEFT_SHIFT; + _glfw.win32.keycodes[0x15B] = GLFW_KEY_LEFT_SUPER; + _glfw.win32.keycodes[0x137] = GLFW_KEY_PRINT_SCREEN; + _glfw.win32.keycodes[0x138] = GLFW_KEY_RIGHT_ALT; + _glfw.win32.keycodes[0x11D] = GLFW_KEY_RIGHT_CONTROL; + _glfw.win32.keycodes[0x036] = GLFW_KEY_RIGHT_SHIFT; + _glfw.win32.keycodes[0x15C] = GLFW_KEY_RIGHT_SUPER; + _glfw.win32.keycodes[0x150] = GLFW_KEY_DOWN; + _glfw.win32.keycodes[0x14B] = GLFW_KEY_LEFT; + _glfw.win32.keycodes[0x14D] = GLFW_KEY_RIGHT; + _glfw.win32.keycodes[0x148] = GLFW_KEY_UP; + + _glfw.win32.keycodes[0x052] = GLFW_KEY_KP_0; + _glfw.win32.keycodes[0x04F] = GLFW_KEY_KP_1; + _glfw.win32.keycodes[0x050] = GLFW_KEY_KP_2; + _glfw.win32.keycodes[0x051] = GLFW_KEY_KP_3; + _glfw.win32.keycodes[0x04B] = GLFW_KEY_KP_4; + _glfw.win32.keycodes[0x04C] = GLFW_KEY_KP_5; + _glfw.win32.keycodes[0x04D] = GLFW_KEY_KP_6; + _glfw.win32.keycodes[0x047] = GLFW_KEY_KP_7; + _glfw.win32.keycodes[0x048] = GLFW_KEY_KP_8; + _glfw.win32.keycodes[0x049] = GLFW_KEY_KP_9; + _glfw.win32.keycodes[0x04E] = GLFW_KEY_KP_ADD; + _glfw.win32.keycodes[0x053] = GLFW_KEY_KP_DECIMAL; + _glfw.win32.keycodes[0x135] = GLFW_KEY_KP_DIVIDE; + _glfw.win32.keycodes[0x11C] = GLFW_KEY_KP_ENTER; + _glfw.win32.keycodes[0x037] = GLFW_KEY_KP_MULTIPLY; + _glfw.win32.keycodes[0x04A] = GLFW_KEY_KP_SUBTRACT; + + for (scancode = 0; scancode < 512; scancode++) + { + if (_glfw.win32.keycodes[scancode] > 0) + _glfw.win32.scancodes[_glfw.win32.keycodes[scancode]] = scancode; + } +} + +// Creates a dummy window for behind-the-scenes work +// +static HWND createHelperWindow(void) +{ + HWND window = CreateWindowExW(WS_EX_OVERLAPPEDWINDOW, + _GLFW_WNDCLASSNAME, + L"GLFW helper window", + WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 1, 1, + HWND_MESSAGE, NULL, + GetModuleHandleW(NULL), + NULL); + if (!window) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create helper window"); + return NULL; + } + + // HACK: The first call to ShowWindow is ignored if the parent process + // passed along a STARTUPINFO, so clear that flag with a no-op call + ShowWindow(window, SW_HIDE); + + // Register for HID device notifications + { + DEV_BROADCAST_DEVICEINTERFACE_W dbi; + ZeroMemory(&dbi, sizeof(dbi)); + dbi.dbcc_size = sizeof(dbi); + dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + dbi.dbcc_classguid = GUID_DEVINTERFACE_HID; + + RegisterDeviceNotificationW(window, + (DEV_BROADCAST_HDR*) &dbi, + DEVICE_NOTIFY_WINDOW_HANDLE); + } + + return window; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Returns a wide string version of the specified UTF-8 string +// +WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source) +{ + WCHAR* target; + int length; + + length = MultiByteToWideChar(CP_UTF8, 0, source, -1, NULL, 0); + if (!length) + return NULL; + + target = calloc(length, sizeof(WCHAR)); + + if (!MultiByteToWideChar(CP_UTF8, 0, source, -1, target, length)) + { + free(target); + return NULL; + } + + return target; +} + +// Returns a UTF-8 string version of the specified wide string +// +char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source) +{ + char* target; + int length; + + length = WideCharToMultiByte(CP_UTF8, 0, source, -1, NULL, 0, NULL, NULL); + if (!length) + return NULL; + + target = calloc(length, 1); + + if (!WideCharToMultiByte(CP_UTF8, 0, source, -1, target, length, NULL, NULL)) + { + free(target); + return NULL; + } + + return target; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + if (!_glfwInitThreadLocalStorageWin32()) + return GLFW_FALSE; + + // To make SetForegroundWindow work as we want, we need to fiddle + // with the FOREGROUNDLOCKTIMEOUT system setting (we do this as early + // as possible in the hope of still being the foreground process) + SystemParametersInfoW(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, + &_glfw.win32.foregroundLockTimeout, 0); + SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, UIntToPtr(0), + SPIF_SENDCHANGE); + + if (!loadLibraries()) + return GLFW_FALSE; + + createKeyTables(); + + if (_glfw_SetProcessDpiAwareness) + _glfw_SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE); + else if (_glfw_SetProcessDPIAware) + _glfw_SetProcessDPIAware(); + + if (!_glfwRegisterWindowClassWin32()) + return GLFW_FALSE; + + _glfw.win32.helperWindowHandle = createHelperWindow(); + if (!_glfw.win32.helperWindowHandle) + return GLFW_FALSE; + + _glfwPlatformPollEvents(); + + _glfwInitTimerWin32(); + _glfwInitJoysticksWin32(); + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + if (_glfw.win32.helperWindowHandle) + DestroyWindow(_glfw.win32.helperWindowHandle); + + _glfwUnregisterWindowClassWin32(); + + // Restore previous foreground lock timeout system setting + SystemParametersInfoW(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, + UIntToPtr(_glfw.win32.foregroundLockTimeout), + SPIF_SENDCHANGE); + + free(_glfw.win32.clipboardString); + + _glfwTerminateWGL(); + _glfwTerminateEGL(); + + _glfwTerminateJoysticksWin32(); + _glfwTerminateThreadLocalStorageWin32(); + + freeLibraries(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Win32 WGL EGL" +#if defined(__MINGW32__) + " MinGW" +#elif defined(_MSC_VER) + " VisualC" +#endif +#if defined(_GLFW_USE_HYBRID_HPG) || defined(_GLFW_USE_OPTIMUS_HPG) + " hybrid-GPU" +#endif +#if defined(_GLFW_BUILD_DLL) + " DLL" +#endif + ; +} + diff --git a/apps/exampleViewer/common/glfw/src/win32_joystick.c b/apps/exampleViewer/common/glfw/src/win32_joystick.c new file mode 100644 index 0000000000..5d2e21c369 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_joystick.c @@ -0,0 +1,763 @@ +//======================================================================== +// GLFW 3.1 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + +#include + +#define _GLFW_PRESENCE_ONLY 1 +#define _GLFW_UPDATE_STATE 2 + +#define _GLFW_TYPE_AXIS 0 +#define _GLFW_TYPE_SLIDER 1 +#define _GLFW_TYPE_BUTTON 2 +#define _GLFW_TYPE_POV 3 + +// Data produced with DirectInput device object enumeration +// +typedef struct _GLFWobjenumWin32 +{ + IDirectInputDevice8W* device; + _GLFWjoyobjectWin32* objects; + int objectCount; + int axisCount; + int sliderCount; + int buttonCount; + int povCount; +} _GLFWobjenumWin32; + +// Define only the necessary GUIDs (it's bad enough that we're exporting these) +// +DEFINE_GUID(IID_IDirectInput8W,0xbf798031,0x483a,0x4da2,0xaa,0x99,0x5d,0x64,0xed,0x36,0x97,0x00); +DEFINE_GUID(GUID_XAxis,0xa36d02e0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_YAxis,0xa36d02e1,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_ZAxis,0xa36d02e2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RxAxis,0xa36d02f4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RyAxis,0xa36d02f5,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_RzAxis,0xa36d02e3,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Slider,0xa36d02e4,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_Button,0xa36d02f0,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); +DEFINE_GUID(GUID_POV,0xa36d02f2,0xc9f3,0x11cf,0xbf,0xc7,0x44,0x45,0x53,0x54,0x00,0x00); + +// Object data array for our clone of c_dfDIJoystick +// Generated with https://github.com/elmindreda/c_dfDIJoystick2 +// +static DIOBJECTDATAFORMAT _glfwObjectDataFormats[] = +{ + { &GUID_XAxis,DIJOFS_X,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_YAxis,DIJOFS_Y,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_ZAxis,DIJOFS_Z,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RxAxis,DIJOFS_RX,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RyAxis,DIJOFS_RY,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_RzAxis,DIJOFS_RZ,DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(0),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_Slider,DIJOFS_SLIDER(1),DIDFT_AXIS|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,DIDOI_ASPECTPOSITION }, + { &GUID_POV,DIJOFS_POV(0),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(1),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(2),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { &GUID_POV,DIJOFS_POV(3),DIDFT_POV|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(0),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(1),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(2),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(3),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(4),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(5),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(6),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(7),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(8),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(9),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(10),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(11),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(12),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(13),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(14),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(15),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(16),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(17),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(18),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(19),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(20),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(21),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(22),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(23),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(24),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(25),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(26),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(27),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(28),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(29),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(30),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, + { NULL,DIJOFS_BUTTON(31),DIDFT_BUTTON|DIDFT_OPTIONAL|DIDFT_ANYINSTANCE,0 }, +}; + +// Our clone of c_dfDIJoystick +// +static const DIDATAFORMAT _glfwDataFormat = +{ + sizeof(DIDATAFORMAT), + sizeof(DIOBJECTDATAFORMAT), + DIDFT_ABSAXIS, + sizeof(DIJOYSTATE), + sizeof(_glfwObjectDataFormats) / sizeof(DIOBJECTDATAFORMAT), + _glfwObjectDataFormats +}; + +// Returns a description fitting the specified XInput capabilities +// +static const char* getDeviceDescription(const XINPUT_CAPABILITIES* xic) +{ + switch (xic->SubType) + { + case XINPUT_DEVSUBTYPE_WHEEL: + return "XInput Wheel"; + case XINPUT_DEVSUBTYPE_ARCADE_STICK: + return "XInput Arcade Stick"; + case XINPUT_DEVSUBTYPE_FLIGHT_STICK: + return "XInput Flight Stick"; + case XINPUT_DEVSUBTYPE_DANCE_PAD: + return "XInput Dance Pad"; + case XINPUT_DEVSUBTYPE_GUITAR: + return "XInput Guitar"; + case XINPUT_DEVSUBTYPE_DRUM_KIT: + return "XInput Drum Kit"; + case XINPUT_DEVSUBTYPE_GAMEPAD: + { + if (xic->Flags & XINPUT_CAPS_WIRELESS) + return "Wireless Xbox 360 Controller"; + else + return "Xbox 360 Controller"; + } + } + + return "Unknown XInput Device"; +} + +// Lexically compare device objects +// +static int compareJoystickObjects(const void* first, const void* second) +{ + const _GLFWjoyobjectWin32* fo = first; + const _GLFWjoyobjectWin32* so = second; + + if (fo->type != so->type) + return fo->type - so->type; + + return fo->offset - so->offset; +} + +// Checks whether the specified device supports XInput +// Technique from FDInputJoystickManager::IsXInputDeviceFast in ZDoom +// +static GLFWbool supportsXInput(const GUID* guid) +{ + UINT i, count = 0; + RAWINPUTDEVICELIST* ridl; + GLFWbool result = GLFW_FALSE; + + if (GetRawInputDeviceList(NULL, &count, sizeof(RAWINPUTDEVICELIST)) != 0) + return GLFW_FALSE; + + ridl = calloc(count, sizeof(RAWINPUTDEVICELIST)); + + if (GetRawInputDeviceList(ridl, &count, sizeof(RAWINPUTDEVICELIST)) == (UINT) -1) + { + free(ridl); + return GLFW_FALSE; + } + + for (i = 0; i < count; i++) + { + RID_DEVICE_INFO rdi; + char name[256]; + UINT size; + + if (ridl[i].dwType != RIM_TYPEHID) + continue; + + ZeroMemory(&rdi, sizeof(rdi)); + rdi.cbSize = sizeof(rdi); + size = sizeof(rdi); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICEINFO, + &rdi, &size) == -1) + { + continue; + } + + if (MAKELONG(rdi.hid.dwVendorId, rdi.hid.dwProductId) != (LONG) guid->Data1) + continue; + + memset(name, 0, sizeof(name)); + size = sizeof(name); + + if ((INT) GetRawInputDeviceInfoA(ridl[i].hDevice, + RIDI_DEVICENAME, + name, &size) == -1) + { + break; + } + + name[sizeof(name) - 1] = '\0'; + if (strstr(name, "IG_")) + { + result = GLFW_TRUE; + break; + } + } + + free(ridl); + return result; +} + +// Frees all resources associated with the specified joystick +// +static void closeJoystick(_GLFWjoystickWin32* js) +{ + if (js->device) + { + IDirectInputDevice8_Unacquire(js->device); + IDirectInputDevice8_Release(js->device); + } + + free(js->name); + free(js->axes); + free(js->buttons); + free(js->objects); + memset(js, 0, sizeof(_GLFWjoystickWin32)); + + _glfwInputJoystickChange((int) (js - _glfw.win32_js), GLFW_DISCONNECTED); +} + +// DirectInput device object enumeration callback +// Insights gleaned from SDL2 +// +static BOOL CALLBACK deviceObjectCallback(const DIDEVICEOBJECTINSTANCEW* doi, + void* user) +{ + _GLFWobjenumWin32* data = user; + _GLFWjoyobjectWin32* object = data->objects + data->objectCount; + + if (DIDFT_GETTYPE(doi->dwType) & DIDFT_AXIS) + { + DIPROPRANGE dipr; + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + object->offset = DIJOFS_SLIDER(data->sliderCount); + else if (memcmp(&doi->guidType, &GUID_XAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_X; + else if (memcmp(&doi->guidType, &GUID_YAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Y; + else if (memcmp(&doi->guidType, &GUID_ZAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_Z; + else if (memcmp(&doi->guidType, &GUID_RxAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RX; + else if (memcmp(&doi->guidType, &GUID_RyAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RY; + else if (memcmp(&doi->guidType, &GUID_RzAxis, sizeof(GUID)) == 0) + object->offset = DIJOFS_RZ; + else + return DIENUM_CONTINUE; + + ZeroMemory(&dipr, sizeof(dipr)); + dipr.diph.dwSize = sizeof(dipr); + dipr.diph.dwHeaderSize = sizeof(dipr.diph); + dipr.diph.dwObj = doi->dwType; + dipr.diph.dwHow = DIPH_BYID; + dipr.lMin = -32768; + dipr.lMax = 32767; + + if (FAILED(IDirectInputDevice8_SetProperty(data->device, + DIPROP_RANGE, + &dipr.diph))) + { + return DIENUM_CONTINUE; + } + + if (memcmp(&doi->guidType, &GUID_Slider, sizeof(GUID)) == 0) + { + object->type = _GLFW_TYPE_SLIDER; + data->sliderCount++; + } + else + { + object->type = _GLFW_TYPE_AXIS; + data->axisCount++; + } + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_BUTTON) + { + object->offset = DIJOFS_BUTTON(data->buttonCount); + object->type = _GLFW_TYPE_BUTTON; + data->buttonCount++; + } + else if (DIDFT_GETTYPE(doi->dwType) & DIDFT_POV) + { + object->offset = DIJOFS_POV(data->povCount); + object->type = _GLFW_TYPE_POV; + data->povCount++; + } + + data->objectCount++; + return DIENUM_CONTINUE; +} + +// DirectInput device enumeration callback +// +static BOOL CALLBACK deviceCallback(const DIDEVICEINSTANCE* di, void* user) +{ + int jid = 0; + DIDEVCAPS dc; + DIPROPDWORD dipd; + IDirectInputDevice8* device; + _GLFWobjenumWin32 data; + _GLFWjoystickWin32* js; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (memcmp(&_glfw.win32_js[jid].guid, &di->guidInstance, sizeof(GUID)) == 0) + return DIENUM_CONTINUE; + } + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.win32_js[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return DIENUM_STOP; + + if (supportsXInput(&di->guidProduct)) + return DIENUM_CONTINUE; + + if (FAILED(IDirectInput8_CreateDevice(_glfw.win32.dinput8.api, + &di->guidInstance, + &device, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "DI: Failed to create device"); + return DIENUM_CONTINUE; + } + + if (FAILED(IDirectInputDevice8_SetDataFormat(device, &_glfwDataFormat))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to set device data format"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dc, sizeof(dc)); + dc.dwSize = sizeof(dc); + + if (FAILED(IDirectInputDevice8_GetCapabilities(device, &dc))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to query device capabilities"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + ZeroMemory(&dipd, sizeof(dipd)); + dipd.diph.dwSize = sizeof(dipd); + dipd.diph.dwHeaderSize = sizeof(dipd.diph); + dipd.diph.dwHow = DIPH_DEVICE; + dipd.dwData = DIPROPAXISMODE_ABS; + + if (FAILED(IDirectInputDevice8_SetProperty(device, + DIPROP_AXISMODE, + &dipd.diph))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to set device axis mode"); + + IDirectInputDevice8_Release(device); + return DIENUM_CONTINUE; + } + + memset(&data, 0, sizeof(data)); + data.device = device; + data.objects = calloc(dc.dwAxes + dc.dwButtons + dc.dwPOVs, + sizeof(_GLFWjoyobjectWin32)); + + if (FAILED(IDirectInputDevice8_EnumObjects(device, + deviceObjectCallback, + &data, + DIDFT_AXIS | DIDFT_BUTTON | DIDFT_POV))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to enumerate device objects"); + + IDirectInputDevice8_Release(device); + free(data.objects); + return DIENUM_CONTINUE; + } + + qsort(data.objects, data.objectCount, + sizeof(_GLFWjoyobjectWin32), + compareJoystickObjects); + + js = _glfw.win32_js + jid; + js->device = device; + js->guid = di->guidInstance; + js->axisCount = data.axisCount + data.sliderCount; + js->axes = calloc(js->axisCount, sizeof(float)); + js->buttonCount += data.buttonCount + data.povCount * 4; + js->buttons = calloc(js->buttonCount, 1); + js->objects = data.objects; + js->objectCount = data.objectCount; + js->name = _glfwCreateUTF8FromWideStringWin32(di->tszInstanceName); + js->present = GLFW_TRUE; + + _glfwInputJoystickChange(jid, GLFW_CONNECTED); + return DIENUM_CONTINUE; +} + +// Attempt to open the specified joystick device +// TODO: Pack state arrays for non-gamepad devices +// +static GLFWbool openXinputDevice(DWORD index) +{ + int jid; + XINPUT_CAPABILITIES xic; + _GLFWjoystickWin32* js; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (_glfw.win32_js[jid].present && + _glfw.win32_js[jid].device == NULL && + _glfw.win32_js[jid].index == index) + { + return GLFW_FALSE; + } + } + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (!_glfw.win32_js[jid].present) + break; + } + + if (jid > GLFW_JOYSTICK_LAST) + return GLFW_FALSE; + + if (_glfw_XInputGetCapabilities(index, 0, &xic) != ERROR_SUCCESS) + return GLFW_FALSE; + + js = _glfw.win32_js + jid; + js->axisCount = 6; + js->axes = calloc(js->axisCount, sizeof(float)); + js->buttonCount = 14; + js->buttons = calloc(js->buttonCount, 1); + js->present = GLFW_TRUE; + js->name = strdup(getDeviceDescription(&xic)); + js->index = index; + + _glfwInputJoystickChange(jid, GLFW_CONNECTED); + + return GLFW_TRUE; +} + +// Polls for and processes events the specified joystick +// +static GLFWbool pollJoystickState(_GLFWjoystickWin32* js, int mode) +{ + if (!js->present) + return GLFW_FALSE; + + if (js->device) + { + int i, j, ai = 0, bi = 0; + HRESULT result; + DIJOYSTATE state; + + IDirectInputDevice8_Poll(js->device); + result = IDirectInputDevice8_GetDeviceState(js->device, + sizeof(state), + &state); + if (result == DIERR_NOTACQUIRED || result == DIERR_INPUTLOST) + { + IDirectInputDevice8_Acquire(js->device); + IDirectInputDevice8_Poll(js->device); + result = IDirectInputDevice8_GetDeviceState(js->device, + sizeof(state), + &state); + } + + if (FAILED(result)) + { + closeJoystick(js); + return GLFW_FALSE; + } + + if (mode == _GLFW_PRESENCE_ONLY) + return GLFW_TRUE; + + for (i = 0; i < js->objectCount; i++) + { + const void* data = (char*) &state + js->objects[i].offset; + + switch (js->objects[i].type) + { + case _GLFW_TYPE_AXIS: + case _GLFW_TYPE_SLIDER: + { + js->axes[ai++] = (*((LONG*) data) + 0.5f) / 32767.5f; + break; + } + + case _GLFW_TYPE_BUTTON: + { + if (*((BYTE*) data) & 0x80) + js->buttons[bi++] = GLFW_PRESS; + else + js->buttons[bi++] = GLFW_RELEASE; + + break; + } + + case _GLFW_TYPE_POV: + { + const int directions[9] = { 1, 3, 2, 6, 4, 12, 8, 9, 0 }; + // Screams of horror are appropriate at this point + int value = LOWORD(*(DWORD*) data) / (45 * DI_DEGREES); + if (value < 0 || value > 8) + value = 8; + + for (j = 0; j < 4; j++) + { + if (directions[value] & (1 << j)) + js->buttons[bi++] = GLFW_PRESS; + else + js->buttons[bi++] = GLFW_RELEASE; + } + + break; + } + } + } + + return GLFW_TRUE; + } + else + { + int i; + DWORD result; + XINPUT_STATE xis; + const WORD buttons[14] = + { + XINPUT_GAMEPAD_A, + XINPUT_GAMEPAD_B, + XINPUT_GAMEPAD_X, + XINPUT_GAMEPAD_Y, + XINPUT_GAMEPAD_LEFT_SHOULDER, + XINPUT_GAMEPAD_RIGHT_SHOULDER, + XINPUT_GAMEPAD_BACK, + XINPUT_GAMEPAD_START, + XINPUT_GAMEPAD_LEFT_THUMB, + XINPUT_GAMEPAD_RIGHT_THUMB, + XINPUT_GAMEPAD_DPAD_UP, + XINPUT_GAMEPAD_DPAD_RIGHT, + XINPUT_GAMEPAD_DPAD_DOWN, + XINPUT_GAMEPAD_DPAD_LEFT + }; + + result = _glfw_XInputGetState(js->index, &xis); + if (result != ERROR_SUCCESS) + { + if (result == ERROR_DEVICE_NOT_CONNECTED) + closeJoystick(js); + + return GLFW_FALSE; + } + + if (mode == _GLFW_PRESENCE_ONLY) + return GLFW_TRUE; + + if (sqrt((double) (xis.Gamepad.sThumbLX * xis.Gamepad.sThumbLX + + xis.Gamepad.sThumbLY * xis.Gamepad.sThumbLY)) > + (double) XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE) + { + js->axes[0] = (xis.Gamepad.sThumbLX + 0.5f) / 32767.f; + js->axes[1] = (xis.Gamepad.sThumbLY + 0.5f) / 32767.f; + } + else + { + js->axes[0] = 0.f; + js->axes[1] = 0.f; + } + + if (sqrt((double) (xis.Gamepad.sThumbRX * xis.Gamepad.sThumbRX + + xis.Gamepad.sThumbRY * xis.Gamepad.sThumbRY)) > + (double) XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE) + { + js->axes[2] = (xis.Gamepad.sThumbRX + 0.5f) / 32767.f; + js->axes[3] = (xis.Gamepad.sThumbRY + 0.5f) / 32767.f; + } + else + { + js->axes[2] = 0.f; + js->axes[3] = 0.f; + } + + if (xis.Gamepad.bLeftTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + js->axes[4] = xis.Gamepad.bLeftTrigger / 127.5f - 1.f; + else + js->axes[4] = -1.f; + + if (xis.Gamepad.bRightTrigger > XINPUT_GAMEPAD_TRIGGER_THRESHOLD) + js->axes[5] = xis.Gamepad.bRightTrigger / 127.5f - 1.f; + else + js->axes[5] = -1.f; + + for (i = 0; i < 14; i++) + js->buttons[i] = (xis.Gamepad.wButtons & buttons[i]) ? 1 : 0; + + return GLFW_TRUE; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialize joystick interface +// +void _glfwInitJoysticksWin32(void) +{ + if (_glfw.win32.dinput8.instance) + { + if (FAILED(_glfw_DirectInput8Create(GetModuleHandle(NULL), + DIRECTINPUT_VERSION, + &IID_IDirectInput8W, + (void**) &_glfw.win32.dinput8.api, + NULL))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "DI: Failed to create interface"); + } + } + + _glfwDetectJoystickConnectionWin32(); +} + +// Close all opened joystick handles +// +void _glfwTerminateJoysticksWin32(void) +{ + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + closeJoystick(_glfw.win32_js + jid); + + if (_glfw.win32.dinput8.api) + IDirectInput8_Release(_glfw.win32.dinput8.api); +} + +// Checks for new joysticks after DBT_DEVICEARRIVAL +// +void _glfwDetectJoystickConnectionWin32(void) +{ + if (_glfw.win32.xinput.instance) + { + DWORD i; + + for (i = 0; i < XUSER_MAX_COUNT; i++) + openXinputDevice(i); + } + + if (_glfw.win32.dinput8.api) + { + if (FAILED(IDirectInput8_EnumDevices(_glfw.win32.dinput8.api, + DI8DEVCLASS_GAMECTRL, + deviceCallback, + NULL, + DIEDFL_ALLDEVICES))) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Failed to enumerate DirectInput8 devices"); + return; + } + } +} + +// Checks for joystick disconnection after DBT_DEVICEREMOVECOMPLETE +// +void _glfwDetectJoystickDisconnectionWin32(void) +{ + int jid; + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + pollJoystickState(_glfw.win32_js + jid, _GLFW_PRESENCE_ONLY); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformJoystickPresent(int jid) +{ + _GLFWjoystickWin32* js = _glfw.win32_js + jid; + return pollJoystickState(js, _GLFW_PRESENCE_ONLY); +} + +const float* _glfwPlatformGetJoystickAxes(int jid, int* count) +{ + _GLFWjoystickWin32* js = _glfw.win32_js + jid; + if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) + return NULL; + + *count = js->axisCount; + return js->axes; +} + +const unsigned char* _glfwPlatformGetJoystickButtons(int jid, int* count) +{ + _GLFWjoystickWin32* js = _glfw.win32_js + jid; + if (!pollJoystickState(js, _GLFW_UPDATE_STATE)) + return NULL; + + *count = js->buttonCount; + return js->buttons; +} + +const char* _glfwPlatformGetJoystickName(int jid) +{ + _GLFWjoystickWin32* js = _glfw.win32_js + jid; + if (!pollJoystickState(js, _GLFW_PRESENCE_ONLY)) + return NULL; + + return js->name; +} + diff --git a/apps/exampleViewer/common/glfw/src/win32_joystick.h b/apps/exampleViewer/common/glfw/src/win32_joystick.h new file mode 100644 index 0000000000..44ce9531b6 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_joystick.h @@ -0,0 +1,64 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_win32_joystick_h_ +#define _glfw3_win32_joystick_h_ + +#define _GLFW_PLATFORM_LIBRARY_JOYSTICK_STATE \ + _GLFWjoystickWin32 win32_js[GLFW_JOYSTICK_LAST + 1] + +// Joystick element (axis, button or slider) +// +typedef struct _GLFWjoyobjectWin32 +{ + int offset; + int type; +} _GLFWjoyobjectWin32; + +// Win32-specific per-joystick data +// +typedef struct _GLFWjoystickWin32 +{ + GLFWbool present; + float* axes; + int axisCount; + unsigned char* buttons; + int buttonCount; + _GLFWjoyobjectWin32* objects; + int objectCount; + char* name; + IDirectInputDevice8W* device; + DWORD index; + GUID guid; +} _GLFWjoystickWin32; + + +void _glfwInitJoysticksWin32(void); +void _glfwTerminateJoysticksWin32(void); +void _glfwDetectJoystickConnectionWin32(void); +void _glfwDetectJoystickDisconnectionWin32(void); + +#endif // _glfw3_win32_joystick_h_ diff --git a/apps/exampleViewer/common/glfw/src/win32_monitor.c b/apps/exampleViewer/common/glfw/src/win32_monitor.c new file mode 100644 index 0000000000..dc29f7c7cd --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_monitor.c @@ -0,0 +1,401 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +// Create monitor from an adapter and (optionally) a display +// +static _GLFWmonitor* createMonitor(DISPLAY_DEVICEW* adapter, + DISPLAY_DEVICEW* display) +{ + _GLFWmonitor* monitor; + char* name; + HDC dc; + + if (display) + name = _glfwCreateUTF8FromWideStringWin32(display->DeviceString); + else + name = _glfwCreateUTF8FromWideStringWin32(adapter->DeviceString); + if (!name) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert string to UTF-8"); + return NULL; + } + + dc = CreateDCW(L"DISPLAY", adapter->DeviceName, NULL, NULL); + + monitor = _glfwAllocMonitor(name, + GetDeviceCaps(dc, HORZSIZE), + GetDeviceCaps(dc, VERTSIZE)); + + DeleteDC(dc); + free(name); + + if (adapter->StateFlags & DISPLAY_DEVICE_MODESPRUNED) + monitor->win32.modesPruned = GLFW_TRUE; + + wcscpy(monitor->win32.adapterName, adapter->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + adapter->DeviceName, -1, + monitor->win32.publicAdapterName, + sizeof(monitor->win32.publicAdapterName), + NULL, NULL); + + if (display) + { + wcscpy(monitor->win32.displayName, display->DeviceName); + WideCharToMultiByte(CP_UTF8, 0, + display->DeviceName, -1, + monitor->win32.publicDisplayName, + sizeof(monitor->win32.publicDisplayName), + NULL, NULL); + } + + return monitor; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Change the current video mode +// +GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + GLFWvidmode current; + const GLFWvidmode* best; + DEVMODEW dm; + + best = _glfwChooseVideoMode(monitor, desired); + _glfwPlatformGetVideoMode(monitor, ¤t); + if (_glfwCompareVideoModes(¤t, best) == 0) + return GLFW_TRUE; + + ZeroMemory(&dm, sizeof(dm)); + dm.dmSize = sizeof(DEVMODEW); + dm.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL | + DM_DISPLAYFREQUENCY; + dm.dmPelsWidth = best->width; + dm.dmPelsHeight = best->height; + dm.dmBitsPerPel = best->redBits + best->greenBits + best->blueBits; + dm.dmDisplayFrequency = best->refreshRate; + + if (dm.dmBitsPerPel < 15 || dm.dmBitsPerPel >= 24) + dm.dmBitsPerPel = 32; + + if (ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_FULLSCREEN, + NULL) != DISP_CHANGE_SUCCESSFUL) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to set video mode"); + return GLFW_FALSE; + } + + monitor->win32.modeChanged = GLFW_TRUE; + return GLFW_TRUE; +} + +// Restore the previously saved (original) video mode +// +void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor) +{ + if (monitor->win32.modeChanged) + { + ChangeDisplaySettingsExW(monitor->win32.adapterName, + NULL, NULL, CDS_FULLSCREEN, NULL); + monitor->win32.modeChanged = GLFW_FALSE; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + int found = 0; + DWORD adapterIndex, displayIndex, primaryIndex = 0; + DISPLAY_DEVICEW adapter, display; + GLFWbool hasDisplays = GLFW_FALSE; + _GLFWmonitor** monitors = NULL; + + *count = 0; + + // HACK: Check if any active adapters have connected displays + // If not, this is a headless system or a VMware guest + + for (adapterIndex = 0; ; adapterIndex++) + { + ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); + adapter.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); + display.cb = sizeof(DISPLAY_DEVICEW); + + if (EnumDisplayDevicesW(adapter.DeviceName, 0, &display, 0)) + { + hasDisplays = GLFW_TRUE; + break; + } + } + + for (adapterIndex = 0; ; adapterIndex++) + { + ZeroMemory(&adapter, sizeof(DISPLAY_DEVICEW)); + adapter.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(NULL, adapterIndex, &adapter, 0)) + break; + + if (!(adapter.StateFlags & DISPLAY_DEVICE_ACTIVE)) + continue; + + if (adapter.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE) + primaryIndex = found; + + if (hasDisplays) + { + for (displayIndex = 0; ; displayIndex++) + { + ZeroMemory(&display, sizeof(DISPLAY_DEVICEW)); + display.cb = sizeof(DISPLAY_DEVICEW); + + if (!EnumDisplayDevicesW(adapter.DeviceName, displayIndex, &display, 0)) + break; + + found++; + monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); + monitors[found - 1] = createMonitor(&adapter, &display); + } + } + else + { + found++; + monitors = realloc(monitors, sizeof(_GLFWmonitor*) * found); + monitors[found - 1] = createMonitor(&adapter, NULL); + } + } + + _GLFW_SWAP_POINTERS(monitors[0], monitors[primaryIndex]); + + *count = found; + return monitors; +} + +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + if (wcslen(first->win32.displayName)) + return wcscmp(first->win32.displayName, second->win32.displayName) == 0; + else + return wcscmp(first->win32.adapterName, second->win32.adapterName) == 0; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + DEVMODEW settings; + ZeroMemory(&settings, sizeof(DEVMODEW)); + settings.dmSize = sizeof(DEVMODEW); + + EnumDisplaySettingsExW(monitor->win32.adapterName, + ENUM_CURRENT_SETTINGS, + &settings, + EDS_ROTATEDMODE); + + if (xpos) + *xpos = settings.dmPosition.x; + if (ypos) + *ypos = settings.dmPosition.y; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + int modeIndex = 0, size = 0; + GLFWvidmode* result = NULL; + + *count = 0; + + for (;;) + { + int i; + GLFWvidmode mode; + DEVMODEW dm; + + ZeroMemory(&dm, sizeof(DEVMODEW)); + dm.dmSize = sizeof(DEVMODEW); + + if (!EnumDisplaySettingsW(monitor->win32.adapterName, modeIndex, &dm)) + break; + + modeIndex++; + + // Skip modes with less than 15 BPP + if (dm.dmBitsPerPel < 15) + continue; + + mode.width = dm.dmPelsWidth; + mode.height = dm.dmPelsHeight; + mode.refreshRate = dm.dmDisplayFrequency; + _glfwSplitBPP(dm.dmBitsPerPel, + &mode.redBits, + &mode.greenBits, + &mode.blueBits); + + for (i = 0; i < *count; i++) + { + if (_glfwCompareVideoModes(result + i, &mode) == 0) + break; + } + + // Skip duplicate modes + if (i < *count) + continue; + + if (monitor->win32.modesPruned) + { + // Skip modes not supported by the connected displays + if (ChangeDisplaySettingsExW(monitor->win32.adapterName, + &dm, + NULL, + CDS_TEST, + NULL) != DISP_CHANGE_SUCCESSFUL) + { + continue; + } + } + + if (*count == size) + { + size += 128; + result = (GLFWvidmode*) realloc(result, size * sizeof(GLFWvidmode)); + } + + (*count)++; + result[*count - 1] = mode; + } + + if (!*count) + { + // HACK: Report the current mode if no valid modes were found + result = calloc(1, sizeof(GLFWvidmode)); + _glfwPlatformGetVideoMode(monitor, result); + *count = 1; + } + + return result; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + DEVMODEW dm; + + ZeroMemory(&dm, sizeof(DEVMODEW)); + dm.dmSize = sizeof(DEVMODEW); + + EnumDisplaySettingsW(monitor->win32.adapterName, ENUM_CURRENT_SETTINGS, &dm); + + mode->width = dm.dmPelsWidth; + mode->height = dm.dmPelsHeight; + mode->refreshRate = dm.dmDisplayFrequency; + _glfwSplitBPP(dm.dmBitsPerPel, + &mode->redBits, + &mode->greenBits, + &mode->blueBits); +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + HDC dc; + WORD values[768]; + + dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); + GetDeviceGammaRamp(dc, values); + DeleteDC(dc); + + _glfwAllocGammaArrays(ramp, 256); + + memcpy(ramp->red, values + 0, 256 * sizeof(unsigned short)); + memcpy(ramp->green, values + 256, 256 * sizeof(unsigned short)); + memcpy(ramp->blue, values + 512, 256 * sizeof(unsigned short)); +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + HDC dc; + WORD values[768]; + + if (ramp->size != 256) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Gamma ramp size must be 256"); + return; + } + + memcpy(values + 0, ramp->red, 256 * sizeof(unsigned short)); + memcpy(values + 256, ramp->green, 256 * sizeof(unsigned short)); + memcpy(values + 512, ramp->blue, 256 * sizeof(unsigned short)); + + dc = CreateDCW(L"DISPLAY", monitor->win32.adapterName, NULL, NULL); + SetDeviceGammaRamp(dc, values); + DeleteDC(dc); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->win32.publicAdapterName; +} + +GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->win32.publicDisplayName; +} + diff --git a/apps/exampleViewer/common/glfw/src/win32_platform.h b/apps/exampleViewer/common/glfw/src/win32_platform.h new file mode 100644 index 0000000000..162ea4dd38 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_platform.h @@ -0,0 +1,351 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_win32_platform_h_ +#define _glfw3_win32_platform_h_ + +// We don't need all the fancy stuff +#ifndef NOMINMAX + #define NOMINMAX +#endif + +#ifndef VC_EXTRALEAN + #define VC_EXTRALEAN +#endif + +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif + +// This is a workaround for the fact that glfw3.h needs to export APIENTRY (for +// example to allow applications to correctly declare a GL_ARB_debug_output +// callback) but windows.h assumes no one will define APIENTRY before it does +#undef APIENTRY + +// GLFW on Windows is Unicode only and does not work in MBCS mode +#ifndef UNICODE + #define UNICODE +#endif + +// GLFW requires Windows XP or later +#if WINVER < 0x0501 + #undef WINVER + #define WINVER 0x0501 +#endif +#if _WIN32_WINNT < 0x0501 + #undef _WIN32_WINNT + #define _WIN32_WINNT 0x0501 +#endif + +// GLFW uses DirectInput8 interfaces +#define DIRECTINPUT_VERSION 0x0800 + +#include +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) + #include + #define strdup _strdup +#endif + +// HACK: Define macros that some windows.h variants don't +#ifndef WM_MOUSEHWHEEL + #define WM_MOUSEHWHEEL 0x020E +#endif +#ifndef WM_DWMCOMPOSITIONCHANGED + #define WM_DWMCOMPOSITIONCHANGED 0x031E +#endif +#ifndef WM_COPYGLOBALDATA + #define WM_COPYGLOBALDATA 0x0049 +#endif +#ifndef WM_UNICHAR + #define WM_UNICHAR 0x0109 +#endif +#ifndef UNICODE_NOCHAR + #define UNICODE_NOCHAR 0xFFFF +#endif +#ifndef WM_DPICHANGED + #define WM_DPICHANGED 0x02E0 +#endif +#ifndef GET_XBUTTON_WPARAM + #define GET_XBUTTON_WPARAM(w) (HIWORD(w)) +#endif +#ifndef EDS_ROTATEDMODE + #define EDS_ROTATEDMODE 0x00000004 +#endif +#ifndef DISPLAY_DEVICE_ACTIVE + #define DISPLAY_DEVICE_ACTIVE 0x00000001 +#endif + +#if WINVER < 0x0601 +typedef struct tagCHANGEFILTERSTRUCT +{ + DWORD cbSize; + DWORD ExtStatus; + +} CHANGEFILTERSTRUCT, *PCHANGEFILTERSTRUCT; +#ifndef MSGFLT_ALLOW + #define MSGFLT_ALLOW 1 +#endif +#endif /*Windows 7*/ + +#ifndef DPI_ENUMS_DECLARED +typedef enum PROCESS_DPI_AWARENESS +{ + PROCESS_DPI_UNAWARE = 0, + PROCESS_SYSTEM_DPI_AWARE = 1, + PROCESS_PER_MONITOR_DPI_AWARE = 2 +} PROCESS_DPI_AWARENESS; +#endif /*DPI_ENUMS_DECLARED*/ + +// HACK: Define macros that some xinput.h variants don't +#ifndef XINPUT_CAPS_WIRELESS + #define XINPUT_CAPS_WIRELESS 0x0002 +#endif +#ifndef XINPUT_DEVSUBTYPE_WHEEL + #define XINPUT_DEVSUBTYPE_WHEEL 0x02 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_STICK + #define XINPUT_DEVSUBTYPE_ARCADE_STICK 0x03 +#endif +#ifndef XINPUT_DEVSUBTYPE_FLIGHT_STICK + #define XINPUT_DEVSUBTYPE_FLIGHT_STICK 0x04 +#endif +#ifndef XINPUT_DEVSUBTYPE_DANCE_PAD + #define XINPUT_DEVSUBTYPE_DANCE_PAD 0x05 +#endif +#ifndef XINPUT_DEVSUBTYPE_GUITAR + #define XINPUT_DEVSUBTYPE_GUITAR 0x06 +#endif +#ifndef XINPUT_DEVSUBTYPE_DRUM_KIT + #define XINPUT_DEVSUBTYPE_DRUM_KIT 0x08 +#endif +#ifndef XINPUT_DEVSUBTYPE_ARCADE_PAD + #define XINPUT_DEVSUBTYPE_ARCADE_PAD 0x13 +#endif +#ifndef XUSER_MAX_COUNT + #define XUSER_MAX_COUNT 4 +#endif + +// HACK: Define macros that some dinput.h variants don't +#ifndef DIDFT_OPTIONAL + #define DIDFT_OPTIONAL 0x80000000 +#endif + +// winmm.dll function pointer typedefs +typedef DWORD (WINAPI * TIMEGETTIME_T)(void); +#define _glfw_timeGetTime _glfw.win32.winmm.timeGetTime + +// xinput.dll function pointer typedefs +typedef DWORD (WINAPI * XINPUTGETCAPABILITIES_T)(DWORD,DWORD,XINPUT_CAPABILITIES*); +typedef DWORD (WINAPI * XINPUTGETSTATE_T)(DWORD,XINPUT_STATE*); +#define _glfw_XInputGetCapabilities _glfw.win32.xinput.XInputGetCapabilities +#define _glfw_XInputGetState _glfw.win32.xinput.XInputGetState + +// dinput8.dll function pointer typedefs +typedef HRESULT (WINAPI * DIRECTINPUT8CREATE_T)(HINSTANCE,DWORD,REFIID,LPVOID*,LPUNKNOWN); +#define _glfw_DirectInput8Create _glfw.win32.dinput8.DirectInput8Create + +// user32.dll function pointer typedefs +typedef BOOL (WINAPI * SETPROCESSDPIAWARE_T)(void); +typedef BOOL (WINAPI * CHANGEWINDOWMESSAGEFILTEREX_T)(HWND,UINT,DWORD,PCHANGEFILTERSTRUCT); +#define _glfw_SetProcessDPIAware _glfw.win32.user32.SetProcessDPIAware +#define _glfw_ChangeWindowMessageFilterEx _glfw.win32.user32.ChangeWindowMessageFilterEx + +// dwmapi.dll function pointer typedefs +typedef HRESULT (WINAPI * DWMISCOMPOSITIONENABLED_T)(BOOL*); +typedef HRESULT (WINAPI * DWMFLUSH_T)(VOID); +#define _glfw_DwmIsCompositionEnabled _glfw.win32.dwmapi.DwmIsCompositionEnabled +#define _glfw_DwmFlush _glfw.win32.dwmapi.DwmFlush + +// shcore.dll function pointer typedefs +typedef HRESULT (WINAPI * SETPROCESSDPIAWARENESS_T)(PROCESS_DPI_AWARENESS); +#define _glfw_SetProcessDpiAwareness _glfw.win32.shcore.SetProcessDpiAwareness + +typedef VkFlags VkWin32SurfaceCreateFlagsKHR; + +typedef struct VkWin32SurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkWin32SurfaceCreateFlagsKHR flags; + HINSTANCE hinstance; + HWND hwnd; +} VkWin32SurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateWin32SurfaceKHR)(VkInstance,const VkWin32SurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR)(VkPhysicalDevice,uint32_t); + +#include "win32_joystick.h" +#include "wgl_context.h" +#include "egl_context.h" + +#define _GLFW_WNDCLASSNAME L"GLFW30" + +#define _glfw_dlopen(name) LoadLibraryA(name) +#define _glfw_dlclose(handle) FreeLibrary((HMODULE) handle) +#define _glfw_dlsym(handle, name) GetProcAddress((HMODULE) handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->win32.handle) +#define _GLFW_EGL_NATIVE_DISPLAY EGL_DEFAULT_DISPLAY + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWin32 win32 +#define _GLFW_PLATFORM_LIBRARY_TIME_STATE _GLFWtimeWin32 win32_time +#define _GLFW_PLATFORM_LIBRARY_TLS_STATE _GLFWtlsWin32 win32_tls +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWin32 win32 +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWin32 win32 + + +// Win32-specific per-window data +// +typedef struct _GLFWwindowWin32 +{ + HWND handle; + HICON bigIcon; + HICON smallIcon; + + GLFWbool cursorTracked; + GLFWbool iconified; + GLFWbool maximized; + + // The last received cursor position, regardless of source + int lastCursorPosX, lastCursorPosY; + +} _GLFWwindowWin32; + +// Win32-specific global data +// +typedef struct _GLFWlibraryWin32 +{ + HWND helperWindowHandle; + DWORD foregroundLockTimeout; + char* clipboardString; + char keyName[64]; + short int keycodes[512]; + short int scancodes[GLFW_KEY_LAST + 1]; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + + struct { + HINSTANCE instance; + TIMEGETTIME_T timeGetTime; + } winmm; + + struct { + HINSTANCE instance; + DIRECTINPUT8CREATE_T DirectInput8Create; + IDirectInput8W* api; + } dinput8; + + struct { + HINSTANCE instance; + XINPUTGETCAPABILITIES_T XInputGetCapabilities; + XINPUTGETSTATE_T XInputGetState; + } xinput; + + struct { + HINSTANCE instance; + SETPROCESSDPIAWARE_T SetProcessDPIAware; + CHANGEWINDOWMESSAGEFILTEREX_T ChangeWindowMessageFilterEx; + } user32; + + struct { + HINSTANCE instance; + DWMISCOMPOSITIONENABLED_T DwmIsCompositionEnabled; + DWMFLUSH_T DwmFlush; + } dwmapi; + + struct { + HINSTANCE instance; + SETPROCESSDPIAWARENESS_T SetProcessDpiAwareness; + } shcore; + +} _GLFWlibraryWin32; + +// Win32-specific per-monitor data +// +typedef struct _GLFWmonitorWin32 +{ + // This size matches the static size of DISPLAY_DEVICE.DeviceName + WCHAR adapterName[32]; + WCHAR displayName[32]; + char publicAdapterName[64]; + char publicDisplayName[64]; + GLFWbool modesPruned; + GLFWbool modeChanged; + +} _GLFWmonitorWin32; + +// Win32-specific per-cursor data +// +typedef struct _GLFWcursorWin32 +{ + HCURSOR handle; + +} _GLFWcursorWin32; + +// Win32-specific global timer data +// +typedef struct _GLFWtimeWin32 +{ + GLFWbool hasPC; + uint64_t frequency; + +} _GLFWtimeWin32; + +// Win32-specific global TLS data +// +typedef struct _GLFWtlsWin32 +{ + GLFWbool allocated; + DWORD context; + +} _GLFWtlsWin32; + + +GLFWbool _glfwRegisterWindowClassWin32(void); +void _glfwUnregisterWindowClassWin32(void); + +GLFWbool _glfwInitThreadLocalStorageWin32(void); +void _glfwTerminateThreadLocalStorageWin32(void); + +WCHAR* _glfwCreateWideStringFromUTF8Win32(const char* source); +char* _glfwCreateUTF8FromWideStringWin32(const WCHAR* source); + +void _glfwInitTimerWin32(void); + +GLFWbool _glfwSetVideoModeWin32(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeWin32(_GLFWmonitor* monitor); + +#endif // _glfw3_win32_platform_h_ diff --git a/apps/exampleViewer/common/glfw/src/win32_time.c b/apps/exampleViewer/common/glfw/src/win32_time.c new file mode 100644 index 0000000000..add55c5f9f --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_time.c @@ -0,0 +1,74 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Initialise timer +// +void _glfwInitTimerWin32(void) +{ + uint64_t frequency; + + if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) + { + _glfw.win32_time.hasPC = GLFW_TRUE; + _glfw.win32_time.frequency = frequency; + } + else + { + _glfw.win32_time.hasPC = GLFW_FALSE; + _glfw.win32_time.frequency = 1000; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +uint64_t _glfwPlatformGetTimerValue(void) +{ + if (_glfw.win32_time.hasPC) + { + uint64_t value; + QueryPerformanceCounter((LARGE_INTEGER*) &value); + return value; + } + else + return (uint64_t) _glfw_timeGetTime(); +} + +uint64_t _glfwPlatformGetTimerFrequency(void) +{ + return _glfw.win32_time.frequency; +} + diff --git a/apps/exampleViewer/common/glfw/src/win32_tls.c b/apps/exampleViewer/common/glfw/src/win32_tls.c new file mode 100644 index 0000000000..0607ca6cfe --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_tls.c @@ -0,0 +1,69 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWbool _glfwInitThreadLocalStorageWin32(void) +{ + _glfw.win32_tls.context = TlsAlloc(); + if (_glfw.win32_tls.context == TLS_OUT_OF_INDEXES) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate TLS index"); + return GLFW_FALSE; + } + + _glfw.win32_tls.allocated = GLFW_TRUE; + return GLFW_TRUE; +} + +void _glfwTerminateThreadLocalStorageWin32(void) +{ + if (_glfw.win32_tls.allocated) + TlsFree(_glfw.win32_tls.context); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwPlatformSetCurrentContext(_GLFWwindow* context) +{ + TlsSetValue(_glfw.win32_tls.context, context); +} + +_GLFWwindow* _glfwPlatformGetCurrentContext(void) +{ + return TlsGetValue(_glfw.win32_tls.context); +} + diff --git a/apps/exampleViewer/common/glfw/src/win32_window.c b/apps/exampleViewer/common/glfw/src/win32_window.c new file mode 100644 index 0000000000..c88f343d58 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/win32_window.c @@ -0,0 +1,1719 @@ +//======================================================================== +// GLFW 3.3 Win32 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include + +#define _GLFW_KEY_INVALID -2 + +// Returns the window style for the specified window +// +static DWORD getWindowStyle(const _GLFWwindow* window) +{ + DWORD style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN; + + if (window->monitor) + style |= WS_POPUP; + else + { + style |= WS_SYSMENU | WS_MINIMIZEBOX; + + if (window->decorated) + { + style |= WS_CAPTION; + + if (window->resizable) + style |= WS_MAXIMIZEBOX | WS_THICKFRAME; + } + else + style |= WS_POPUP; + } + + return style; +} + +// Returns the extended window style for the specified window +// +static DWORD getWindowExStyle(const _GLFWwindow* window) +{ + DWORD style = WS_EX_APPWINDOW; + + if (window->monitor || window->floating) + style |= WS_EX_TOPMOST; + + return style; +} + +// Returns the image whose area most closely matches the desired one +// +static const GLFWimage* chooseImage(int count, const GLFWimage* images, + int width, int height) +{ + int i, leastDiff = INT_MAX; + const GLFWimage* closest = NULL; + + for (i = 0; i < count; i++) + { + const int currDiff = abs(images[i].width * images[i].height - + width * height); + if (currDiff < leastDiff) + { + closest = images + i; + leastDiff = currDiff; + } + } + + return closest; +} + +// Creates an RGBA icon or cursor +// +static HICON createIcon(const GLFWimage* image, + int xhot, int yhot, GLFWbool icon) +{ + int i; + HDC dc; + HICON handle; + HBITMAP color, mask; + BITMAPV5HEADER bi; + ICONINFO ii; + unsigned char* target = NULL; + unsigned char* source = image->pixels; + + ZeroMemory(&bi, sizeof(bi)); + bi.bV5Size = sizeof(BITMAPV5HEADER); + bi.bV5Width = image->width; + bi.bV5Height = -image->height; + bi.bV5Planes = 1; + bi.bV5BitCount = 32; + bi.bV5Compression = BI_BITFIELDS; + bi.bV5RedMask = 0x00ff0000; + bi.bV5GreenMask = 0x0000ff00; + bi.bV5BlueMask = 0x000000ff; + bi.bV5AlphaMask = 0xff000000; + + dc = GetDC(NULL); + color = CreateDIBSection(dc, + (BITMAPINFO*) &bi, + DIB_RGB_COLORS, + (void**) &target, + NULL, + (DWORD) 0); + ReleaseDC(NULL, dc); + + if (!color) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create RGBA bitmap"); + return NULL; + } + + mask = CreateBitmap(image->width, image->height, 1, 1, NULL); + if (!mask) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create mask bitmap"); + DeleteObject(color); + return NULL; + } + + for (i = 0; i < image->width * image->height; i++) + { + target[0] = source[2]; + target[1] = source[1]; + target[2] = source[0]; + target[3] = source[3]; + target += 4; + source += 4; + } + + ZeroMemory(&ii, sizeof(ii)); + ii.fIcon = icon; + ii.xHotspot = xhot; + ii.yHotspot = yhot; + ii.hbmMask = mask; + ii.hbmColor = color; + + handle = CreateIconIndirect(&ii); + + DeleteObject(color); + DeleteObject(mask); + + if (!handle) + { + if (icon) + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create icon"); + else + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create cursor"); + } + + return handle; +} + +// Translate client window size to full window size according to styles +// +static void getFullWindowSize(DWORD style, DWORD exStyle, + int clientWidth, int clientHeight, + int* fullWidth, int* fullHeight) +{ + RECT rect = { 0, 0, clientWidth, clientHeight }; + AdjustWindowRectEx(&rect, style, FALSE, exStyle); + *fullWidth = rect.right - rect.left; + *fullHeight = rect.bottom - rect.top; +} + +// Enforce the client rect aspect ratio based on which edge is being dragged +// +static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) +{ + int xoff, yoff; + const float ratio = (float) window->numer / (float) window->denom; + + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + 0, 0, &xoff, &yoff); + + if (edge == WMSZ_LEFT || edge == WMSZ_BOTTOMLEFT || + edge == WMSZ_RIGHT || edge == WMSZ_BOTTOMRIGHT) + { + area->bottom = area->top + yoff + + (int) ((area->right - area->left - xoff) / ratio); + } + else if (edge == WMSZ_TOPLEFT || edge == WMSZ_TOPRIGHT) + { + area->top = area->bottom - yoff - + (int) ((area->right - area->left - xoff) / ratio); + } + else if (edge == WMSZ_TOP || edge == WMSZ_BOTTOM) + { + area->right = area->left + xoff + + (int) ((area->bottom - area->top - yoff) * ratio); + } +} + +// Centers the cursor over the window client area +// +static void centerCursor(_GLFWwindow* window) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); +} + +// Returns whether the cursor is in the client area of the specified window +// +static GLFWbool cursorInClientArea(_GLFWwindow* window) +{ + RECT area; + POINT pos; + + if (!GetCursorPos(&pos)) + return GLFW_FALSE; + + if (WindowFromPoint(pos) != window->win32.handle) + return GLFW_FALSE; + + GetClientRect(window->win32.handle, &area); + ClientToScreen(window->win32.handle, (POINT*) &area.left); + ClientToScreen(window->win32.handle, (POINT*) &area.right); + + return PtInRect(&area, pos); +} + +// Updates the cursor image according to its cursor mode +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (window->cursor) + SetCursor(window->cursor->win32.handle); + else + SetCursor(LoadCursorW(NULL, IDC_ARROW)); + } + else + SetCursor(NULL); +} + +// Updates the cursor clip rect +// +static void updateClipRect(_GLFWwindow* window) +{ + if (window) + { + RECT clipRect; + GetClientRect(window->win32.handle, &clipRect); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.left); + ClientToScreen(window->win32.handle, (POINT*) &clipRect.right); + ClipCursor(&clipRect); + } + else + ClipCursor(NULL); +} + +// Translates a GLFW standard cursor to a resource ID +// +static LPWSTR translateCursorShape(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return IDC_ARROW; + case GLFW_IBEAM_CURSOR: + return IDC_IBEAM; + case GLFW_CROSSHAIR_CURSOR: + return IDC_CROSS; + case GLFW_HAND_CURSOR: + return IDC_HAND; + case GLFW_HRESIZE_CURSOR: + return IDC_SIZEWE; + case GLFW_VRESIZE_CURSOR: + return IDC_SIZENS; + } + + return NULL; +} + +// Retrieves and translates modifier keys +// +static int getKeyMods(void) +{ + int mods = 0; + + if (GetKeyState(VK_SHIFT) & (1 << 31)) + mods |= GLFW_MOD_SHIFT; + if (GetKeyState(VK_CONTROL) & (1 << 31)) + mods |= GLFW_MOD_CONTROL; + if (GetKeyState(VK_MENU) & (1 << 31)) + mods |= GLFW_MOD_ALT; + if ((GetKeyState(VK_LWIN) | GetKeyState(VK_RWIN)) & (1 << 31)) + mods |= GLFW_MOD_SUPER; + + return mods; +} + +// Retrieves and translates modifier keys +// +static int getAsyncKeyMods(void) +{ + int mods = 0; + + if (GetAsyncKeyState(VK_SHIFT) & (1 << 31)) + mods |= GLFW_MOD_SHIFT; + if (GetAsyncKeyState(VK_CONTROL) & (1 << 31)) + mods |= GLFW_MOD_CONTROL; + if (GetAsyncKeyState(VK_MENU) & (1 << 31)) + mods |= GLFW_MOD_ALT; + if ((GetAsyncKeyState(VK_LWIN) | GetAsyncKeyState(VK_RWIN)) & (1 << 31)) + mods |= GLFW_MOD_SUPER; + + return mods; +} + +// Translates a Windows key to the corresponding GLFW key +// +static int translateKey(WPARAM wParam, LPARAM lParam) +{ + if (wParam == VK_CONTROL) + { + // The CTRL keys require special handling + + MSG next; + DWORD time; + + // Is this an extended key (i.e. right key)? + if (lParam & 0x01000000) + return GLFW_KEY_RIGHT_CONTROL; + + // Here is a trick: "Alt Gr" sends LCTRL, then RALT. We only + // want the RALT message, so we try to see if the next message + // is a RALT message. In that case, this is a false LCTRL! + time = GetMessageTime(); + + if (PeekMessageW(&next, NULL, 0, 0, PM_NOREMOVE)) + { + if (next.message == WM_KEYDOWN || + next.message == WM_SYSKEYDOWN || + next.message == WM_KEYUP || + next.message == WM_SYSKEYUP) + { + if (next.wParam == VK_MENU && + (next.lParam & 0x01000000) && + next.time == time) + { + // Next message is a RALT down message, which + // means that this is not a proper LCTRL message + return _GLFW_KEY_INVALID; + } + } + } + + return GLFW_KEY_LEFT_CONTROL; + } + + if (wParam == VK_PROCESSKEY) + { + // IME notifies that keys have been filtered by setting the virtual + // key-code to VK_PROCESSKEY + return _GLFW_KEY_INVALID; + } + + return _glfw.win32.keycodes[HIWORD(lParam) & 0x1FF]; +} + +// Make the specified window and its video mode active on its monitor +// +static GLFWbool acquireMonitor(_GLFWwindow* window) +{ + GLFWvidmode mode; + GLFWbool status; + int xpos, ypos; + + status = _glfwSetVideoModeWin32(window->monitor, &window->videoMode); + + _glfwPlatformGetVideoMode(window->monitor, &mode); + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + + SetWindowPos(window->win32.handle, HWND_TOPMOST, + xpos, ypos, mode.width, mode.height, + SWP_NOACTIVATE | SWP_NOCOPYBITS); + + _glfwInputMonitorWindowChange(window->monitor, window); + return status; +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwRestoreVideoModeWin32(window->monitor); +} + +// Window callback function (handles window messages) +// +static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, + WPARAM wParam, LPARAM lParam) +{ + _GLFWwindow* window = GetPropW(hWnd, L"GLFW"); + if (!window) + { + // This is the message handling for the hidden helper window + + switch (uMsg) + { + case WM_DEVICECHANGE: + { + if (wParam == DBT_DEVNODES_CHANGED) + { + _glfwInputMonitorChange(); + return TRUE; + } + else if (wParam == DBT_DEVICEARRIVAL) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickConnectionWin32(); + } + } + else if (wParam == DBT_DEVICEREMOVECOMPLETE) + { + DEV_BROADCAST_HDR* dbh = (DEV_BROADCAST_HDR*) lParam; + if (dbh) + { + if (dbh->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) + _glfwDetectJoystickDisconnectionWin32(); + } + } + + break; + } + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); + } + + switch (uMsg) + { + case WM_SETFOCUS: + { + _glfwInputWindowFocus(window, GLFW_TRUE); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + + return 0; + } + + case WM_KILLFOCUS: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); + return 0; + } + + case WM_SYSCOMMAND: + { + switch (wParam & 0xfff0) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + { + if (window->monitor) + { + // We are running in full screen mode, so disallow + // screen saver and screen blanking + return 0; + } + else + break; + } + + // User trying to access application menu using ALT? + case SC_KEYMENU: + return 0; + } + break; + } + + case WM_CLOSE: + { + _glfwInputWindowCloseRequest(window); + return 0; + } + + case WM_CHAR: + case WM_SYSCHAR: + case WM_UNICHAR: + { + const GLFWbool plain = (uMsg != WM_SYSCHAR); + + if (uMsg == WM_UNICHAR && wParam == UNICODE_NOCHAR) + { + // WM_UNICHAR is not sent by Windows, but is sent by some + // third-party input method engine + // Returning TRUE here announces support for this message + return TRUE; + } + + _glfwInputChar(window, (unsigned int) wParam, getKeyMods(), plain); + return 0; + } + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + { + const int key = translateKey(wParam, lParam); + const int scancode = (lParam >> 16) & 0x1ff; + const int action = ((lParam >> 31) & 1) ? GLFW_RELEASE : GLFW_PRESS; + const int mods = getKeyMods(); + + if (key == _GLFW_KEY_INVALID) + break; + + if (action == GLFW_RELEASE && wParam == VK_SHIFT) + { + // Release both Shift keys on Shift up event, as only one event + // is sent even if both keys are released + _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, scancode, action, mods); + _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, scancode, action, mods); + } + else if (wParam == VK_SNAPSHOT) + { + // Key down is not reported for the Print Screen key + _glfwInputKey(window, key, scancode, GLFW_PRESS, mods); + _glfwInputKey(window, key, scancode, GLFW_RELEASE, mods); + } + else + _glfwInputKey(window, key, scancode, action, mods); + + break; + } + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_XBUTTONDOWN: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_XBUTTONUP: + { + int button, action; + + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP) + button = GLFW_MOUSE_BUTTON_LEFT; + else if (uMsg == WM_RBUTTONDOWN || uMsg == WM_RBUTTONUP) + button = GLFW_MOUSE_BUTTON_RIGHT; + else if (uMsg == WM_MBUTTONDOWN || uMsg == WM_MBUTTONUP) + button = GLFW_MOUSE_BUTTON_MIDDLE; + else if (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) + button = GLFW_MOUSE_BUTTON_4; + else + button = GLFW_MOUSE_BUTTON_5; + + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_RBUTTONDOWN || + uMsg == WM_MBUTTONDOWN || uMsg == WM_XBUTTONDOWN) + { + action = GLFW_PRESS; + SetCapture(hWnd); + } + else + { + action = GLFW_RELEASE; + ReleaseCapture(); + } + + _glfwInputMouseClick(window, button, action, getKeyMods()); + + if (uMsg == WM_XBUTTONDOWN || uMsg == WM_XBUTTONUP) + return TRUE; + + return 0; + } + + case WM_MOUSEMOVE: + { + const int x = GET_X_LPARAM(lParam); + const int y = GET_Y_LPARAM(lParam); + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + const int dx = x - window->win32.lastCursorPosX; + const int dy = y - window->win32.lastCursorPosY; + + if (_glfw.win32.disabledCursorWindow != window) + break; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + _glfwInputCursorPos(window, x, y); + + window->win32.lastCursorPosX = x; + window->win32.lastCursorPosY = y; + + if (!window->win32.cursorTracked) + { + TRACKMOUSEEVENT tme; + ZeroMemory(&tme, sizeof(tme)); + tme.cbSize = sizeof(tme); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = window->win32.handle; + TrackMouseEvent(&tme); + + window->win32.cursorTracked = GLFW_TRUE; + _glfwInputCursorEnter(window, GLFW_TRUE); + } + + return 0; + } + + case WM_MOUSELEAVE: + { + window->win32.cursorTracked = GLFW_FALSE; + _glfwInputCursorEnter(window, GLFW_FALSE); + return 0; + } + + case WM_MOUSEWHEEL: + { + _glfwInputScroll(window, 0.0, (SHORT) HIWORD(wParam) / (double) WHEEL_DELTA); + return 0; + } + + case WM_MOUSEHWHEEL: + { + // This message is only sent on Windows Vista and later + // NOTE: The X-axis is inverted for consistency with macOS and X11 + _glfwInputScroll(window, -((SHORT) HIWORD(wParam) / (double) WHEEL_DELTA), 0.0); + return 0; + } + + case WM_ENTERSIZEMOVE: + case WM_ENTERMENULOOP: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + + break; + } + + case WM_EXITSIZEMOVE: + case WM_EXITMENULOOP: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + + break; + } + + case WM_SIZE: + { + const GLFWbool iconified = wParam == SIZE_MINIMIZED; + const GLFWbool maximized = wParam == SIZE_MAXIMIZED || + (window->win32.maximized && + wParam != SIZE_RESTORED); + + if (_glfw.win32.disabledCursorWindow == window) + updateClipRect(window); + + if (window->win32.iconified != iconified) + _glfwInputWindowIconify(window, iconified); + + if (window->win32.maximized != maximized) + _glfwInputWindowMaximize(window, maximized); + + _glfwInputFramebufferSize(window, LOWORD(lParam), HIWORD(lParam)); + _glfwInputWindowSize(window, LOWORD(lParam), HIWORD(lParam)); + + if (window->monitor && window->win32.iconified != iconified) + { + if (iconified) + releaseMonitor(window); + else + acquireMonitor(window); + } + + window->win32.iconified = iconified; + window->win32.maximized = maximized; + return 0; + } + + case WM_MOVE: + { + if (_glfw.win32.disabledCursorWindow == window) + updateClipRect(window); + + // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as + // those macros do not handle negative window positions correctly + _glfwInputWindowPos(window, + GET_X_LPARAM(lParam), + GET_Y_LPARAM(lParam)); + return 0; + } + + case WM_SIZING: + { + if (window->numer == GLFW_DONT_CARE || + window->denom == GLFW_DONT_CARE) + { + break; + } + + applyAspectRatio(window, (int) wParam, (RECT*) lParam); + return TRUE; + } + + case WM_GETMINMAXINFO: + { + int xoff, yoff; + MINMAXINFO* mmi = (MINMAXINFO*) lParam; + + if (window->monitor) + break; + + getFullWindowSize(getWindowStyle(window), getWindowExStyle(window), + 0, 0, &xoff, &yoff); + + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + mmi->ptMinTrackSize.x = window->minwidth + xoff; + mmi->ptMinTrackSize.y = window->minheight + yoff; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + mmi->ptMaxTrackSize.x = window->maxwidth + xoff; + mmi->ptMaxTrackSize.y = window->maxheight + yoff; + } + + return 0; + } + + case WM_PAINT: + { + _glfwInputWindowDamage(window); + break; + } + + case WM_ERASEBKGND: + { + return TRUE; + } + + case WM_SETCURSOR: + { + if (LOWORD(lParam) == HTCLIENT) + { + updateCursorImage(window); + return TRUE; + } + + break; + } + + case WM_DPICHANGED: + { + RECT* rect = (RECT*) lParam; + SetWindowPos(window->win32.handle, + HWND_TOP, + rect->left, + rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + SWP_NOACTIVATE | SWP_NOZORDER); + break; + } + + case WM_DROPFILES: + { + HDROP drop = (HDROP) wParam; + POINT pt; + int i; + + const int count = DragQueryFileW(drop, 0xffffffff, NULL, 0); + char** paths = calloc(count, sizeof(char*)); + + // Move the mouse to the position of the drop + DragQueryPoint(drop, &pt); + _glfwInputCursorPos(window, pt.x, pt.y); + + for (i = 0; i < count; i++) + { + const UINT length = DragQueryFileW(drop, i, NULL, 0); + WCHAR* buffer = calloc(length + 1, sizeof(WCHAR)); + + DragQueryFileW(drop, i, buffer, length + 1); + paths[i] = _glfwCreateUTF8FromWideStringWin32(buffer); + + free(buffer); + } + + _glfwInputDrop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + free(paths[i]); + free(paths); + + DragFinish(drop); + return 0; + } + } + + return DefWindowProcW(hWnd, uMsg, wParam, lParam); +} + +// Creates the GLFW window +// +static int createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + int xpos, ypos, fullWidth, fullHeight; + WCHAR* wideTitle; + DWORD style = getWindowStyle(window); + DWORD exStyle = getWindowExStyle(window); + + if (window->monitor) + { + GLFWvidmode mode; + + // NOTE: This window placement is temporary and approximate, as the + // correct position and size cannot be known until the monitor + // video mode has been set + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwPlatformGetVideoMode(window->monitor, &mode); + fullWidth = mode.width; + fullHeight = mode.height; + } + else + { + xpos = CW_USEDEFAULT; + ypos = CW_USEDEFAULT; + + if (wndconfig->maximized) + style |= WS_MAXIMIZE; + + getFullWindowSize(style, exStyle, + wndconfig->width, wndconfig->height, + &fullWidth, &fullHeight); + } + + wideTitle = _glfwCreateWideStringFromUTF8Win32(wndconfig->title); + if (!wideTitle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert window title to UTF-16"); + return GLFW_FALSE; + } + + window->win32.handle = CreateWindowExW(exStyle, + _GLFW_WNDCLASSNAME, + wideTitle, + style, + xpos, ypos, + fullWidth, fullHeight, + NULL, // No parent window + NULL, // No window menu + GetModuleHandleW(NULL), + NULL); + + free(wideTitle); + + if (!window->win32.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window"); + return GLFW_FALSE; + } + + SetPropW(window->win32.handle, L"GLFW", window); + + if (_glfw_ChangeWindowMessageFilterEx) + { + _glfw_ChangeWindowMessageFilterEx(window->win32.handle, + WM_DROPFILES, MSGFLT_ALLOW, NULL); + _glfw_ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYDATA, MSGFLT_ALLOW, NULL); + _glfw_ChangeWindowMessageFilterEx(window->win32.handle, + WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL); + } + + DragAcceptFiles(window->win32.handle, TRUE); + + return GLFW_TRUE; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Registers the GLFW window class +// +GLFWbool _glfwRegisterWindowClassWin32(void) +{ + WNDCLASSEXW wc; + + ZeroMemory(&wc, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC) windowProc; + wc.hInstance = GetModuleHandleW(NULL); + wc.hCursor = LoadCursorW(NULL, IDC_ARROW); + wc.lpszClassName = _GLFW_WNDCLASSNAME; + + // Load user-provided icon if available + wc.hIcon = LoadImageW(GetModuleHandleW(NULL), + L"GLFW_ICON", IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + if (!wc.hIcon) + { + // No user-provided icon found, load default icon + wc.hIcon = LoadImageW(NULL, + IDI_APPLICATION, IMAGE_ICON, + 0, 0, LR_DEFAULTSIZE | LR_SHARED); + } + + if (!RegisterClassExW(&wc)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to register window class"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Unregisters the GLFW window class +// +void _glfwUnregisterWindowClassWin32(void) +{ + UnregisterClassW(_GLFW_WNDCLASSNAME, GetModuleHandleW(NULL)); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createNativeWindow(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitWGL()) + return GLFW_FALSE; + if (!_glfwCreateContextWGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); + if (!acquireMonitor(window)) + return GLFW_FALSE; + + centerCursor(window); + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window->monitor) + releaseMonitor(window); + + if (window->context.destroy) + window->context.destroy(window); + + if (_glfw.win32.disabledCursorWindow == window) + _glfw.win32.disabledCursorWindow = NULL; + + if (window->win32.handle) + { + RemovePropW(window->win32.handle, L"GLFW"); + DestroyWindow(window->win32.handle); + window->win32.handle = NULL; + } + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + WCHAR* wideTitle = _glfwCreateWideStringFromUTF8Win32(title); + if (!wideTitle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert window title to UTF-16"); + return; + } + + SetWindowTextW(window->win32.handle, wideTitle); + free(wideTitle); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + HICON bigIcon = NULL, smallIcon = NULL; + + if (count) + { + const GLFWimage* bigImage = chooseImage(count, images, + GetSystemMetrics(SM_CXICON), + GetSystemMetrics(SM_CYICON)); + const GLFWimage* smallImage = chooseImage(count, images, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON)); + + bigIcon = createIcon(bigImage, 0, 0, GLFW_TRUE); + smallIcon = createIcon(smallImage, 0, 0, GLFW_TRUE); + } + else + { + bigIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICON); + smallIcon = (HICON) GetClassLongPtrW(window->win32.handle, GCLP_HICONSM); + } + + SendMessage(window->win32.handle, WM_SETICON, ICON_BIG, (LPARAM) bigIcon); + SendMessage(window->win32.handle, WM_SETICON, ICON_SMALL, (LPARAM) smallIcon); + + if (window->win32.bigIcon) + DestroyIcon(window->win32.bigIcon); + + if (window->win32.smallIcon) + DestroyIcon(window->win32.smallIcon); + + if (count) + { + window->win32.bigIcon = bigIcon; + window->win32.smallIcon = smallIcon; + } +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + POINT pos = { 0, 0 }; + ClientToScreen(window->win32.handle, &pos); + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = pos.y; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + RECT rect = { xpos, ypos, xpos, ypos }; + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, NULL, rect.left, rect.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + RECT area; + GetClientRect(window->win32.handle, &area); + + if (width) + *width = area.right; + if (height) + *height = area.bottom; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } + else + { + RECT rect = { 0, 0, width, height }; + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, HWND_TOP, + 0, 0, rect.right - rect.left, rect.bottom - rect.top, + SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOZORDER); + } +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + RECT area; + + if ((minwidth == GLFW_DONT_CARE || minheight == GLFW_DONT_CARE) && + (maxwidth == GLFW_DONT_CARE || maxheight == GLFW_DONT_CARE)) + { + return; + } + + GetWindowRect(window->win32.handle, &area); + MoveWindow(window->win32.handle, + area.left, area.top, + area.right - area.left, + area.bottom - area.top, TRUE); +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + RECT area; + + if (numer == GLFW_DONT_CARE || denom == GLFW_DONT_CARE) + return; + + GetWindowRect(window->win32.handle, &area); + applyAspectRatio(window, WMSZ_BOTTOMRIGHT, &area); + MoveWindow(window->win32.handle, + area.left, area.top, + area.right - area.left, + area.bottom - area.top, TRUE); +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + RECT rect; + int width, height; + + _glfwPlatformGetWindowSize(window, &width, &height); + SetRect(&rect, 0, 0, width, height); + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + + if (left) + *left = -rect.left; + if (top) + *top = -rect.top; + if (right) + *right = rect.right - width; + if (bottom) + *bottom = rect.bottom - height; +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_MINIMIZE); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_RESTORE); +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_MAXIMIZE); +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_SHOW); +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + ShowWindow(window->win32.handle, SW_HIDE); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + BringWindowToTop(window->win32.handle); + SetForegroundWindow(window->win32.handle); + SetFocus(window->win32.handle); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, HWND_TOP, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + SWP_NOCOPYBITS | SWP_NOACTIVATE | SWP_NOZORDER); + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + + if (monitor) + { + GLFWvidmode mode; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + style &= ~WS_OVERLAPPEDWINDOW; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + flags |= SWP_FRAMECHANGED; + } + + _glfwPlatformGetVideoMode(monitor, &mode); + _glfwPlatformGetMonitorPos(monitor, &xpos, &ypos); + + SetWindowPos(window->win32.handle, HWND_TOPMOST, + xpos, ypos, mode.width, mode.height, + flags); + + acquireMonitor(window); + } + else + { + HWND after; + RECT rect = { xpos, ypos, xpos + width, ypos + height }; + DWORD style = GetWindowLongW(window->win32.handle, GWL_STYLE); + UINT flags = SWP_NOACTIVATE | SWP_NOCOPYBITS; + + if (window->decorated) + { + style &= ~WS_POPUP; + style |= getWindowStyle(window); + SetWindowLongW(window->win32.handle, GWL_STYLE, style); + + flags |= SWP_FRAMECHANGED; + } + + if (window->floating) + after = HWND_TOPMOST; + else + after = HWND_NOTOPMOST; + + AdjustWindowRectEx(&rect, getWindowStyle(window), + FALSE, getWindowExStyle(window)); + SetWindowPos(window->win32.handle, after, + rect.left, rect.top, + rect.right - rect.left, rect.bottom - rect.top, + flags); + } +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return window->win32.handle == GetActiveWindow(); +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return IsIconic(window->win32.handle); +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return IsWindowVisible(window->win32.handle); +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return IsZoomed(window->win32.handle); +} + +void _glfwPlatformPollEvents(void) +{ + MSG msg; + HWND handle; + _GLFWwindow* window; + + while (PeekMessageW(&msg, NULL, 0, 0, PM_REMOVE)) + { + if (msg.message == WM_QUIT) + { + // Treat WM_QUIT as a close on all windows + // While GLFW does not itself post WM_QUIT, other processes may post + // it to this one, for example Task Manager + + window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + } + else + { + TranslateMessage(&msg); + DispatchMessageW(&msg); + } + } + + handle = GetActiveWindow(); + if (handle) + { + // LSHIFT/RSHIFT fixup (keys tend to "stick" without this fix) + // This is the only async event handling in GLFW, but it solves some + // nasty problems + window = GetPropW(handle, L"GLFW"); + if (window) + { + const int mods = getAsyncKeyMods(); + + // Get current state of left and right shift keys + const int lshiftDown = (GetAsyncKeyState(VK_LSHIFT) >> 15) & 1; + const int rshiftDown = (GetAsyncKeyState(VK_RSHIFT) >> 15) & 1; + + // See if this differs from our belief of what has happened + // (we only have to check for lost key up events) + if (!lshiftDown && window->keys[GLFW_KEY_LEFT_SHIFT] == 1) + _glfwInputKey(window, GLFW_KEY_LEFT_SHIFT, 0, GLFW_RELEASE, mods); + + if (!rshiftDown && window->keys[GLFW_KEY_RIGHT_SHIFT] == 1) + _glfwInputKey(window, GLFW_KEY_RIGHT_SHIFT, 0, GLFW_RELEASE, mods); + } + } + + window = _glfw.win32.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE + if (window->win32.lastCursorPosX != width / 2 || + window->win32.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } +} + +void _glfwPlatformWaitEvents(void) +{ + WaitMessage(); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + MsgWaitForMultipleObjects(0, NULL, FALSE, (DWORD) (timeout * 1e3), QS_ALLEVENTS); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + PostMessage(_glfw.win32.helperWindowHandle, WM_NULL, 0, 0); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + POINT pos; + + if (GetCursorPos(&pos)) + { + ScreenToClient(window->win32.handle, &pos); + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = pos.y; + } +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) +{ + POINT pos = { (int) xpos, (int) ypos }; + + // Store the new position so it can be recognized later + window->win32.lastCursorPosX = pos.x; + window->win32.lastCursorPosY = pos.y; + + ClientToScreen(window->win32.handle, &pos); + SetCursorPos(pos.x, pos.y); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + _glfw.win32.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.win32.restoreCursorPosX, + &_glfw.win32.restoreCursorPosY); + centerCursor(window); + updateClipRect(window); + } + else if (_glfw.win32.disabledCursorWindow == window) + { + _glfw.win32.disabledCursorWindow = NULL; + updateClipRect(NULL); + _glfwPlatformSetCursorPos(window, + _glfw.win32.restoreCursorPosX, + _glfw.win32.restoreCursorPosY); + } + + if (cursorInClientArea(window)) + updateCursorImage(window); +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + WCHAR name[16]; + + if (key != GLFW_KEY_UNKNOWN) + scancode = _glfw.win32.scancodes[key]; + + if (!_glfwIsPrintable(_glfw.win32.keycodes[scancode])) + return NULL; + + if (!GetKeyNameTextW(scancode << 16, name, sizeof(name) / sizeof(WCHAR))) + return NULL; + + if (!WideCharToMultiByte(CP_UTF8, 0, name, -1, + _glfw.win32.keyName, + sizeof(_glfw.win32.keyName), + NULL, NULL)) + { + return NULL; + } + + return _glfw.win32.keyName; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.win32.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + cursor->win32.handle = (HCURSOR) createIcon(image, xhot, yhot, GLFW_FALSE); + if (!cursor->win32.handle) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + cursor->win32.handle = + CopyCursor(LoadCursorW(NULL, translateCursorShape(shape))); + if (!cursor->win32.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create standard cursor"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->win32.handle) + DestroyIcon((HICON) cursor->win32.handle); +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (cursorInClientArea(window)) + updateCursorImage(window); +} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + int characterCount; + HANDLE object; + WCHAR* buffer; + + characterCount = MultiByteToWideChar(CP_UTF8, 0, string, -1, NULL, 0); + if (!characterCount) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert clipboard string to UTF-16"); + return; + } + + object = GlobalAlloc(GMEM_MOVEABLE, characterCount * sizeof(WCHAR)); + if (!object) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to allocate global handle for clipboard"); + return; + } + + buffer = GlobalLock(object); + if (!buffer) + { + GlobalFree(object); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); + return; + } + + MultiByteToWideChar(CP_UTF8, 0, string, -1, buffer, characterCount); + GlobalUnlock(object); + + if (!OpenClipboard(_glfw.win32.helperWindowHandle)) + { + GlobalFree(object); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + return; + } + + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, object); + CloseClipboard(); +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + HANDLE object; + WCHAR* buffer; + + if (!OpenClipboard(_glfw.win32.helperWindowHandle)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to open clipboard"); + return NULL; + } + + object = GetClipboardData(CF_UNICODETEXT); + if (!object) + { + CloseClipboard(); + + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "Win32: Failed to convert clipboard to string"); + return NULL; + } + + buffer = GlobalLock(object); + if (!buffer) + { + CloseClipboard(); + + _glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to lock global handle"); + return NULL; + } + + free(_glfw.win32.clipboardString); + _glfw.win32.clipboardString = + _glfwCreateUTF8FromWideStringWin32(buffer); + + GlobalUnlock(object); + CloseClipboard(); + + if (!_glfw.win32.clipboardString) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to convert wide string to UTF-8"); + return NULL; + } + + return _glfw.win32.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_win32_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_win32_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR vkGetPhysicalDeviceWin32PresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWin32PresentationSupportKHR"); + if (!vkGetPhysicalDeviceWin32PresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceWin32PresentationSupportKHR(device, queuefamily); +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkWin32SurfaceCreateInfoKHR sci; + PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR; + + vkCreateWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateWin32SurfaceKHR"); + if (!vkCreateWin32SurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Win32: Vulkan instance missing VK_KHR_win32_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; + sci.hinstance = GetModuleHandle(NULL); + sci.hwnd = window->win32.handle; + + err = vkCreateWin32SurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI HWND glfwGetWin32Window(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->win32.handle; +} + diff --git a/apps/exampleViewer/common/glfw/src/window.c b/apps/exampleViewer/common/glfw/src/window.c new file mode 100644 index 0000000000..6c4626d8f7 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/window.c @@ -0,0 +1,925 @@ +//======================================================================== +// GLFW 3.3 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// Copyright (c) 2012 Torsten Walluhn +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////// +////// GLFW event API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwInputWindowFocus(_GLFWwindow* window, GLFWbool focused) +{ + if (focused) + { + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); + } + else + { + int i; + + if (window->callbacks.focus) + window->callbacks.focus((GLFWwindow*) window, focused); + + // Release all pressed keyboard keys + for (i = 0; i <= GLFW_KEY_LAST; i++) + { + if (window->keys[i] == GLFW_PRESS) + _glfwInputKey(window, i, 0, GLFW_RELEASE, 0); + } + + // Release all pressed mouse buttons + for (i = 0; i <= GLFW_MOUSE_BUTTON_LAST; i++) + { + if (window->mouseButtons[i] == GLFW_PRESS) + _glfwInputMouseClick(window, i, GLFW_RELEASE, 0); + } + } +} + +void _glfwInputWindowPos(_GLFWwindow* window, int x, int y) +{ + if (window->callbacks.pos) + window->callbacks.pos((GLFWwindow*) window, x, y); +} + +void _glfwInputWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.size) + window->callbacks.size((GLFWwindow*) window, width, height); +} + +void _glfwInputWindowIconify(_GLFWwindow* window, GLFWbool iconified) +{ + if (window->callbacks.iconify) + window->callbacks.iconify((GLFWwindow*) window, iconified); +} + +void _glfwInputWindowMaximize(_GLFWwindow* window, GLFWbool maximized) +{ + if (window->callbacks.maximize) + window->callbacks.maximize((GLFWwindow*) window, maximized); +} + +void _glfwInputFramebufferSize(_GLFWwindow* window, int width, int height) +{ + if (window->callbacks.fbsize) + window->callbacks.fbsize((GLFWwindow*) window, width, height); +} + +void _glfwInputWindowDamage(_GLFWwindow* window) +{ + if (window->callbacks.refresh) + window->callbacks.refresh((GLFWwindow*) window); +} + +void _glfwInputWindowCloseRequest(_GLFWwindow* window) +{ + window->closed = GLFW_TRUE; + + if (window->callbacks.close) + window->callbacks.close((GLFWwindow*) window); +} + +void _glfwInputWindowMonitorChange(_GLFWwindow* window, _GLFWmonitor* monitor) +{ + window->monitor = monitor; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW public API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, + const char* title, + GLFWmonitor* monitor, + GLFWwindow* share) +{ + _GLFWfbconfig fbconfig; + _GLFWctxconfig ctxconfig; + _GLFWwndconfig wndconfig; + _GLFWwindow* window; + _GLFWwindow* previous; + + assert(title != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + + return NULL; + } + + fbconfig = _glfw.hints.framebuffer; + ctxconfig = _glfw.hints.context; + wndconfig = _glfw.hints.window; + + wndconfig.width = width; + wndconfig.height = height; + wndconfig.title = title; + ctxconfig.share = (_GLFWwindow*) share; + + if (ctxconfig.share) + { + if (ctxconfig.client == GLFW_NO_API || + ctxconfig.share->context.client == GLFW_NO_API) + { + _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); + return NULL; + } + } + + if (!_glfwIsValidContextConfig(&ctxconfig)) + return NULL; + + window = calloc(1, sizeof(_GLFWwindow)); + window->next = _glfw.windowListHead; + _glfw.windowListHead = window; + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.redBits = fbconfig.redBits; + window->videoMode.greenBits = fbconfig.greenBits; + window->videoMode.blueBits = fbconfig.blueBits; + window->videoMode.refreshRate = _glfw.hints.refreshRate; + + window->monitor = (_GLFWmonitor*) monitor; + window->resizable = wndconfig.resizable; + window->decorated = wndconfig.decorated; + window->autoIconify = wndconfig.autoIconify; + window->floating = wndconfig.floating; + window->cursorMode = GLFW_CURSOR_NORMAL; + + window->minwidth = GLFW_DONT_CARE; + window->minheight = GLFW_DONT_CARE; + window->maxwidth = GLFW_DONT_CARE; + window->maxheight = GLFW_DONT_CARE; + window->numer = GLFW_DONT_CARE; + window->denom = GLFW_DONT_CARE; + + // Save the currently current context so it can be restored later + previous = _glfwPlatformGetCurrentContext(); + if (ctxconfig.client != GLFW_NO_API) + glfwMakeContextCurrent(NULL); + + // Open the actual window and create its context + if (!_glfwPlatformCreateWindow(window, &wndconfig, &ctxconfig, &fbconfig)) + { + glfwMakeContextCurrent((GLFWwindow*) previous); + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + + if (ctxconfig.client != GLFW_NO_API) + { + window->context.makeCurrent(window); + + // Retrieve the actual (as opposed to requested) context attributes + if (!_glfwRefreshContextAttribs(&ctxconfig)) + { + glfwMakeContextCurrent((GLFWwindow*) previous); + glfwDestroyWindow((GLFWwindow*) window); + return NULL; + } + + // Restore the previously current context (or NULL) + glfwMakeContextCurrent((GLFWwindow*) previous); + } + + if (!window->monitor) + { + if (wndconfig.visible) + { + _glfwPlatformShowWindow(window); + if (wndconfig.focused) + _glfwPlatformFocusWindow(window); + } + } + + return (GLFWwindow*) window; +} + +void glfwDefaultWindowHints(void) +{ + _GLFW_REQUIRE_INIT(); + + memset(&_glfw.hints, 0, sizeof(_glfw.hints)); + + // The default is OpenGL with minimum version 1.0 + _glfw.hints.context.client = GLFW_OPENGL_API; + _glfw.hints.context.source = GLFW_NATIVE_CONTEXT_API; + _glfw.hints.context.major = 1; + _glfw.hints.context.minor = 0; + + // The default is a focused, visible, resizable window with decorations + _glfw.hints.window.resizable = GLFW_TRUE; + _glfw.hints.window.visible = GLFW_TRUE; + _glfw.hints.window.decorated = GLFW_TRUE; + _glfw.hints.window.focused = GLFW_TRUE; + _glfw.hints.window.autoIconify = GLFW_TRUE; + + // The default is 24 bits of color, 24 bits of depth and 8 bits of stencil, + // double buffered + _glfw.hints.framebuffer.redBits = 8; + _glfw.hints.framebuffer.greenBits = 8; + _glfw.hints.framebuffer.blueBits = 8; + _glfw.hints.framebuffer.alphaBits = 8; + _glfw.hints.framebuffer.depthBits = 24; + _glfw.hints.framebuffer.stencilBits = 8; + _glfw.hints.framebuffer.doublebuffer = GLFW_TRUE; + + // The default is to select the highest available refresh rate + _glfw.hints.refreshRate = GLFW_DONT_CARE; +} + +GLFWAPI void glfwWindowHint(int hint, int value) +{ + _GLFW_REQUIRE_INIT(); + + switch (hint) + { + case GLFW_RED_BITS: + _glfw.hints.framebuffer.redBits = value; + break; + case GLFW_GREEN_BITS: + _glfw.hints.framebuffer.greenBits = value; + break; + case GLFW_BLUE_BITS: + _glfw.hints.framebuffer.blueBits = value; + break; + case GLFW_ALPHA_BITS: + _glfw.hints.framebuffer.alphaBits = value; + break; + case GLFW_DEPTH_BITS: + _glfw.hints.framebuffer.depthBits = value; + break; + case GLFW_STENCIL_BITS: + _glfw.hints.framebuffer.stencilBits = value; + break; + case GLFW_ACCUM_RED_BITS: + _glfw.hints.framebuffer.accumRedBits = value; + break; + case GLFW_ACCUM_GREEN_BITS: + _glfw.hints.framebuffer.accumGreenBits = value; + break; + case GLFW_ACCUM_BLUE_BITS: + _glfw.hints.framebuffer.accumBlueBits = value; + break; + case GLFW_ACCUM_ALPHA_BITS: + _glfw.hints.framebuffer.accumAlphaBits = value; + break; + case GLFW_AUX_BUFFERS: + _glfw.hints.framebuffer.auxBuffers = value; + break; + case GLFW_STEREO: + _glfw.hints.framebuffer.stereo = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_DOUBLEBUFFER: + _glfw.hints.framebuffer.doublebuffer = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_SAMPLES: + _glfw.hints.framebuffer.samples = value; + break; + case GLFW_SRGB_CAPABLE: + _glfw.hints.framebuffer.sRGB = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_RESIZABLE: + _glfw.hints.window.resizable = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_DECORATED: + _glfw.hints.window.decorated = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_FOCUSED: + _glfw.hints.window.focused = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_AUTO_ICONIFY: + _glfw.hints.window.autoIconify = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_FLOATING: + _glfw.hints.window.floating = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_MAXIMIZED: + _glfw.hints.window.maximized = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_VISIBLE: + _glfw.hints.window.visible = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_CLIENT_API: + _glfw.hints.context.client = value; + break; + case GLFW_CONTEXT_CREATION_API: + _glfw.hints.context.source = value; + break; + case GLFW_CONTEXT_VERSION_MAJOR: + _glfw.hints.context.major = value; + break; + case GLFW_CONTEXT_VERSION_MINOR: + _glfw.hints.context.minor = value; + break; + case GLFW_CONTEXT_ROBUSTNESS: + _glfw.hints.context.robustness = value; + break; + case GLFW_OPENGL_FORWARD_COMPAT: + _glfw.hints.context.forward = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_OPENGL_DEBUG_CONTEXT: + _glfw.hints.context.debug = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_CONTEXT_NO_ERROR: + _glfw.hints.context.noerror = value ? GLFW_TRUE : GLFW_FALSE; + break; + case GLFW_OPENGL_PROFILE: + _glfw.hints.context.profile = value; + break; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + _glfw.hints.context.release = value; + break; + case GLFW_REFRESH_RATE: + _glfw.hints.refreshRate = value; + break; + default: + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window hint %i", hint); + break; + } +} + +GLFWAPI void glfwDestroyWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + + _GLFW_REQUIRE_INIT(); + + // Allow closing of NULL (to match the behavior of free) + if (window == NULL) + return; + + // Clear all callbacks to avoid exposing a half torn-down window object + memset(&window->callbacks, 0, sizeof(window->callbacks)); + + // The window's context must not be current on another thread when the + // window is destroyed + if (window == _glfwPlatformGetCurrentContext()) + glfwMakeContextCurrent(NULL); + + _glfwPlatformDestroyWindow(window); + + // Unlink window from global linked list + { + _GLFWwindow** prev = &_glfw.windowListHead; + + while (*prev != window) + prev = &((*prev)->next); + + *prev = window->next; + } + + free(window); +} + +GLFWAPI int glfwWindowShouldClose(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + return window->closed; +} + +GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* handle, int value) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->closed = value; +} + +GLFWAPI void glfwSetWindowTitle(GLFWwindow* handle, const char* title) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + assert(title != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowTitle(window, title); +} + +GLFWAPI void glfwSetWindowIcon(GLFWwindow* handle, + int count, const GLFWimage* images) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + assert(count >= 0); + assert(count == 0 || images != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformSetWindowIcon(window, count, images); +} + +GLFWAPI void glfwGetWindowPos(GLFWwindow* handle, int* xpos, int* ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (xpos) + *xpos = 0; + if (ypos) + *ypos = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwSetWindowPos(GLFWwindow* handle, int xpos, int ypos) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformSetWindowPos(window, xpos, ypos); +} + +GLFWAPI void glfwGetWindowSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSize(GLFWwindow* handle, int width, int height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + window->videoMode.width = width; + window->videoMode.height = height; + + _glfwPlatformSetWindowSize(window, width, height); +} + +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* handle, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (minwidth != GLFW_DONT_CARE && minheight != GLFW_DONT_CARE) + { + if (minwidth < 0 || minheight < 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window minimum size %ix%i", + minwidth, minheight); + return; + } + } + + if (maxwidth != GLFW_DONT_CARE && maxheight != GLFW_DONT_CARE) + { + if (maxwidth < 0 || maxheight < 0 || + maxwidth < minwidth || maxheight < minheight) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window maximum size %ix%i", + maxwidth, maxheight); + return; + } + } + + window->minwidth = minwidth; + window->minheight = minheight; + window->maxwidth = maxwidth; + window->maxheight = maxheight; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowSizeLimits(window, + minwidth, minheight, + maxwidth, maxheight); +} + +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* handle, int numer, int denom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (numer != GLFW_DONT_CARE && denom != GLFW_DONT_CARE) + { + if (numer <= 0 || denom <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window aspect ratio %i:%i", + numer, denom); + return; + } + } + + window->numer = numer; + window->denom = denom; + + if (window->monitor || !window->resizable) + return; + + _glfwPlatformSetWindowAspectRatio(window, numer, denom); +} + +GLFWAPI void glfwGetFramebufferSize(GLFWwindow* handle, int* width, int* height) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (width) + *width = 0; + if (height) + *height = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetFramebufferSize(window, width, height); +} + +GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* handle, + int* left, int* top, + int* right, int* bottom) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + if (left) + *left = 0; + if (top) + *top = 0; + if (right) + *right = 0; + if (bottom) + *bottom = 0; + + _GLFW_REQUIRE_INIT(); + _glfwPlatformGetWindowFrameSize(window, left, top, right, bottom); +} + +GLFWAPI void glfwIconifyWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformIconifyWindow(window); +} + +GLFWAPI void glfwRestoreWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + _glfwPlatformRestoreWindow(window); +} + +GLFWAPI void glfwMaximizeWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformMaximizeWindow(window); +} + +GLFWAPI void glfwShowWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformShowWindow(window); + _glfwPlatformFocusWindow(window); +} + +GLFWAPI void glfwHideWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (window->monitor) + return; + + _glfwPlatformHideWindow(window); +} + +GLFWAPI void glfwFocusWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + _glfwPlatformFocusWindow(window); +} + +GLFWAPI int glfwGetWindowAttrib(GLFWwindow* handle, int attrib) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(0); + + switch (attrib) + { + case GLFW_FOCUSED: + return _glfwPlatformWindowFocused(window); + case GLFW_ICONIFIED: + return _glfwPlatformWindowIconified(window); + case GLFW_VISIBLE: + return _glfwPlatformWindowVisible(window); + case GLFW_MAXIMIZED: + return _glfwPlatformWindowMaximized(window); + case GLFW_RESIZABLE: + return window->resizable; + case GLFW_DECORATED: + return window->decorated; + case GLFW_FLOATING: + return window->floating; + case GLFW_CLIENT_API: + return window->context.client; + case GLFW_CONTEXT_CREATION_API: + return window->context.source; + case GLFW_CONTEXT_VERSION_MAJOR: + return window->context.major; + case GLFW_CONTEXT_VERSION_MINOR: + return window->context.minor; + case GLFW_CONTEXT_REVISION: + return window->context.revision; + case GLFW_CONTEXT_ROBUSTNESS: + return window->context.robustness; + case GLFW_OPENGL_FORWARD_COMPAT: + return window->context.forward; + case GLFW_OPENGL_DEBUG_CONTEXT: + return window->context.debug; + case GLFW_OPENGL_PROFILE: + return window->context.profile; + case GLFW_CONTEXT_RELEASE_BEHAVIOR: + return window->context.release; + case GLFW_CONTEXT_NO_ERROR: + return window->context.noerror; + } + + _glfwInputError(GLFW_INVALID_ENUM, "Invalid window attribute %i", attrib); + return 0; +} + +GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return (GLFWmonitor*) window->monitor; +} + +GLFWAPI void glfwSetWindowMonitor(GLFWwindow* wh, + GLFWmonitor* mh, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + _GLFWwindow* window = (_GLFWwindow*) wh; + _GLFWmonitor* monitor = (_GLFWmonitor*) mh; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + + if (width <= 0 || height <= 0) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid window size %ix%i", + width, height); + return; + } + + if (refreshRate < 0 && refreshRate != GLFW_DONT_CARE) + { + _glfwInputError(GLFW_INVALID_VALUE, + "Invalid refresh rate %i", + refreshRate); + return; + } + + window->videoMode.width = width; + window->videoMode.height = height; + window->videoMode.refreshRate = refreshRate; + + _glfwPlatformSetWindowMonitor(window, monitor, + xpos, ypos, width, height, + refreshRate); +} + +GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* handle, void* pointer) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT(); + window->userPointer = pointer; +} + +GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->userPointer; +} + +GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* handle, + GLFWwindowposfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.pos, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* handle, + GLFWwindowsizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.size, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* handle, + GLFWwindowclosefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.close, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* handle, + GLFWwindowrefreshfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.refresh, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* handle, + GLFWwindowfocusfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.focus, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* handle, + GLFWwindowiconifyfun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.iconify, cbfun); + return cbfun; +} + +GLFWAPI GLFWwindowmaximizefun glfwSetWindowMaximizeCallback(GLFWwindow* handle, + GLFWwindowmaximizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.maximize, cbfun); + return cbfun; +} + +GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* handle, + GLFWframebuffersizefun cbfun) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + assert(window != NULL); + + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + _GLFW_SWAP_POINTERS(window->callbacks.fbsize, cbfun); + return cbfun; +} + +GLFWAPI void glfwPollEvents(void) +{ + _GLFW_REQUIRE_INIT(); + _glfwPlatformPollEvents(); +} + +GLFWAPI void glfwWaitEvents(void) +{ + _GLFW_REQUIRE_INIT(); + + if (!_glfw.windowListHead) + return; + + _glfwPlatformWaitEvents(); +} + +GLFWAPI void glfwWaitEventsTimeout(double timeout) +{ + _GLFW_REQUIRE_INIT(); + + if (timeout != timeout || timeout < 0.0 || timeout > DBL_MAX) + { + _glfwInputError(GLFW_INVALID_VALUE, "Invalid time %f", timeout); + return; + } + + _glfwPlatformWaitEventsTimeout(timeout); +} + +GLFWAPI void glfwPostEmptyEvent(void) +{ + _GLFW_REQUIRE_INIT(); + + if (!_glfw.windowListHead) + return; + + _glfwPlatformPostEmptyEvent(); +} + diff --git a/apps/exampleViewer/common/glfw/src/wl_init.c b/apps/exampleViewer/common/glfw/src/wl_init.c new file mode 100644 index 0000000000..e61f72f70f --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wl_init.c @@ -0,0 +1,752 @@ +//======================================================================== +// GLFW 3.3 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +static inline int min(int n1, int n2) +{ + return n1 < n2 ? n1 : n2; +} + +static void pointerHandleEnter(void* data, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface, + wl_fixed_t sx, + wl_fixed_t sy) +{ + _GLFWwindow* window = wl_surface_get_user_data(surface); + + _glfw.wl.pointerSerial = serial; + _glfw.wl.pointerFocus = window; + + _glfwPlatformSetCursor(window, window->wl.currentCursor); + _glfwInputCursorEnter(window, GLFW_TRUE); +} + +static void pointerHandleLeave(void* data, + struct wl_pointer* pointer, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + + if (!window) + return; + + _glfw.wl.pointerSerial = serial; + _glfw.wl.pointerFocus = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); +} + +static void pointerHandleMotion(void* data, + struct wl_pointer* pointer, + uint32_t time, + wl_fixed_t sx, + wl_fixed_t sy) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + + if (!window) + return; + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + return; + else + { + window->wl.cursorPosX = wl_fixed_to_double(sx); + window->wl.cursorPosY = wl_fixed_to_double(sy); + } + + _glfwInputCursorPos(window, + wl_fixed_to_double(sx), + wl_fixed_to_double(sy)); +} + +static void pointerHandleButton(void* data, + struct wl_pointer* wl_pointer, + uint32_t serial, + uint32_t time, + uint32_t button, + uint32_t state) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + int glfwButton; + + if (!window) + return; + + /* Makes left, right and middle 0, 1 and 2. Overall order follows evdev + * codes. */ + glfwButton = button - BTN_LEFT; + + _glfwInputMouseClick(window, + glfwButton, + state == WL_POINTER_BUTTON_STATE_PRESSED + ? GLFW_PRESS + : GLFW_RELEASE, + _glfw.wl.xkb.modifiers); +} + +static void pointerHandleAxis(void* data, + struct wl_pointer* wl_pointer, + uint32_t time, + uint32_t axis, + wl_fixed_t value) +{ + _GLFWwindow* window = _glfw.wl.pointerFocus; + double scrollFactor; + double x, y; + + if (!window) + return; + + /* Wayland scroll events are in pointer motion coordinate space (think + * two finger scroll). The factor 10 is commonly used to convert to + * "scroll step means 1.0. */ + scrollFactor = 1.0/10.0; + + switch (axis) + { + case WL_POINTER_AXIS_HORIZONTAL_SCROLL: + x = wl_fixed_to_double(value) * scrollFactor; + y = 0.0; + break; + case WL_POINTER_AXIS_VERTICAL_SCROLL: + x = 0.0; + y = wl_fixed_to_double(value) * scrollFactor; + break; + default: + break; + } + + _glfwInputScroll(window, x, y); +} + +static const struct wl_pointer_listener pointerListener = { + pointerHandleEnter, + pointerHandleLeave, + pointerHandleMotion, + pointerHandleButton, + pointerHandleAxis, +}; + +static void keyboardHandleKeymap(void* data, + struct wl_keyboard* keyboard, + uint32_t format, + int fd, + uint32_t size) +{ + struct xkb_keymap* keymap; + struct xkb_state* state; + struct xkb_compose_table* composeTable; + struct xkb_compose_state* composeState; + char* mapStr; + const char* locale; + + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) + { + close(fd); + return; + } + + mapStr = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (mapStr == MAP_FAILED) { + close(fd); + return; + } + + keymap = xkb_keymap_new_from_string(_glfw.wl.xkb.context, + mapStr, + XKB_KEYMAP_FORMAT_TEXT_V1, + 0); + munmap(mapStr, size); + close(fd); + + if (!keymap) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to compile keymap"); + return; + } + + state = xkb_state_new(keymap); + if (!state) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB state"); + xkb_keymap_unref(keymap); + return; + } + + // Look up the preferred locale, falling back to "C" as default. + locale = getenv("LC_ALL"); + if (!locale) + locale = getenv("LC_CTYPE"); + if (!locale) + locale = getenv("LANG"); + if (!locale) + locale = "C"; + + composeTable = + xkb_compose_table_new_from_locale(_glfw.wl.xkb.context, locale, + XKB_COMPOSE_COMPILE_NO_FLAGS); + if (composeTable) + { + composeState = + xkb_compose_state_new(composeTable, XKB_COMPOSE_STATE_NO_FLAGS); + xkb_compose_table_unref(composeTable); + if (composeState) + _glfw.wl.xkb.composeState = composeState; + else + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose state"); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create XKB compose table"); + } + + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + _glfw.wl.xkb.keymap = keymap; + _glfw.wl.xkb.state = state; + + _glfw.wl.xkb.controlMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Control"); + _glfw.wl.xkb.altMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod1"); + _glfw.wl.xkb.shiftMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Shift"); + _glfw.wl.xkb.superMask = + 1 << xkb_keymap_mod_get_index(_glfw.wl.xkb.keymap, "Mod4"); +} + +static void keyboardHandleEnter(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface, + struct wl_array* keys) +{ + _GLFWwindow* window = wl_surface_get_user_data(surface); + + _glfw.wl.keyboardFocus = window; + _glfwInputWindowFocus(window, GLFW_TRUE); +} + +static void keyboardHandleLeave(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + struct wl_surface* surface) +{ + _GLFWwindow* window = _glfw.wl.keyboardFocus; + + if (!window) + return; + + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); +} + +static int toGLFWKeyCode(uint32_t key) +{ + if (key < sizeof(_glfw.wl.keycodes) / sizeof(_glfw.wl.keycodes[0])) + return _glfw.wl.keycodes[key]; + + return GLFW_KEY_UNKNOWN; +} + +static xkb_keysym_t composeSymbol(xkb_keysym_t sym) +{ + if (sym == XKB_KEY_NoSymbol) + return sym; + if (xkb_compose_state_feed(_glfw.wl.xkb.composeState, sym) + != XKB_COMPOSE_FEED_ACCEPTED) + return sym; + switch (xkb_compose_state_get_status(_glfw.wl.xkb.composeState)) + { + case XKB_COMPOSE_COMPOSED: + return xkb_compose_state_get_one_sym(_glfw.wl.xkb.composeState); + case XKB_COMPOSE_COMPOSING: + case XKB_COMPOSE_CANCELLED: + return XKB_KEY_NoSymbol; + case XKB_COMPOSE_NOTHING: + default: + return sym; + } +} + +static void inputChar(_GLFWwindow* window, uint32_t key) +{ + uint32_t code, numSyms; + long cp; + const xkb_keysym_t *syms; + xkb_keysym_t sym; + + code = key + 8; + numSyms = xkb_state_key_get_syms(_glfw.wl.xkb.state, code, &syms); + + if (numSyms == 1) + { + sym = composeSymbol(syms[0]); + cp = _glfwKeySym2Unicode(sym); + if (cp != -1) + { + const int mods = _glfw.wl.xkb.modifiers; + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + _glfwInputChar(window, cp, mods, plain); + } + } +} + +static void keyboardHandleKey(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t time, + uint32_t key, + uint32_t state) +{ + int keyCode; + int action; + _GLFWwindow* window = _glfw.wl.keyboardFocus; + + if (!window) + return; + + keyCode = toGLFWKeyCode(key); + action = state == WL_KEYBOARD_KEY_STATE_PRESSED + ? GLFW_PRESS : GLFW_RELEASE; + + _glfwInputKey(window, keyCode, key, action, + _glfw.wl.xkb.modifiers); + + if (action == GLFW_PRESS) + inputChar(window, key); +} + +static void keyboardHandleModifiers(void* data, + struct wl_keyboard* keyboard, + uint32_t serial, + uint32_t modsDepressed, + uint32_t modsLatched, + uint32_t modsLocked, + uint32_t group) +{ + xkb_mod_mask_t mask; + unsigned int modifiers = 0; + + if (!_glfw.wl.xkb.keymap) + return; + + xkb_state_update_mask(_glfw.wl.xkb.state, + modsDepressed, + modsLatched, + modsLocked, + 0, + 0, + group); + + mask = xkb_state_serialize_mods(_glfw.wl.xkb.state, + XKB_STATE_MODS_DEPRESSED | + XKB_STATE_LAYOUT_DEPRESSED | + XKB_STATE_MODS_LATCHED | + XKB_STATE_LAYOUT_LATCHED); + if (mask & _glfw.wl.xkb.controlMask) + modifiers |= GLFW_MOD_CONTROL; + if (mask & _glfw.wl.xkb.altMask) + modifiers |= GLFW_MOD_ALT; + if (mask & _glfw.wl.xkb.shiftMask) + modifiers |= GLFW_MOD_SHIFT; + if (mask & _glfw.wl.xkb.superMask) + modifiers |= GLFW_MOD_SUPER; + _glfw.wl.xkb.modifiers = modifiers; +} + +static const struct wl_keyboard_listener keyboardListener = { + keyboardHandleKeymap, + keyboardHandleEnter, + keyboardHandleLeave, + keyboardHandleKey, + keyboardHandleModifiers, +}; + +static void seatHandleCapabilities(void* data, + struct wl_seat* seat, + enum wl_seat_capability caps) +{ + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !_glfw.wl.pointer) + { + _glfw.wl.pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(_glfw.wl.pointer, &pointerListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && _glfw.wl.pointer) + { + wl_pointer_destroy(_glfw.wl.pointer); + _glfw.wl.pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_KEYBOARD) && !_glfw.wl.keyboard) + { + _glfw.wl.keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(_glfw.wl.keyboard, &keyboardListener, NULL); + } + else if (!(caps & WL_SEAT_CAPABILITY_KEYBOARD) && _glfw.wl.keyboard) + { + wl_keyboard_destroy(_glfw.wl.keyboard); + _glfw.wl.keyboard = NULL; + } +} + +static const struct wl_seat_listener seatListener = { + seatHandleCapabilities +}; + +static void registryHandleGlobal(void* data, + struct wl_registry* registry, + uint32_t name, + const char* interface, + uint32_t version) +{ + if (strcmp(interface, "wl_compositor") == 0) + { + _glfw.wl.compositorVersion = min(3, version); + _glfw.wl.compositor = + wl_registry_bind(registry, name, &wl_compositor_interface, + _glfw.wl.compositorVersion); + } + else if (strcmp(interface, "wl_shm") == 0) + { + _glfw.wl.shm = + wl_registry_bind(registry, name, &wl_shm_interface, 1); + } + else if (strcmp(interface, "wl_shell") == 0) + { + _glfw.wl.shell = + wl_registry_bind(registry, name, &wl_shell_interface, 1); + } + else if (strcmp(interface, "wl_output") == 0) + { + _glfwAddOutputWayland(name, version); + } + else if (strcmp(interface, "wl_seat") == 0) + { + if (!_glfw.wl.seat) + { + _glfw.wl.seat = + wl_registry_bind(registry, name, &wl_seat_interface, 1); + wl_seat_add_listener(_glfw.wl.seat, &seatListener, NULL); + } + } + else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) + { + _glfw.wl.relativePointerManager = + wl_registry_bind(registry, name, + &zwp_relative_pointer_manager_v1_interface, + 1); + } + else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) + { + _glfw.wl.pointerConstraints = + wl_registry_bind(registry, name, + &zwp_pointer_constraints_v1_interface, + 1); + } +} + +static void registryHandleGlobalRemove(void *data, + struct wl_registry *registry, + uint32_t name) +{ +} + + +static const struct wl_registry_listener registryListener = { + registryHandleGlobal, + registryHandleGlobalRemove +}; + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode; + + memset(_glfw.wl.keycodes, -1, sizeof(_glfw.wl.keycodes)); + memset(_glfw.wl.scancodes, -1, sizeof(_glfw.wl.scancodes)); + + _glfw.wl.keycodes[KEY_GRAVE] = GLFW_KEY_GRAVE_ACCENT; + _glfw.wl.keycodes[KEY_1] = GLFW_KEY_1; + _glfw.wl.keycodes[KEY_2] = GLFW_KEY_2; + _glfw.wl.keycodes[KEY_3] = GLFW_KEY_3; + _glfw.wl.keycodes[KEY_4] = GLFW_KEY_4; + _glfw.wl.keycodes[KEY_5] = GLFW_KEY_5; + _glfw.wl.keycodes[KEY_6] = GLFW_KEY_6; + _glfw.wl.keycodes[KEY_7] = GLFW_KEY_7; + _glfw.wl.keycodes[KEY_8] = GLFW_KEY_8; + _glfw.wl.keycodes[KEY_9] = GLFW_KEY_9; + _glfw.wl.keycodes[KEY_0] = GLFW_KEY_0; + _glfw.wl.keycodes[KEY_MINUS] = GLFW_KEY_MINUS; + _glfw.wl.keycodes[KEY_EQUAL] = GLFW_KEY_EQUAL; + _glfw.wl.keycodes[KEY_Q] = GLFW_KEY_Q; + _glfw.wl.keycodes[KEY_W] = GLFW_KEY_W; + _glfw.wl.keycodes[KEY_E] = GLFW_KEY_E; + _glfw.wl.keycodes[KEY_R] = GLFW_KEY_R; + _glfw.wl.keycodes[KEY_T] = GLFW_KEY_T; + _glfw.wl.keycodes[KEY_Y] = GLFW_KEY_Y; + _glfw.wl.keycodes[KEY_U] = GLFW_KEY_U; + _glfw.wl.keycodes[KEY_I] = GLFW_KEY_I; + _glfw.wl.keycodes[KEY_O] = GLFW_KEY_O; + _glfw.wl.keycodes[KEY_P] = GLFW_KEY_P; + _glfw.wl.keycodes[KEY_LEFTBRACE] = GLFW_KEY_LEFT_BRACKET; + _glfw.wl.keycodes[KEY_RIGHTBRACE] = GLFW_KEY_RIGHT_BRACKET; + _glfw.wl.keycodes[KEY_A] = GLFW_KEY_A; + _glfw.wl.keycodes[KEY_S] = GLFW_KEY_S; + _glfw.wl.keycodes[KEY_D] = GLFW_KEY_D; + _glfw.wl.keycodes[KEY_F] = GLFW_KEY_F; + _glfw.wl.keycodes[KEY_G] = GLFW_KEY_G; + _glfw.wl.keycodes[KEY_H] = GLFW_KEY_H; + _glfw.wl.keycodes[KEY_J] = GLFW_KEY_J; + _glfw.wl.keycodes[KEY_K] = GLFW_KEY_K; + _glfw.wl.keycodes[KEY_L] = GLFW_KEY_L; + _glfw.wl.keycodes[KEY_SEMICOLON] = GLFW_KEY_SEMICOLON; + _glfw.wl.keycodes[KEY_APOSTROPHE] = GLFW_KEY_APOSTROPHE; + _glfw.wl.keycodes[KEY_Z] = GLFW_KEY_Z; + _glfw.wl.keycodes[KEY_X] = GLFW_KEY_X; + _glfw.wl.keycodes[KEY_C] = GLFW_KEY_C; + _glfw.wl.keycodes[KEY_V] = GLFW_KEY_V; + _glfw.wl.keycodes[KEY_B] = GLFW_KEY_B; + _glfw.wl.keycodes[KEY_N] = GLFW_KEY_N; + _glfw.wl.keycodes[KEY_M] = GLFW_KEY_M; + _glfw.wl.keycodes[KEY_COMMA] = GLFW_KEY_COMMA; + _glfw.wl.keycodes[KEY_DOT] = GLFW_KEY_PERIOD; + _glfw.wl.keycodes[KEY_SLASH] = GLFW_KEY_SLASH; + _glfw.wl.keycodes[KEY_BACKSLASH] = GLFW_KEY_BACKSLASH; + _glfw.wl.keycodes[KEY_ESC] = GLFW_KEY_ESCAPE; + _glfw.wl.keycodes[KEY_TAB] = GLFW_KEY_TAB; + _glfw.wl.keycodes[KEY_LEFTSHIFT] = GLFW_KEY_LEFT_SHIFT; + _glfw.wl.keycodes[KEY_RIGHTSHIFT] = GLFW_KEY_RIGHT_SHIFT; + _glfw.wl.keycodes[KEY_LEFTCTRL] = GLFW_KEY_LEFT_CONTROL; + _glfw.wl.keycodes[KEY_RIGHTCTRL] = GLFW_KEY_RIGHT_CONTROL; + _glfw.wl.keycodes[KEY_LEFTALT] = GLFW_KEY_LEFT_ALT; + _glfw.wl.keycodes[KEY_RIGHTALT] = GLFW_KEY_RIGHT_ALT; + _glfw.wl.keycodes[KEY_LEFTMETA] = GLFW_KEY_LEFT_SUPER; + _glfw.wl.keycodes[KEY_RIGHTMETA] = GLFW_KEY_RIGHT_SUPER; + _glfw.wl.keycodes[KEY_MENU] = GLFW_KEY_MENU; + _glfw.wl.keycodes[KEY_NUMLOCK] = GLFW_KEY_NUM_LOCK; + _glfw.wl.keycodes[KEY_CAPSLOCK] = GLFW_KEY_CAPS_LOCK; + _glfw.wl.keycodes[KEY_PRINT] = GLFW_KEY_PRINT_SCREEN; + _glfw.wl.keycodes[KEY_SCROLLLOCK] = GLFW_KEY_SCROLL_LOCK; + _glfw.wl.keycodes[KEY_PAUSE] = GLFW_KEY_PAUSE; + _glfw.wl.keycodes[KEY_DELETE] = GLFW_KEY_DELETE; + _glfw.wl.keycodes[KEY_BACKSPACE] = GLFW_KEY_BACKSPACE; + _glfw.wl.keycodes[KEY_ENTER] = GLFW_KEY_ENTER; + _glfw.wl.keycodes[KEY_HOME] = GLFW_KEY_HOME; + _glfw.wl.keycodes[KEY_END] = GLFW_KEY_END; + _glfw.wl.keycodes[KEY_PAGEUP] = GLFW_KEY_PAGE_UP; + _glfw.wl.keycodes[KEY_PAGEDOWN] = GLFW_KEY_PAGE_DOWN; + _glfw.wl.keycodes[KEY_INSERT] = GLFW_KEY_INSERT; + _glfw.wl.keycodes[KEY_LEFT] = GLFW_KEY_LEFT; + _glfw.wl.keycodes[KEY_RIGHT] = GLFW_KEY_RIGHT; + _glfw.wl.keycodes[KEY_DOWN] = GLFW_KEY_DOWN; + _glfw.wl.keycodes[KEY_UP] = GLFW_KEY_UP; + _glfw.wl.keycodes[KEY_F1] = GLFW_KEY_F1; + _glfw.wl.keycodes[KEY_F2] = GLFW_KEY_F2; + _glfw.wl.keycodes[KEY_F3] = GLFW_KEY_F3; + _glfw.wl.keycodes[KEY_F4] = GLFW_KEY_F4; + _glfw.wl.keycodes[KEY_F5] = GLFW_KEY_F5; + _glfw.wl.keycodes[KEY_F6] = GLFW_KEY_F6; + _glfw.wl.keycodes[KEY_F7] = GLFW_KEY_F7; + _glfw.wl.keycodes[KEY_F8] = GLFW_KEY_F8; + _glfw.wl.keycodes[KEY_F9] = GLFW_KEY_F9; + _glfw.wl.keycodes[KEY_F10] = GLFW_KEY_F10; + _glfw.wl.keycodes[KEY_F11] = GLFW_KEY_F11; + _glfw.wl.keycodes[KEY_F12] = GLFW_KEY_F12; + _glfw.wl.keycodes[KEY_F13] = GLFW_KEY_F13; + _glfw.wl.keycodes[KEY_F14] = GLFW_KEY_F14; + _glfw.wl.keycodes[KEY_F15] = GLFW_KEY_F15; + _glfw.wl.keycodes[KEY_F16] = GLFW_KEY_F16; + _glfw.wl.keycodes[KEY_F17] = GLFW_KEY_F17; + _glfw.wl.keycodes[KEY_F18] = GLFW_KEY_F18; + _glfw.wl.keycodes[KEY_F19] = GLFW_KEY_F19; + _glfw.wl.keycodes[KEY_F20] = GLFW_KEY_F20; + _glfw.wl.keycodes[KEY_F21] = GLFW_KEY_F21; + _glfw.wl.keycodes[KEY_F22] = GLFW_KEY_F22; + _glfw.wl.keycodes[KEY_F23] = GLFW_KEY_F23; + _glfw.wl.keycodes[KEY_F24] = GLFW_KEY_F24; + _glfw.wl.keycodes[KEY_KPSLASH] = GLFW_KEY_KP_DIVIDE; + _glfw.wl.keycodes[KEY_KPDOT] = GLFW_KEY_KP_MULTIPLY; + _glfw.wl.keycodes[KEY_KPMINUS] = GLFW_KEY_KP_SUBTRACT; + _glfw.wl.keycodes[KEY_KPPLUS] = GLFW_KEY_KP_ADD; + _glfw.wl.keycodes[KEY_KP0] = GLFW_KEY_KP_0; + _glfw.wl.keycodes[KEY_KP1] = GLFW_KEY_KP_1; + _glfw.wl.keycodes[KEY_KP2] = GLFW_KEY_KP_2; + _glfw.wl.keycodes[KEY_KP3] = GLFW_KEY_KP_3; + _glfw.wl.keycodes[KEY_KP4] = GLFW_KEY_KP_4; + _glfw.wl.keycodes[KEY_KP5] = GLFW_KEY_KP_5; + _glfw.wl.keycodes[KEY_KP6] = GLFW_KEY_KP_6; + _glfw.wl.keycodes[KEY_KP7] = GLFW_KEY_KP_7; + _glfw.wl.keycodes[KEY_KP8] = GLFW_KEY_KP_8; + _glfw.wl.keycodes[KEY_KP9] = GLFW_KEY_KP_9; + _glfw.wl.keycodes[KEY_KPCOMMA] = GLFW_KEY_KP_DECIMAL; + _glfw.wl.keycodes[KEY_KPEQUAL] = GLFW_KEY_KP_EQUAL; + _glfw.wl.keycodes[KEY_KPENTER] = GLFW_KEY_KP_ENTER; + + for (scancode = 0; scancode < 256; scancode++) + { + if (_glfw.wl.keycodes[scancode] > 0) + _glfw.wl.scancodes[_glfw.wl.keycodes[scancode]] = scancode; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ + _glfw.wl.display = wl_display_connect(NULL); + if (!_glfw.wl.display) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to connect to display"); + return GLFW_FALSE; + } + + _glfw.wl.registry = wl_display_get_registry(_glfw.wl.display); + wl_registry_add_listener(_glfw.wl.registry, ®istryListener, NULL); + + _glfw.wl.monitors = calloc(4, sizeof(_GLFWmonitor*)); + _glfw.wl.monitorsSize = 4; + + createKeyTables(); + + _glfw.wl.xkb.context = xkb_context_new(0); + if (!_glfw.wl.xkb.context) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to initialize xkb context"); + return GLFW_FALSE; + } + + // Sync so we got all registry objects + wl_display_roundtrip(_glfw.wl.display); + + // Sync so we got all initial output events + wl_display_roundtrip(_glfw.wl.display); + + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; + + if (!_glfwInitJoysticksLinux()) + return GLFW_FALSE; + + _glfwInitTimerPOSIX(); + + if (_glfw.wl.pointer && _glfw.wl.shm) + { + _glfw.wl.cursorTheme = wl_cursor_theme_load(NULL, 32, _glfw.wl.shm); + if (!_glfw.wl.cursorTheme) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unable to load default cursor theme\n"); + return GLFW_FALSE; + } + _glfw.wl.cursorSurface = + wl_compositor_create_surface(_glfw.wl.compositor); + } + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + _glfwTerminateEGL(); + _glfwTerminateJoysticksLinux(); + _glfwTerminateThreadLocalStoragePOSIX(); + + xkb_compose_state_unref(_glfw.wl.xkb.composeState); + xkb_keymap_unref(_glfw.wl.xkb.keymap); + xkb_state_unref(_glfw.wl.xkb.state); + xkb_context_unref(_glfw.wl.xkb.context); + + if (_glfw.wl.cursorTheme) + wl_cursor_theme_destroy(_glfw.wl.cursorTheme); + if (_glfw.wl.cursorSurface) + wl_surface_destroy(_glfw.wl.cursorSurface); + if (_glfw.wl.compositor) + wl_compositor_destroy(_glfw.wl.compositor); + if (_glfw.wl.shm) + wl_shm_destroy(_glfw.wl.shm); + if (_glfw.wl.shell) + wl_shell_destroy(_glfw.wl.shell); + if (_glfw.wl.pointer) + wl_pointer_destroy(_glfw.wl.pointer); + if (_glfw.wl.keyboard) + wl_keyboard_destroy(_glfw.wl.keyboard); + if (_glfw.wl.seat) + wl_seat_destroy(_glfw.wl.seat); + if (_glfw.wl.relativePointerManager) + zwp_relative_pointer_manager_v1_destroy(_glfw.wl.relativePointerManager); + if (_glfw.wl.pointerConstraints) + zwp_pointer_constraints_v1_destroy(_glfw.wl.pointerConstraints); + if (_glfw.wl.registry) + wl_registry_destroy(_glfw.wl.registry); + if (_glfw.wl.display) + { + wl_display_flush(_glfw.wl.display); + wl_display_disconnect(_glfw.wl.display); + } +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " Wayland EGL" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif +#if defined(__linux__) + " /dev/js" +#endif +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} + diff --git a/apps/exampleViewer/common/glfw/src/wl_monitor.c b/apps/exampleViewer/common/glfw/src/wl_monitor.c new file mode 100644 index 0000000000..7a830513b4 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wl_monitor.c @@ -0,0 +1,269 @@ +//======================================================================== +// GLFW 3.3 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include +#include + + +struct _GLFWvidmodeWayland +{ + GLFWvidmode base; + uint32_t flags; +}; + +static void geometry(void* data, + struct wl_output* output, + int32_t x, + int32_t y, + int32_t physicalWidth, + int32_t physicalHeight, + int32_t subpixel, + const char* make, + const char* model, + int32_t transform) +{ + struct _GLFWmonitor *monitor = data; + + monitor->wl.x = x; + monitor->wl.y = y; + monitor->widthMM = physicalWidth; + monitor->heightMM = physicalHeight; +} + +static void mode(void* data, + struct wl_output* output, + uint32_t flags, + int32_t width, + int32_t height, + int32_t refresh) +{ + struct _GLFWmonitor *monitor = data; + _GLFWvidmodeWayland mode = { { 0 }, }; + + mode.base.width = width; + mode.base.height = height; + mode.base.refreshRate = refresh / 1000; + mode.flags = flags; + + if (monitor->wl.modesCount + 1 >= monitor->wl.modesSize) + { + int size = monitor->wl.modesSize * 2; + _GLFWvidmodeWayland* modes = + realloc(monitor->wl.modes, + size * sizeof(_GLFWvidmodeWayland)); + monitor->wl.modes = modes; + monitor->wl.modesSize = size; + } + + monitor->wl.modes[monitor->wl.modesCount++] = mode; +} + +static void done(void* data, + struct wl_output* output) +{ + struct _GLFWmonitor *monitor = data; + + monitor->wl.done = GLFW_TRUE; +} + +static void scale(void* data, + struct wl_output* output, + int32_t factor) +{ + struct _GLFWmonitor *monitor = data; + + monitor->wl.scale = factor; +} + +static const struct wl_output_listener outputListener = { + geometry, + mode, + done, + scale, +}; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +void _glfwAddOutputWayland(uint32_t name, uint32_t version) +{ + _GLFWmonitor *monitor; + struct wl_output *output; + char nameStr[80]; + + memset(nameStr, 0, sizeof(nameStr)); + snprintf(nameStr, 79, "wl_output@%u", name); + + if (version < 2) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Unsupported output interface version"); + return; + } + + monitor = _glfwAllocMonitor(nameStr, 0, 0); + + output = wl_registry_bind(_glfw.wl.registry, + name, + &wl_output_interface, + 2); + if (!output) + { + _glfwFreeMonitor(monitor); + return; + } + + monitor->wl.modes = calloc(4, sizeof(_GLFWvidmodeWayland)); + monitor->wl.modesSize = 4; + + monitor->wl.scale = 1; + + monitor->wl.output = output; + wl_output_add_listener(output, &outputListener, monitor); + + if (_glfw.wl.monitorsCount + 1 >= _glfw.wl.monitorsSize) + { + _GLFWmonitor** monitors = _glfw.wl.monitors; + int size = _glfw.wl.monitorsSize * 2; + + monitors = realloc(monitors, size * sizeof(_GLFWmonitor*)); + + _glfw.wl.monitors = monitors; + _glfw.wl.monitorsSize = size; + } + + _glfw.wl.monitors[_glfw.wl.monitorsCount++] = monitor; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + _GLFWmonitor** monitors; + _GLFWmonitor* monitor; + int i, monitorsCount = _glfw.wl.monitorsCount; + + if (_glfw.wl.monitorsCount == 0) + goto err; + + monitors = calloc(monitorsCount, sizeof(_GLFWmonitor*)); + + for (i = 0; i < monitorsCount; i++) + { + _GLFWmonitor* origMonitor = _glfw.wl.monitors[i]; + monitor = calloc(1, sizeof(_GLFWmonitor)); + + monitor->modes = + _glfwPlatformGetVideoModes(origMonitor, + &origMonitor->wl.modesCount); + *monitor = *_glfw.wl.monitors[i]; + monitors[i] = monitor; + } + + *count = monitorsCount; + return monitors; + +err: + *count = 0; + return NULL; +} + +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + return first->wl.output == second->wl.output; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (xpos) + *xpos = monitor->wl.x; + if (ypos) + *ypos = monitor->wl.y; +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* found) +{ + GLFWvidmode *modes; + int i, modesCount = monitor->wl.modesCount; + + modes = calloc(modesCount, sizeof(GLFWvidmode)); + + for (i = 0; i < modesCount; i++) + modes[i] = monitor->wl.modes[i].base; + + *found = modesCount; + return modes; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + int i; + + for (i = 0; i < monitor->wl.modesCount; i++) + { + if (monitor->wl.modes[i].flags & WL_OUTPUT_MODE_CURRENT) + { + *mode = monitor->wl.modes[i].base; + return; + } + } +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp getting not supported yet"); +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Gamma ramp setting not supported yet"); +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return monitor->wl.output; +} + diff --git a/apps/exampleViewer/common/glfw/src/wl_platform.h b/apps/exampleViewer/common/glfw/src/wl_platform.h new file mode 100644 index 0000000000..209f7bca0b --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wl_platform.h @@ -0,0 +1,182 @@ +//======================================================================== +// GLFW 3.3 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_wayland_platform_h_ +#define _glfw3_wayland_platform_h_ + +#include +#include +#include +#include + +typedef VkFlags VkWaylandSurfaceCreateFlagsKHR; + +typedef struct VkWaylandSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkWaylandSurfaceCreateFlagsKHR flags; + struct wl_display* display; + struct wl_surface* surface; +} VkWaylandSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateWaylandSurfaceKHR)(VkInstance,const VkWaylandSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR)(VkPhysicalDevice,uint32_t,struct wl_display*); + +#include "posix_tls.h" +#include "posix_time.h" +#include "linux_joystick.h" +#include "xkb_unicode.h" +#include "egl_context.h" + +#include "wayland-relative-pointer-unstable-v1-client-protocol.h" +#include "wayland-pointer-constraints-unstable-v1-client-protocol.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->wl.native) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.wl.display) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowWayland wl +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryWayland wl +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorWayland wl +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorWayland wl + +#define _GLFW_PLATFORM_CONTEXT_STATE +#define _GLFW_PLATFORM_LIBRARY_CONTEXT_STATE + + +// Wayland-specific video mode data +// +typedef struct _GLFWvidmodeWayland _GLFWvidmodeWayland; + +// Wayland-specific per-window data +// +typedef struct _GLFWwindowWayland +{ + int width, height; + GLFWbool visible; + GLFWbool maximized; + struct wl_surface* surface; + struct wl_egl_window* native; + struct wl_shell_surface* shellSurface; + struct wl_callback* callback; + + _GLFWcursor* currentCursor; + double cursorPosX, cursorPosY; + + char* title; + + // We need to track the monitors the window spans on to calculate the + // optimal scaling factor. + int scale; + _GLFWmonitor** monitors; + int monitorsCount; + int monitorsSize; + + struct { + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + } pointerLock; +} _GLFWwindowWayland; + +// Wayland-specific global data +// +typedef struct _GLFWlibraryWayland +{ + struct wl_display* display; + struct wl_registry* registry; + struct wl_compositor* compositor; + struct wl_shell* shell; + struct wl_shm* shm; + struct wl_seat* seat; + struct wl_pointer* pointer; + struct wl_keyboard* keyboard; + struct zwp_relative_pointer_manager_v1* relativePointerManager; + struct zwp_pointer_constraints_v1* pointerConstraints; + + int compositorVersion; + + struct wl_cursor_theme* cursorTheme; + struct wl_surface* cursorSurface; + uint32_t pointerSerial; + + _GLFWmonitor** monitors; + int monitorsCount; + int monitorsSize; + + short int keycodes[256]; + short int scancodes[GLFW_KEY_LAST + 1]; + + struct { + struct xkb_context* context; + struct xkb_keymap* keymap; + struct xkb_state* state; + struct xkb_compose_state* composeState; + xkb_mod_mask_t controlMask; + xkb_mod_mask_t altMask; + xkb_mod_mask_t shiftMask; + xkb_mod_mask_t superMask; + unsigned int modifiers; + } xkb; + + _GLFWwindow* pointerFocus; + _GLFWwindow* keyboardFocus; + +} _GLFWlibraryWayland; + +// Wayland-specific per-monitor data +// +typedef struct _GLFWmonitorWayland +{ + struct wl_output* output; + + _GLFWvidmodeWayland* modes; + int modesCount; + int modesSize; + GLFWbool done; + + int x; + int y; + int scale; +} _GLFWmonitorWayland; + +// Wayland-specific per-cursor data +// +typedef struct _GLFWcursorWayland +{ + struct wl_cursor_image* image; + struct wl_buffer* buffer; + int width, height; + int xhot, yhot; +} _GLFWcursorWayland; + + +void _glfwAddOutputWayland(uint32_t name, uint32_t version); + +#endif // _glfw3_wayland_platform_h_ diff --git a/apps/exampleViewer/common/glfw/src/wl_window.c b/apps/exampleViewer/common/glfw/src/wl_window.c new file mode 100644 index 0000000000..a2bae3732f --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/wl_window.c @@ -0,0 +1,1047 @@ +//======================================================================== +// GLFW 3.3 Wayland - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define _GNU_SOURCE + +#include "internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +static void handlePing(void* data, + struct wl_shell_surface* shellSurface, + uint32_t serial) +{ + wl_shell_surface_pong(shellSurface, serial); +} + +static void handleConfigure(void* data, + struct wl_shell_surface* shellSurface, + uint32_t edges, + int32_t width, + int32_t height) +{ + _GLFWwindow* window = data; + float aspectRatio; + float targetRatio; + + if (!window->monitor) + { + if (window->numer != GLFW_DONT_CARE && window->denom != GLFW_DONT_CARE) + { + aspectRatio = (float)width / (float)height; + targetRatio = (float)window->numer / (float)window->denom; + if (aspectRatio < targetRatio) + height = width / targetRatio; + else if (aspectRatio > targetRatio) + width = height * targetRatio; + } + + if (window->minwidth != GLFW_DONT_CARE && width < window->minwidth) + width = window->minwidth; + else if (window->maxwidth != GLFW_DONT_CARE && width > window->maxwidth) + width = window->maxwidth; + + if (window->minheight != GLFW_DONT_CARE && height < window->minheight) + height = window->minheight; + else if (window->maxheight != GLFW_DONT_CARE && height > window->maxheight) + height = window->maxheight; + } + + _glfwInputWindowSize(window, width, height); + _glfwPlatformSetWindowSize(window, width, height); + _glfwInputWindowDamage(window); +} + +static void handlePopupDone(void* data, + struct wl_shell_surface* shellSurface) +{ +} + +static const struct wl_shell_surface_listener shellSurfaceListener = { + handlePing, + handleConfigure, + handlePopupDone +}; + +static void checkScaleChange(_GLFWwindow* window) +{ + int scaledWidth, scaledHeight; + int scale = 1; + int i; + int monitorScale; + + // Check if we will be able to set the buffer scale or not. + if (_glfw.wl.compositorVersion < 3) + return; + + // Get the scale factor from the highest scale monitor. + for (i = 0; i < window->wl.monitorsCount; ++i) + { + monitorScale = window->wl.monitors[i]->wl.scale; + if (scale < monitorScale) + scale = monitorScale; + } + + // Only change the framebuffer size if the scale changed. + if (scale != window->wl.scale) + { + window->wl.scale = scale; + scaledWidth = window->wl.width * scale; + scaledHeight = window->wl.height * scale; + wl_surface_set_buffer_scale(window->wl.surface, scale); + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); + } +} + +static void handleEnter(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + + if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) + { + ++window->wl.monitorsSize; + window->wl.monitors = + realloc(window->wl.monitors, + window->wl.monitorsSize * sizeof(_GLFWmonitor*)); + } + + window->wl.monitors[window->wl.monitorsCount++] = monitor; + + checkScaleChange(window); +} + +static void handleLeave(void *data, + struct wl_surface *surface, + struct wl_output *output) +{ + _GLFWwindow* window = data; + _GLFWmonitor* monitor = wl_output_get_user_data(output); + GLFWbool found; + int i; + + for (i = 0, found = GLFW_FALSE; i < window->wl.monitorsCount - 1; ++i) + { + if (monitor == window->wl.monitors[i]) + found = GLFW_TRUE; + if (found) + window->wl.monitors[i] = window->wl.monitors[i + 1]; + } + window->wl.monitors[--window->wl.monitorsCount] = NULL; + + checkScaleChange(window); +} + +static const struct wl_surface_listener surfaceListener = { + handleEnter, + handleLeave +}; + +// Makes the surface considered as XRGB instead of ARGB. +static void setOpaqueRegion(_GLFWwindow* window) +{ + struct wl_region* region; + + region = wl_compositor_create_region(_glfw.wl.compositor); + if (!region) + return; + + wl_region_add(region, 0, 0, window->wl.width, window->wl.height); + wl_surface_set_opaque_region(window->wl.surface, region); + wl_surface_commit(window->wl.surface); + wl_region_destroy(region); +} + +static GLFWbool createSurface(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig) +{ + window->wl.surface = wl_compositor_create_surface(_glfw.wl.compositor); + if (!window->wl.surface) + return GLFW_FALSE; + + wl_surface_add_listener(window->wl.surface, + &surfaceListener, + window); + + wl_surface_set_user_data(window->wl.surface, window); + + window->wl.native = wl_egl_window_create(window->wl.surface, + wndconfig->width, + wndconfig->height); + if (!window->wl.native) + return GLFW_FALSE; + + window->wl.width = wndconfig->width; + window->wl.height = wndconfig->height; + window->wl.scale = 1; + + // TODO: make this optional once issue #197 is fixed. + setOpaqueRegion(window); + + return GLFW_TRUE; +} + +static GLFWbool createShellSurface(_GLFWwindow* window) +{ + window->wl.shellSurface = wl_shell_get_shell_surface(_glfw.wl.shell, + window->wl.surface); + if (!window->wl.shellSurface) + return GLFW_FALSE; + + wl_shell_surface_add_listener(window->wl.shellSurface, + &shellSurfaceListener, + window); + + if (window->wl.title) + wl_shell_surface_set_title(window->wl.shellSurface, window->wl.title); + + if (window->monitor) + { + wl_shell_surface_set_fullscreen( + window->wl.shellSurface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + 0, + window->monitor->wl.output); + } + else if (window->wl.maximized) + { + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); + } + else + { + wl_shell_surface_set_toplevel(window->wl.shellSurface); + } + + return GLFW_TRUE; +} + +static int +createTmpfileCloexec(char* tmpname) +{ + int fd; + + fd = mkostemp(tmpname, O_CLOEXEC); + if (fd >= 0) + unlink(tmpname); + + return fd; +} + +static void +handleEvents(int timeout) +{ + struct wl_display* display = _glfw.wl.display; + struct pollfd fds[] = { + { wl_display_get_fd(display), POLLIN }, + }; + + while (wl_display_prepare_read(display) != 0) + wl_display_dispatch_pending(display); + + // If an error different from EAGAIN happens, we have likely been + // disconnected from the Wayland session, try to handle that the best we + // can. + if (wl_display_flush(display) < 0 && errno != EAGAIN) + { + _GLFWwindow* window = _glfw.windowListHead; + while (window) + { + _glfwInputWindowCloseRequest(window); + window = window->next; + } + wl_display_cancel_read(display); + return; + } + + if (poll(fds, 1, timeout) > 0) + { + wl_display_read_events(display); + wl_display_dispatch_pending(display); + } + else + { + wl_display_cancel_read(display); + } +} + +/* + * Create a new, unique, anonymous file of the given size, and + * return the file descriptor for it. The file descriptor is set + * CLOEXEC. The file is immediately suitable for mmap()'ing + * the given size at offset zero. + * + * The file should not have a permanent backing store like a disk, + * but may have if XDG_RUNTIME_DIR is not properly implemented in OS. + * + * The file name is deleted from the file system. + * + * The file is suitable for buffer sharing between processes by + * transmitting the file descriptor over Unix sockets using the + * SCM_RIGHTS methods. + * + * posix_fallocate() is used to guarantee that disk space is available + * for the file at the given size. If disk space is insufficent, errno + * is set to ENOSPC. If posix_fallocate() is not supported, program may + * receive SIGBUS on accessing mmap()'ed file contents instead. + */ +int +createAnonymousFile(off_t size) +{ + static const char template[] = "/glfw-shared-XXXXXX"; + const char* path; + char* name; + int fd; + int ret; + + path = getenv("XDG_RUNTIME_DIR"); + if (!path) + { + errno = ENOENT; + return -1; + } + + name = calloc(strlen(path) + sizeof(template), 1); + strcpy(name, path); + strcat(name, template); + + fd = createTmpfileCloexec(name); + + free(name); + + if (fd < 0) + return -1; + ret = posix_fallocate(fd, 0, size); + if (ret != 0) + { + close(fd); + errno = ret; + return -1; + } + return fd; +} + +// Translates a GLFW standard cursor to a theme cursor name +// +static char *translateCursorShape(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return "left_ptr"; + case GLFW_IBEAM_CURSOR: + return "xterm"; + case GLFW_CROSSHAIR_CURSOR: + return "crosshair"; + case GLFW_HAND_CURSOR: + return "grabbing"; + case GLFW_HRESIZE_CURSOR: + return "sb_h_double_arrow"; + case GLFW_VRESIZE_CURSOR: + return "sb_v_double_arrow"; + } + return NULL; +} + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + if (!createSurface(window, wndconfig)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + + if (wndconfig->title) + window->wl.title = strdup(wndconfig->title); + + if (wndconfig->visible) + { + if (!createShellSurface(window)) + return GLFW_FALSE; + + window->wl.visible = GLFW_TRUE; + } + else + { + window->wl.shellSurface = NULL; + window->wl.visible = GLFW_FALSE; + } + + window->wl.currentCursor = NULL; + + window->wl.monitors = calloc(1, sizeof(_GLFWmonitor*)); + window->wl.monitorsCount = 0; + window->wl.monitorsSize = 1; + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (window == _glfw.wl.pointerFocus) + { + _glfw.wl.pointerFocus = NULL; + _glfwInputCursorEnter(window, GLFW_FALSE); + } + if (window == _glfw.wl.keyboardFocus) + { + _glfw.wl.keyboardFocus = NULL; + _glfwInputWindowFocus(window, GLFW_FALSE); + } + + if (window->context.destroy) + window->context.destroy(window); + + if (window->wl.native) + wl_egl_window_destroy(window->wl.native); + + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); + + if (window->wl.surface) + wl_surface_destroy(window->wl.surface); + + free(window->wl.title); + free(window->wl.monitors); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ + if (window->wl.title) + free(window->wl.title); + window->wl.title = strdup(title); + if (window->wl.shellSurface) + wl_shell_surface_set_title(window->wl.shellSurface, title); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Setting window icon not supported"); +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + // A Wayland client is not aware of its position, so just warn and leave it + // as (0, 0) + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window position retrieval not supported"); +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + // A Wayland client can not set its position, so just warn + + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Window position setting not supported"); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + if (width) + *width = window->wl.width; + if (height) + *height = window->wl.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + int scaledWidth = width * window->wl.scale; + int scaledHeight = height * window->wl.scale; + window->wl.width = width; + window->wl.height = height; + wl_egl_window_resize(window->wl.native, scaledWidth, scaledHeight, 0, 0); + setOpaqueRegion(window); + _glfwInputFramebufferSize(window, scaledWidth, scaledHeight); +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + // TODO: find out how to trigger a resize. + // The actual limits are checked in the wl_shell_surface::configure handler. +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + // TODO: find out how to trigger a resize. + // The actual limits are checked in the wl_shell_surface::configure handler. +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); + *width *= window->wl.scale; + *height *= window->wl.scale; +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + // TODO: will need a proper implementation once decorations are + // implemented, but for now just leave everything as 0. +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + // TODO: move to xdg_shell instead of wl_shell. + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Iconify window not supported"); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + // TODO: also do the same for iconified. + if (window->monitor || window->wl.maximized) + { + if (window->wl.shellSurface) + wl_shell_surface_set_toplevel(window->wl.shellSurface); + + window->wl.maximized = GLFW_FALSE; + } +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (!window->monitor && !window->wl.maximized) + { + if (window->wl.shellSurface) + { + // Let the compositor select the best output. + wl_shell_surface_set_maximized(window->wl.shellSurface, NULL); + } + window->wl.maximized = GLFW_TRUE; + } +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + if (!window->monitor) + { + if (!window->wl.shellSurface) + createShellSurface(window); + window->wl.visible = GLFW_TRUE; + } +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + if (!window->monitor) + { + if (window->wl.shellSurface) + wl_shell_surface_destroy(window->wl.shellSurface); + window->wl.visible = GLFW_FALSE; + } +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Focusing a window requires user interaction"); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (monitor) + { + wl_shell_surface_set_fullscreen( + window->wl.shellSurface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, + refreshRate * 1000, // Convert Hz to mHz. + monitor->wl.output); + } + else + { + wl_shell_surface_set_toplevel(window->wl.shellSurface); + } + _glfwInputWindowMonitorChange(window, monitor); +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + return _glfw.wl.keyboardFocus == window; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + // TODO: move to xdg_shell, wl_shell doesn't have any iconified concept. + return GLFW_FALSE; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + return window->wl.visible; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + return window->wl.maximized; +} + +void _glfwPlatformPollEvents(void) +{ + handleEvents(0); +} + +void _glfwPlatformWaitEvents(void) +{ + handleEvents(-1); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + handleEvents((int) (timeout * 1e3)); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + wl_display_sync(_glfw.wl.display); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + if (xpos) + *xpos = window->wl.cursorPosX; + if (ypos) + *ypos = window->wl.cursorPosY; +} + +static GLFWbool isPointerLocked(_GLFWwindow* window); + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + if (isPointerLocked(window)) + { + zwp_locked_pointer_v1_set_cursor_position_hint( + window->wl.pointerLock.lockedPointer, + wl_fixed_from_double(x), wl_fixed_from_double(y)); + wl_surface_commit(window->wl.surface); + } +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + _glfwPlatformSetCursor(window, window->wl.currentCursor); +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + // TODO + return NULL; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.wl.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + struct wl_shm_pool* pool; + int stride = image->width * 4; + int length = image->width * image->height * 4; + void* data; + int fd, i; + + fd = createAnonymousFile(length); + if (fd < 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Creating a buffer file for %d B failed: %m", + length); + return GLFW_FALSE; + } + + data = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Cursor mmap failed: %m"); + close(fd); + return GLFW_FALSE; + } + + pool = wl_shm_create_pool(_glfw.wl.shm, fd, length); + + close(fd); + unsigned char* source = (unsigned char*) image->pixels; + unsigned char* target = data; + for (i = 0; i < image->width * image->height; i++, source += 4) + { + unsigned int alpha = source[3]; + + *target++ = (unsigned char) ((source[2] * alpha) / 255); + *target++ = (unsigned char) ((source[1] * alpha) / 255); + *target++ = (unsigned char) ((source[0] * alpha) / 255); + *target++ = (unsigned char) alpha; + } + + cursor->wl.buffer = + wl_shm_pool_create_buffer(pool, 0, + image->width, + image->height, + stride, WL_SHM_FORMAT_ARGB8888); + munmap(data, length); + wl_shm_pool_destroy(pool); + + cursor->wl.width = image->width; + cursor->wl.height = image->height; + cursor->wl.xhot = xhot; + cursor->wl.yhot = yhot; + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + struct wl_cursor* standardCursor; + + standardCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, + translateCursorShape(shape)); + if (!standardCursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Standard cursor \"%s\" not found", + translateCursorShape(shape)); + return GLFW_FALSE; + } + + cursor->wl.image = standardCursor->images[0]; + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + // If it's a standard cursor we don't need to do anything here + if (cursor->wl.image) + return; + + if (cursor->wl.buffer) + wl_buffer_destroy(cursor->wl.buffer); +} + +static void handleRelativeMotion(void* data, + struct zwp_relative_pointer_v1* pointer, + uint32_t timeHi, + uint32_t timeLo, + wl_fixed_t dx, + wl_fixed_t dy, + wl_fixed_t dxUnaccel, + wl_fixed_t dyUnaccel) +{ + _GLFWwindow* window = data; + + if (window->cursorMode != GLFW_CURSOR_DISABLED) + return; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + wl_fixed_to_double(dxUnaccel), + window->virtualCursorPosY + wl_fixed_to_double(dyUnaccel)); +} + +static const struct zwp_relative_pointer_v1_listener relativePointerListener = { + handleRelativeMotion +}; + +static void handleLocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static void unlockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer = + window->wl.pointerLock.relativePointer; + struct zwp_locked_pointer_v1* lockedPointer = + window->wl.pointerLock.lockedPointer; + + zwp_relative_pointer_v1_destroy(relativePointer); + zwp_locked_pointer_v1_destroy(lockedPointer); + + window->wl.pointerLock.relativePointer = NULL; + window->wl.pointerLock.lockedPointer = NULL; +} + +static void lockPointer(_GLFWwindow* window); + +static void handleUnlocked(void* data, + struct zwp_locked_pointer_v1* lockedPointer) +{ +} + +static const struct zwp_locked_pointer_v1_listener lockedPointerListener = { + handleLocked, + handleUnlocked +}; + +static void lockPointer(_GLFWwindow* window) +{ + struct zwp_relative_pointer_v1* relativePointer; + struct zwp_locked_pointer_v1* lockedPointer; + + if (!_glfw.wl.relativePointerManager) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: no relative pointer manager"); + return; + } + + relativePointer = + zwp_relative_pointer_manager_v1_get_relative_pointer( + _glfw.wl.relativePointerManager, + _glfw.wl.pointer); + zwp_relative_pointer_v1_add_listener(relativePointer, + &relativePointerListener, + window); + + lockedPointer = + zwp_pointer_constraints_v1_lock_pointer( + _glfw.wl.pointerConstraints, + window->wl.surface, + _glfw.wl.pointer, + NULL, + ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT); + zwp_locked_pointer_v1_add_listener(lockedPointer, + &lockedPointerListener, + window); + + window->wl.pointerLock.relativePointer = relativePointer; + window->wl.pointerLock.lockedPointer = lockedPointer; + + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); +} + +static GLFWbool isPointerLocked(_GLFWwindow* window) +{ + return window->wl.pointerLock.lockedPointer != NULL; +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + struct wl_buffer* buffer; + struct wl_cursor* defaultCursor; + struct wl_cursor_image* image; + struct wl_surface* surface = _glfw.wl.cursorSurface; + + if (!_glfw.wl.pointer) + return; + + window->wl.currentCursor = cursor; + + // If we're not in the correct window just save the cursor + // the next time the pointer enters the window the cursor will change + if (window != _glfw.wl.pointerFocus) + return; + + // Unlock possible pointer lock if no longer disabled. + if (window->cursorMode != GLFW_CURSOR_DISABLED && isPointerLocked(window)) + unlockPointer(window); + + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (cursor) + image = cursor->wl.image; + else + { + defaultCursor = wl_cursor_theme_get_cursor(_glfw.wl.cursorTheme, + "left_ptr"); + if (!defaultCursor) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Standard cursor not found"); + return; + } + image = defaultCursor->images[0]; + } + + if (image) + { + buffer = wl_cursor_image_get_buffer(image); + if (!buffer) + return; + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + surface, + image->hotspot_x, + image->hotspot_y); + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + image->width, image->height); + wl_surface_commit(surface); + } + else + { + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + surface, + cursor->wl.xhot, + cursor->wl.yhot); + wl_surface_attach(surface, cursor->wl.buffer, 0, 0); + wl_surface_damage(surface, 0, 0, + cursor->wl.width, cursor->wl.height); + wl_surface_commit(surface); + } + } + else if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (!isPointerLocked(window)) + lockPointer(window); + } + else if (window->cursorMode == GLFW_CURSOR_HIDDEN) + { + wl_pointer_set_cursor(_glfw.wl.pointer, _glfw.wl.pointerSerial, + NULL, 0, 0); + } +} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard setting not implemented yet"); +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + // TODO + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Clipboard getting not implemented yet"); + return NULL; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface || !_glfw.vk.KHR_wayland_surface) + return; + + extensions[0] = "VK_KHR_surface"; + extensions[1] = "VK_KHR_wayland_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR vkGetPhysicalDeviceWaylandPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWaylandPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceWaylandPresentationSupportKHR"); + if (!vkGetPhysicalDeviceWaylandPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); + return VK_NULL_HANDLE; + } + + return vkGetPhysicalDeviceWaylandPresentationSupportKHR(device, + queuefamily, + _glfw.wl.display); +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + VkResult err; + VkWaylandSurfaceCreateInfoKHR sci; + PFN_vkCreateWaylandSurfaceKHR vkCreateWaylandSurfaceKHR; + + vkCreateWaylandSurfaceKHR = (PFN_vkCreateWaylandSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateWaylandSurfaceKHR"); + if (!vkCreateWaylandSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "Wayland: Vulkan instance missing VK_KHR_wayland_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + sci.display = _glfw.wl.display; + sci.surface = window->wl.surface; + + err = vkCreateWaylandSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Wayland: Failed to create Vulkan surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.wl.display; +} + +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return window->wl.surface; +} + diff --git a/apps/exampleViewer/common/glfw/src/x11_init.c b/apps/exampleViewer/common/glfw/src/x11_init.c new file mode 100644 index 0000000000..012ab5b917 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/x11_init.c @@ -0,0 +1,859 @@ +//======================================================================== +// GLFW 3.3 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include + +#include +#include +#include +#include +#include + + +// Translate an X11 key code to a GLFW key code. +// +static int translateKeyCode(int scancode) +{ + int keySym; + + // Valid key code range is [8,255], according to the Xlib manual + if (scancode < 8 || scancode > 255) + return GLFW_KEY_UNKNOWN; + + if (_glfw.x11.xkb.available) + { + // Try secondary keysym, for numeric keypad keys + // Note: This way we always force "NumLock = ON", which is intentional + // since the returned key code should correspond to a physical + // location. + keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 1); + switch (keySym) + { + case XK_KP_0: return GLFW_KEY_KP_0; + case XK_KP_1: return GLFW_KEY_KP_1; + case XK_KP_2: return GLFW_KEY_KP_2; + case XK_KP_3: return GLFW_KEY_KP_3; + case XK_KP_4: return GLFW_KEY_KP_4; + case XK_KP_5: return GLFW_KEY_KP_5; + case XK_KP_6: return GLFW_KEY_KP_6; + case XK_KP_7: return GLFW_KEY_KP_7; + case XK_KP_8: return GLFW_KEY_KP_8; + case XK_KP_9: return GLFW_KEY_KP_9; + case XK_KP_Separator: + case XK_KP_Decimal: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + default: break; + } + + // Now try primary keysym for function keys (non-printable keys) + // These should not depend on the current keyboard layout + keySym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); + } + else + { + int dummy; + KeySym* keySyms; + + keySyms = XGetKeyboardMapping(_glfw.x11.display, scancode, 1, &dummy); + keySym = keySyms[0]; + XFree(keySyms); + } + + switch (keySym) + { + case XK_Escape: return GLFW_KEY_ESCAPE; + case XK_Tab: return GLFW_KEY_TAB; + case XK_Shift_L: return GLFW_KEY_LEFT_SHIFT; + case XK_Shift_R: return GLFW_KEY_RIGHT_SHIFT; + case XK_Control_L: return GLFW_KEY_LEFT_CONTROL; + case XK_Control_R: return GLFW_KEY_RIGHT_CONTROL; + case XK_Meta_L: + case XK_Alt_L: return GLFW_KEY_LEFT_ALT; + case XK_Mode_switch: // Mapped to Alt_R on many keyboards + case XK_ISO_Level3_Shift: // AltGr on at least some machines + case XK_Meta_R: + case XK_Alt_R: return GLFW_KEY_RIGHT_ALT; + case XK_Super_L: return GLFW_KEY_LEFT_SUPER; + case XK_Super_R: return GLFW_KEY_RIGHT_SUPER; + case XK_Menu: return GLFW_KEY_MENU; + case XK_Num_Lock: return GLFW_KEY_NUM_LOCK; + case XK_Caps_Lock: return GLFW_KEY_CAPS_LOCK; + case XK_Print: return GLFW_KEY_PRINT_SCREEN; + case XK_Scroll_Lock: return GLFW_KEY_SCROLL_LOCK; + case XK_Pause: return GLFW_KEY_PAUSE; + case XK_Delete: return GLFW_KEY_DELETE; + case XK_BackSpace: return GLFW_KEY_BACKSPACE; + case XK_Return: return GLFW_KEY_ENTER; + case XK_Home: return GLFW_KEY_HOME; + case XK_End: return GLFW_KEY_END; + case XK_Page_Up: return GLFW_KEY_PAGE_UP; + case XK_Page_Down: return GLFW_KEY_PAGE_DOWN; + case XK_Insert: return GLFW_KEY_INSERT; + case XK_Left: return GLFW_KEY_LEFT; + case XK_Right: return GLFW_KEY_RIGHT; + case XK_Down: return GLFW_KEY_DOWN; + case XK_Up: return GLFW_KEY_UP; + case XK_F1: return GLFW_KEY_F1; + case XK_F2: return GLFW_KEY_F2; + case XK_F3: return GLFW_KEY_F3; + case XK_F4: return GLFW_KEY_F4; + case XK_F5: return GLFW_KEY_F5; + case XK_F6: return GLFW_KEY_F6; + case XK_F7: return GLFW_KEY_F7; + case XK_F8: return GLFW_KEY_F8; + case XK_F9: return GLFW_KEY_F9; + case XK_F10: return GLFW_KEY_F10; + case XK_F11: return GLFW_KEY_F11; + case XK_F12: return GLFW_KEY_F12; + case XK_F13: return GLFW_KEY_F13; + case XK_F14: return GLFW_KEY_F14; + case XK_F15: return GLFW_KEY_F15; + case XK_F16: return GLFW_KEY_F16; + case XK_F17: return GLFW_KEY_F17; + case XK_F18: return GLFW_KEY_F18; + case XK_F19: return GLFW_KEY_F19; + case XK_F20: return GLFW_KEY_F20; + case XK_F21: return GLFW_KEY_F21; + case XK_F22: return GLFW_KEY_F22; + case XK_F23: return GLFW_KEY_F23; + case XK_F24: return GLFW_KEY_F24; + case XK_F25: return GLFW_KEY_F25; + + // Numeric keypad + case XK_KP_Divide: return GLFW_KEY_KP_DIVIDE; + case XK_KP_Multiply: return GLFW_KEY_KP_MULTIPLY; + case XK_KP_Subtract: return GLFW_KEY_KP_SUBTRACT; + case XK_KP_Add: return GLFW_KEY_KP_ADD; + + // These should have been detected in secondary keysym test above! + case XK_KP_Insert: return GLFW_KEY_KP_0; + case XK_KP_End: return GLFW_KEY_KP_1; + case XK_KP_Down: return GLFW_KEY_KP_2; + case XK_KP_Page_Down: return GLFW_KEY_KP_3; + case XK_KP_Left: return GLFW_KEY_KP_4; + case XK_KP_Right: return GLFW_KEY_KP_6; + case XK_KP_Home: return GLFW_KEY_KP_7; + case XK_KP_Up: return GLFW_KEY_KP_8; + case XK_KP_Page_Up: return GLFW_KEY_KP_9; + case XK_KP_Delete: return GLFW_KEY_KP_DECIMAL; + case XK_KP_Equal: return GLFW_KEY_KP_EQUAL; + case XK_KP_Enter: return GLFW_KEY_KP_ENTER; + + // Last resort: Check for printable keys (should not happen if the XKB + // extension is available). This will give a layout dependent mapping + // (which is wrong, and we may miss some keys, especially on non-US + // keyboards), but it's better than nothing... + case XK_a: return GLFW_KEY_A; + case XK_b: return GLFW_KEY_B; + case XK_c: return GLFW_KEY_C; + case XK_d: return GLFW_KEY_D; + case XK_e: return GLFW_KEY_E; + case XK_f: return GLFW_KEY_F; + case XK_g: return GLFW_KEY_G; + case XK_h: return GLFW_KEY_H; + case XK_i: return GLFW_KEY_I; + case XK_j: return GLFW_KEY_J; + case XK_k: return GLFW_KEY_K; + case XK_l: return GLFW_KEY_L; + case XK_m: return GLFW_KEY_M; + case XK_n: return GLFW_KEY_N; + case XK_o: return GLFW_KEY_O; + case XK_p: return GLFW_KEY_P; + case XK_q: return GLFW_KEY_Q; + case XK_r: return GLFW_KEY_R; + case XK_s: return GLFW_KEY_S; + case XK_t: return GLFW_KEY_T; + case XK_u: return GLFW_KEY_U; + case XK_v: return GLFW_KEY_V; + case XK_w: return GLFW_KEY_W; + case XK_x: return GLFW_KEY_X; + case XK_y: return GLFW_KEY_Y; + case XK_z: return GLFW_KEY_Z; + case XK_1: return GLFW_KEY_1; + case XK_2: return GLFW_KEY_2; + case XK_3: return GLFW_KEY_3; + case XK_4: return GLFW_KEY_4; + case XK_5: return GLFW_KEY_5; + case XK_6: return GLFW_KEY_6; + case XK_7: return GLFW_KEY_7; + case XK_8: return GLFW_KEY_8; + case XK_9: return GLFW_KEY_9; + case XK_0: return GLFW_KEY_0; + case XK_space: return GLFW_KEY_SPACE; + case XK_minus: return GLFW_KEY_MINUS; + case XK_equal: return GLFW_KEY_EQUAL; + case XK_bracketleft: return GLFW_KEY_LEFT_BRACKET; + case XK_bracketright: return GLFW_KEY_RIGHT_BRACKET; + case XK_backslash: return GLFW_KEY_BACKSLASH; + case XK_semicolon: return GLFW_KEY_SEMICOLON; + case XK_apostrophe: return GLFW_KEY_APOSTROPHE; + case XK_grave: return GLFW_KEY_GRAVE_ACCENT; + case XK_comma: return GLFW_KEY_COMMA; + case XK_period: return GLFW_KEY_PERIOD; + case XK_slash: return GLFW_KEY_SLASH; + case XK_less: return GLFW_KEY_WORLD_1; // At least in some layouts... + default: break; + } + + // No matching translation was found + return GLFW_KEY_UNKNOWN; +} + +// Create key code translation tables +// +static void createKeyTables(void) +{ + int scancode, key; + + memset(_glfw.x11.keycodes, -1, sizeof(_glfw.x11.keycodes)); + memset(_glfw.x11.scancodes, -1, sizeof(_glfw.x11.scancodes)); + + if (_glfw.x11.xkb.available) + { + // Use XKB to determine physical key locations independently of the current + // keyboard layout + + char name[XkbKeyNameLength + 1]; + XkbDescPtr desc = XkbGetMap(_glfw.x11.display, 0, XkbUseCoreKbd); + XkbGetNames(_glfw.x11.display, XkbKeyNamesMask, desc); + + // Find the X11 key code -> GLFW key code mapping + for (scancode = desc->min_key_code; scancode <= desc->max_key_code; scancode++) + { + memcpy(name, desc->names->keys[scancode].name, XkbKeyNameLength); + name[XkbKeyNameLength] = '\0'; + + // Map the key name to a GLFW key code. Note: We only map printable + // keys here, and we use the US keyboard layout. The rest of the + // keys (function keys) are mapped using traditional KeySym + // translations. + if (strcmp(name, "TLDE") == 0) key = GLFW_KEY_GRAVE_ACCENT; + else if (strcmp(name, "AE01") == 0) key = GLFW_KEY_1; + else if (strcmp(name, "AE02") == 0) key = GLFW_KEY_2; + else if (strcmp(name, "AE03") == 0) key = GLFW_KEY_3; + else if (strcmp(name, "AE04") == 0) key = GLFW_KEY_4; + else if (strcmp(name, "AE05") == 0) key = GLFW_KEY_5; + else if (strcmp(name, "AE06") == 0) key = GLFW_KEY_6; + else if (strcmp(name, "AE07") == 0) key = GLFW_KEY_7; + else if (strcmp(name, "AE08") == 0) key = GLFW_KEY_8; + else if (strcmp(name, "AE09") == 0) key = GLFW_KEY_9; + else if (strcmp(name, "AE10") == 0) key = GLFW_KEY_0; + else if (strcmp(name, "AE11") == 0) key = GLFW_KEY_MINUS; + else if (strcmp(name, "AE12") == 0) key = GLFW_KEY_EQUAL; + else if (strcmp(name, "AD01") == 0) key = GLFW_KEY_Q; + else if (strcmp(name, "AD02") == 0) key = GLFW_KEY_W; + else if (strcmp(name, "AD03") == 0) key = GLFW_KEY_E; + else if (strcmp(name, "AD04") == 0) key = GLFW_KEY_R; + else if (strcmp(name, "AD05") == 0) key = GLFW_KEY_T; + else if (strcmp(name, "AD06") == 0) key = GLFW_KEY_Y; + else if (strcmp(name, "AD07") == 0) key = GLFW_KEY_U; + else if (strcmp(name, "AD08") == 0) key = GLFW_KEY_I; + else if (strcmp(name, "AD09") == 0) key = GLFW_KEY_O; + else if (strcmp(name, "AD10") == 0) key = GLFW_KEY_P; + else if (strcmp(name, "AD11") == 0) key = GLFW_KEY_LEFT_BRACKET; + else if (strcmp(name, "AD12") == 0) key = GLFW_KEY_RIGHT_BRACKET; + else if (strcmp(name, "AC01") == 0) key = GLFW_KEY_A; + else if (strcmp(name, "AC02") == 0) key = GLFW_KEY_S; + else if (strcmp(name, "AC03") == 0) key = GLFW_KEY_D; + else if (strcmp(name, "AC04") == 0) key = GLFW_KEY_F; + else if (strcmp(name, "AC05") == 0) key = GLFW_KEY_G; + else if (strcmp(name, "AC06") == 0) key = GLFW_KEY_H; + else if (strcmp(name, "AC07") == 0) key = GLFW_KEY_J; + else if (strcmp(name, "AC08") == 0) key = GLFW_KEY_K; + else if (strcmp(name, "AC09") == 0) key = GLFW_KEY_L; + else if (strcmp(name, "AC10") == 0) key = GLFW_KEY_SEMICOLON; + else if (strcmp(name, "AC11") == 0) key = GLFW_KEY_APOSTROPHE; + else if (strcmp(name, "AB01") == 0) key = GLFW_KEY_Z; + else if (strcmp(name, "AB02") == 0) key = GLFW_KEY_X; + else if (strcmp(name, "AB03") == 0) key = GLFW_KEY_C; + else if (strcmp(name, "AB04") == 0) key = GLFW_KEY_V; + else if (strcmp(name, "AB05") == 0) key = GLFW_KEY_B; + else if (strcmp(name, "AB06") == 0) key = GLFW_KEY_N; + else if (strcmp(name, "AB07") == 0) key = GLFW_KEY_M; + else if (strcmp(name, "AB08") == 0) key = GLFW_KEY_COMMA; + else if (strcmp(name, "AB09") == 0) key = GLFW_KEY_PERIOD; + else if (strcmp(name, "AB10") == 0) key = GLFW_KEY_SLASH; + else if (strcmp(name, "BKSL") == 0) key = GLFW_KEY_BACKSLASH; + else if (strcmp(name, "LSGT") == 0) key = GLFW_KEY_WORLD_1; + else key = GLFW_KEY_UNKNOWN; + + if ((scancode >= 0) && (scancode < 256)) + _glfw.x11.keycodes[scancode] = key; + } + + XkbFreeNames(desc, XkbKeyNamesMask, True); + XkbFreeKeyboard(desc, 0, True); + } + + for (scancode = 0; scancode < 256; scancode++) + { + // Translate the un-translated key codes using traditional X11 KeySym + // lookups + if (_glfw.x11.keycodes[scancode] < 0) + _glfw.x11.keycodes[scancode] = translateKeyCode(scancode); + + // Store the reverse translation for faster key name lookup + if (_glfw.x11.keycodes[scancode] > 0) + _glfw.x11.scancodes[_glfw.x11.keycodes[scancode]] = scancode; + } +} + +// Check whether the IM has a usable style +// +static GLFWbool hasUsableInputMethodStyle(void) +{ + unsigned int i; + GLFWbool found = GLFW_FALSE; + XIMStyles* styles = NULL; + + if (XGetIMValues(_glfw.x11.im, XNQueryInputStyle, &styles, NULL) != NULL) + return GLFW_FALSE; + + for (i = 0; i < styles->count_styles; i++) + { + if (styles->supported_styles[i] == (XIMPreeditNothing | XIMStatusNothing)) + { + found = GLFW_TRUE; + break; + } + } + + XFree(styles); + return found; +} + +// Check whether the specified atom is supported +// +static Atom getSupportedAtom(Atom* supportedAtoms, + unsigned long atomCount, + const char* atomName) +{ + unsigned long i; + const Atom atom = XInternAtom(_glfw.x11.display, atomName, False); + + for (i = 0; i < atomCount; i++) + { + if (supportedAtoms[i] == atom) + return atom; + } + + return None; +} + +// Check whether the running window manager is EWMH-compliant +// +static void detectEWMH(void) +{ + Window* windowFromRoot = NULL; + Window* windowFromChild = NULL; + + // First we need a couple of atoms + const Atom supportingWmCheck = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTING_WM_CHECK", False); + const Atom wmSupported = + XInternAtom(_glfw.x11.display, "_NET_SUPPORTED", False); + + // Then we look for the _NET_SUPPORTING_WM_CHECK property of the root window + if (_glfwGetWindowPropertyX11(_glfw.x11.root, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromRoot) != 1) + { + if (windowFromRoot) + XFree(windowFromRoot); + return; + } + + _glfwGrabErrorHandlerX11(); + + // It should be the ID of a child window (of the root) + // Then we look for the same property on the child window + if (_glfwGetWindowPropertyX11(*windowFromRoot, + supportingWmCheck, + XA_WINDOW, + (unsigned char**) &windowFromChild) != 1) + { + XFree(windowFromRoot); + if (windowFromChild) + XFree(windowFromChild); + return; + } + + _glfwReleaseErrorHandlerX11(); + + // It should be the ID of that same child window + if (*windowFromRoot != *windowFromChild) + { + XFree(windowFromRoot); + XFree(windowFromChild); + return; + } + + XFree(windowFromRoot); + XFree(windowFromChild); + + // We are now fairly sure that an EWMH-compliant window manager is running + + Atom* supportedAtoms; + unsigned long atomCount; + + // Now we need to check the _NET_SUPPORTED property of the root window + // It should be a list of supported WM protocol and state atoms + atomCount = _glfwGetWindowPropertyX11(_glfw.x11.root, + wmSupported, + XA_ATOM, + (unsigned char**) &supportedAtoms); + + // See which of the atoms we support that are supported by the WM + _glfw.x11.NET_WM_STATE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE"); + _glfw.x11.NET_WM_STATE_ABOVE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_ABOVE"); + _glfw.x11.NET_WM_STATE_FULLSCREEN = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_FULLSCREEN"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_VERT"); + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_STATE_MAXIMIZED_HORZ"); + _glfw.x11.NET_WM_FULLSCREEN_MONITORS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_FULLSCREEN_MONITORS"); + _glfw.x11.NET_WM_WINDOW_TYPE = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE"); + _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL = + getSupportedAtom(supportedAtoms, atomCount, "_NET_WM_WINDOW_TYPE_NORMAL"); + _glfw.x11.NET_ACTIVE_WINDOW = + getSupportedAtom(supportedAtoms, atomCount, "_NET_ACTIVE_WINDOW"); + _glfw.x11.NET_FRAME_EXTENTS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_FRAME_EXTENTS"); + _glfw.x11.NET_REQUEST_FRAME_EXTENTS = + getSupportedAtom(supportedAtoms, atomCount, "_NET_REQUEST_FRAME_EXTENTS"); + + XFree(supportedAtoms); +} + +// Initialize X11 display and look for supported X11 extensions +// +static GLFWbool initExtensions(void) +{ +#if defined(_GLFW_HAS_XF86VM) + // Check for XF86VidMode extension + _glfw.x11.vidmode.available = + XF86VidModeQueryExtension(_glfw.x11.display, + &_glfw.x11.vidmode.eventBase, + &_glfw.x11.vidmode.errorBase); +#endif /*_GLFW_HAS_XF86VM*/ + + // Check for RandR extension + if (XRRQueryExtension(_glfw.x11.display, + &_glfw.x11.randr.eventBase, + &_glfw.x11.randr.errorBase)) + { + if (XRRQueryVersion(_glfw.x11.display, + &_glfw.x11.randr.major, + &_glfw.x11.randr.minor)) + { + // The GLFW RandR path requires at least version 1.3 + if (_glfw.x11.randr.major > 1 || _glfw.x11.randr.minor >= 3) + _glfw.x11.randr.available = GLFW_TRUE; + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to query RandR version"); + } + } + + if (_glfw.x11.randr.available) + { + XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, + _glfw.x11.root); + + if (!sr->ncrtc || !XRRGetCrtcGammaSize(_glfw.x11.display, sr->crtcs[0])) + { + // This is either a headless system or an older Nvidia binary driver + // with broken gamma support + // Flag it as useless and fall back to Xf86VidMode gamma, if + // available + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: RandR gamma ramp support seems broken"); + _glfw.x11.randr.gammaBroken = GLFW_TRUE; + } + + XRRFreeScreenResources(sr); + + XRRSelectInput(_glfw.x11.display, _glfw.x11.root, + RROutputChangeNotifyMask); + } + + if (XineramaQueryExtension(_glfw.x11.display, + &_glfw.x11.xinerama.major, + &_glfw.x11.xinerama.minor)) + { + if (XineramaIsActive(_glfw.x11.display)) + _glfw.x11.xinerama.available = GLFW_TRUE; + } + + // Check if Xkb is supported on this display + _glfw.x11.xkb.major = 1; + _glfw.x11.xkb.minor = 0; + _glfw.x11.xkb.available = + XkbQueryExtension(_glfw.x11.display, + &_glfw.x11.xkb.majorOpcode, + &_glfw.x11.xkb.eventBase, + &_glfw.x11.xkb.errorBase, + &_glfw.x11.xkb.major, + &_glfw.x11.xkb.minor); + + if (_glfw.x11.xkb.available) + { + Bool supported; + + if (XkbSetDetectableAutoRepeat(_glfw.x11.display, True, &supported)) + { + if (supported) + _glfw.x11.xkb.detectable = GLFW_TRUE; + } + } + + _glfw.x11.x11xcb.handle = dlopen("libX11-xcb.so", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.x11xcb.handle) + { + _glfw.x11.x11xcb.XGetXCBConnection = (XGETXCBCONNECTION_T) + dlsym(_glfw.x11.x11xcb.handle, "XGetXCBConnection"); + } + + // Update the key code LUT + // FIXME: We should listen to XkbMapNotify events to track changes to + // the keyboard mapping. + createKeyTables(); + + // Detect whether an EWMH-conformant window manager is running + detectEWMH(); + + // String format atoms + _glfw.x11.NULL_ = XInternAtom(_glfw.x11.display, "NULL", False); + _glfw.x11.UTF8_STRING = + XInternAtom(_glfw.x11.display, "UTF8_STRING", False); + _glfw.x11.COMPOUND_STRING = + XInternAtom(_glfw.x11.display, "COMPOUND_STRING", False); + _glfw.x11.ATOM_PAIR = XInternAtom(_glfw.x11.display, "ATOM_PAIR", False); + + // Custom selection property atom + _glfw.x11.GLFW_SELECTION = + XInternAtom(_glfw.x11.display, "GLFW_SELECTION", False); + + // ICCCM standard clipboard atoms + _glfw.x11.TARGETS = XInternAtom(_glfw.x11.display, "TARGETS", False); + _glfw.x11.MULTIPLE = XInternAtom(_glfw.x11.display, "MULTIPLE", False); + _glfw.x11.CLIPBOARD = XInternAtom(_glfw.x11.display, "CLIPBOARD", False); + + // Clipboard manager atoms + _glfw.x11.CLIPBOARD_MANAGER = + XInternAtom(_glfw.x11.display, "CLIPBOARD_MANAGER", False); + _glfw.x11.SAVE_TARGETS = + XInternAtom(_glfw.x11.display, "SAVE_TARGETS", False); + + // Xdnd (drag and drop) atoms + _glfw.x11.XdndAware = XInternAtom(_glfw.x11.display, "XdndAware", False); + _glfw.x11.XdndEnter = XInternAtom(_glfw.x11.display, "XdndEnter", False); + _glfw.x11.XdndPosition = XInternAtom(_glfw.x11.display, "XdndPosition", False); + _glfw.x11.XdndStatus = XInternAtom(_glfw.x11.display, "XdndStatus", False); + _glfw.x11.XdndActionCopy = XInternAtom(_glfw.x11.display, "XdndActionCopy", False); + _glfw.x11.XdndDrop = XInternAtom(_glfw.x11.display, "XdndDrop", False); + _glfw.x11.XdndLeave = XInternAtom(_glfw.x11.display, "XdndLeave", False); + _glfw.x11.XdndFinished = XInternAtom(_glfw.x11.display, "XdndFinished", False); + _glfw.x11.XdndSelection = XInternAtom(_glfw.x11.display, "XdndSelection", False); + + // ICCCM, EWMH and Motif window property atoms + // These can be set safely even without WM support + // The EWMH atoms that require WM support are handled in detectEWMH + _glfw.x11.WM_PROTOCOLS = + XInternAtom(_glfw.x11.display, "WM_PROTOCOLS", False); + _glfw.x11.WM_STATE = + XInternAtom(_glfw.x11.display, "WM_STATE", False); + _glfw.x11.WM_DELETE_WINDOW = + XInternAtom(_glfw.x11.display, "WM_DELETE_WINDOW", False); + _glfw.x11.NET_WM_ICON = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON", False); + _glfw.x11.NET_WM_PING = + XInternAtom(_glfw.x11.display, "_NET_WM_PING", False); + _glfw.x11.NET_WM_PID = + XInternAtom(_glfw.x11.display, "_NET_WM_PID", False); + _glfw.x11.NET_WM_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_NAME", False); + _glfw.x11.NET_WM_ICON_NAME = + XInternAtom(_glfw.x11.display, "_NET_WM_ICON_NAME", False); + _glfw.x11.NET_WM_BYPASS_COMPOSITOR = + XInternAtom(_glfw.x11.display, "_NET_WM_BYPASS_COMPOSITOR", False); + _glfw.x11.MOTIF_WM_HINTS = + XInternAtom(_glfw.x11.display, "_MOTIF_WM_HINTS", False); + + return GLFW_TRUE; +} + +// Create a blank cursor for hidden and disabled cursor modes +// +static Cursor createHiddenCursor(void) +{ + unsigned char pixels[16 * 16 * 4]; + GLFWimage image = { 16, 16, pixels }; + + memset(pixels, 0, sizeof(pixels)); + + return _glfwCreateCursorX11(&image, 0, 0); +} + +// Create a helper window for IPC +// +static Window createHelperWindow(void) +{ + XSetWindowAttributes wa; + wa.event_mask = PropertyChangeMask; + + return XCreateWindow(_glfw.x11.display, _glfw.x11.root, + 0, 0, 1, 1, 0, 0, + InputOnly, + DefaultVisual(_glfw.x11.display, _glfw.x11.screen), + CWEventMask, &wa); +} + +// X error handler +// +static int errorHandler(Display *display, XErrorEvent* event) +{ + _glfw.x11.errorCode = event->error_code; + return 0; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Sets the X error handler callback +// +void _glfwGrabErrorHandlerX11(void) +{ + _glfw.x11.errorCode = Success; + XSetErrorHandler(errorHandler); +} + +// Clears the X error handler callback +// +void _glfwReleaseErrorHandlerX11(void) +{ + // Synchronize to make sure all commands are processed + XSync(_glfw.x11.display, False); + XSetErrorHandler(NULL); +} + +// Reports the specified error, appending information about the last X error +// +void _glfwInputErrorX11(int error, const char* message) +{ + char buffer[8192]; + XGetErrorText(_glfw.x11.display, _glfw.x11.errorCode, + buffer, sizeof(buffer)); + + _glfwInputError(error, "%s: %s", message, buffer); +} + +// Creates a native cursor object from the specified image and hotspot +// +Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot) +{ + int i; + Cursor cursor; + + XcursorImage* native = XcursorImageCreate(image->width, image->height); + if (native == NULL) + return None; + + native->xhot = xhot; + native->yhot = yhot; + + unsigned char* source = (unsigned char*) image->pixels; + XcursorPixel* target = native->pixels; + + for (i = 0; i < image->width * image->height; i++, target++, source += 4) + { + unsigned int alpha = source[3]; + + *target = (alpha << 24) | + ((unsigned char) ((source[0] * alpha) / 255) << 16) | + ((unsigned char) ((source[1] * alpha) / 255) << 8) | + ((unsigned char) ((source[2] * alpha) / 255) << 0); + } + + cursor = XcursorImageLoadCursor(_glfw.x11.display, native); + XcursorImageDestroy(native); + + return cursor; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformInit(void) +{ +#if !defined(X_HAVE_UTF8_STRING) + // HACK: If the current locale is C, apply the environment's locale + // This is done because the C locale breaks wide character input + if (strcmp(setlocale(LC_CTYPE, NULL), "C") == 0) + setlocale(LC_CTYPE, ""); +#endif + + XInitThreads(); + + _glfw.x11.display = XOpenDisplay(NULL); + if (!_glfw.x11.display) + { + const char* display = getenv("DISPLAY"); + if (display) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to open display %s", display); + } + else + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: The DISPLAY environment variable is missing"); + } + + return GLFW_FALSE; + } + + _glfw.x11.screen = DefaultScreen(_glfw.x11.display); + _glfw.x11.root = RootWindow(_glfw.x11.display, _glfw.x11.screen); + _glfw.x11.context = XUniqueContext(); + _glfw.x11.helperWindowHandle = createHelperWindow(); + _glfw.x11.hiddenCursorHandle = createHiddenCursor(); + + if (!initExtensions()) + return GLFW_FALSE; + + if (XSupportsLocale()) + { + XSetLocaleModifiers(""); + + _glfw.x11.im = XOpenIM(_glfw.x11.display, 0, NULL, NULL); + if (_glfw.x11.im) + { + if (!hasUsableInputMethodStyle()) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + } + } + + if (!_glfwInitThreadLocalStoragePOSIX()) + return GLFW_FALSE; + + if (!_glfwInitJoysticksLinux()) + return GLFW_FALSE; + + _glfwInitTimerPOSIX(); + + return GLFW_TRUE; +} + +void _glfwPlatformTerminate(void) +{ + if (_glfw.x11.x11xcb.handle) + { + dlclose(_glfw.x11.x11xcb.handle); + _glfw.x11.x11xcb.handle = NULL; + } + + if (_glfw.x11.helperWindowHandle) + { + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == + _glfw.x11.helperWindowHandle) + { + _glfwPushSelectionToManagerX11(); + } + + XDestroyWindow(_glfw.x11.display, _glfw.x11.helperWindowHandle); + _glfw.x11.helperWindowHandle = None; + } + + if (_glfw.x11.hiddenCursorHandle) + { + XFreeCursor(_glfw.x11.display, _glfw.x11.hiddenCursorHandle); + _glfw.x11.hiddenCursorHandle = (Cursor) 0; + } + + free(_glfw.x11.clipboardString); + + if (_glfw.x11.im) + { + XCloseIM(_glfw.x11.im); + _glfw.x11.im = NULL; + } + + _glfwTerminateEGL(); + + if (_glfw.x11.display) + { + XCloseDisplay(_glfw.x11.display); + _glfw.x11.display = NULL; + } + + // NOTE: This needs to be done after XCloseDisplay, as libGL registers + // cleanup callbacks that get called by it + _glfwTerminateGLX(); + + _glfwTerminateJoysticksLinux(); + _glfwTerminateThreadLocalStoragePOSIX(); +} + +const char* _glfwPlatformGetVersionString(void) +{ + return _GLFW_VERSION_NUMBER " X11 GLX EGL" +#if defined(_POSIX_TIMERS) && defined(_POSIX_MONOTONIC_CLOCK) + " clock_gettime" +#else + " gettimeofday" +#endif +#if defined(__linux__) + " /dev/js" +#endif +#if defined(_GLFW_HAS_XF86VM) + " Xf86vm" +#endif +#if defined(_GLFW_BUILD_DLL) + " shared" +#endif + ; +} + diff --git a/apps/exampleViewer/common/glfw/src/x11_monitor.c b/apps/exampleViewer/common/glfw/src/x11_monitor.c new file mode 100644 index 0000000000..111ef1aa2b --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/x11_monitor.c @@ -0,0 +1,491 @@ +//======================================================================== +// GLFW 3.3 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include +#include + + +// Check whether the display mode should be included in enumeration +// +static GLFWbool modeIsGood(const XRRModeInfo* mi) +{ + return (mi->modeFlags & RR_Interlace) == 0; +} + +// Calculates the refresh rate, in Hz, from the specified RandR mode info +// +static int calculateRefreshRate(const XRRModeInfo* mi) +{ + if (mi->hTotal && mi->vTotal) + return (int) ((double) mi->dotClock / ((double) mi->hTotal * (double) mi->vTotal)); + else + return 0; +} + +// Returns the mode info for a RandR mode XID +// +static const XRRModeInfo* getModeInfo(const XRRScreenResources* sr, RRMode id) +{ + int i; + + for (i = 0; i < sr->nmode; i++) + { + if (sr->modes[i].id == id) + return sr->modes + i; + } + + return NULL; +} + +// Convert RandR mode info to GLFW video mode +// +static GLFWvidmode vidmodeFromModeInfo(const XRRModeInfo* mi, + const XRRCrtcInfo* ci) +{ + GLFWvidmode mode; + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + mode.width = mi->height; + mode.height = mi->width; + } + else + { + mode.width = mi->width; + mode.height = mi->height; + } + + mode.refreshRate = calculateRefreshRate(mi); + + _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), + &mode.redBits, &mode.greenBits, &mode.blueBits); + + return mode; +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Set the current video mode for the specified monitor +// +GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr; + XRRCrtcInfo* ci; + XRROutputInfo* oi; + GLFWvidmode current; + const GLFWvidmode* best; + RRMode native = None; + int i; + + best = _glfwChooseVideoMode(monitor, desired); + _glfwPlatformGetVideoMode(monitor, ¤t); + if (_glfwCompareVideoModes(¤t, best) == 0) + return GLFW_TRUE; + + sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); + + for (i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); + if (!modeIsGood(mi)) + continue; + + const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); + if (_glfwCompareVideoModes(best, &mode) == 0) + { + native = mi->id; + break; + } + } + + if (native) + { + if (monitor->x11.oldMode == None) + monitor->x11.oldMode = ci->mode; + + XRRSetCrtcConfig(_glfw.x11.display, + sr, monitor->x11.crtc, + CurrentTime, + ci->x, ci->y, + native, + ci->rotation, + ci->outputs, + ci->noutput); + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + + if (!native) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Monitor mode list changed"); + return GLFW_FALSE; + } + } + + return GLFW_TRUE; +} + +// Restore the saved (original) video mode for the specified monitor +// +void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr; + XRRCrtcInfo* ci; + + if (monitor->x11.oldMode == None) + return; + + sr = XRRGetScreenResources(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + XRRSetCrtcConfig(_glfw.x11.display, + sr, monitor->x11.crtc, + CurrentTime, + ci->x, ci->y, + monitor->x11.oldMode, + ci->rotation, + ci->outputs, + ci->noutput); + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + + monitor->x11.oldMode = None; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +_GLFWmonitor** _glfwPlatformGetMonitors(int* count) +{ + int i, j, k, found = 0; + _GLFWmonitor** monitors = NULL; + + *count = 0; + + if (_glfw.x11.randr.available) + { + int screenCount = 0; + XineramaScreenInfo* screens = NULL; + XRRScreenResources* sr = XRRGetScreenResources(_glfw.x11.display, + _glfw.x11.root); + RROutput primary = XRRGetOutputPrimary(_glfw.x11.display, + _glfw.x11.root); + + monitors = calloc(sr->noutput, sizeof(_GLFWmonitor*)); + + if (_glfw.x11.xinerama.available) + screens = XineramaQueryScreens(_glfw.x11.display, &screenCount); + + for (i = 0; i < sr->ncrtc; i++) + { + XRRCrtcInfo* ci = XRRGetCrtcInfo(_glfw.x11.display, + sr, sr->crtcs[i]); + + for (j = 0; j < ci->noutput; j++) + { + int widthMM, heightMM; + _GLFWmonitor* monitor; + XRROutputInfo* oi = XRRGetOutputInfo(_glfw.x11.display, + sr, ci->outputs[j]); + if (oi->connection != RR_Connected) + { + XRRFreeOutputInfo(oi); + continue; + } + + if (ci->rotation == RR_Rotate_90 || ci->rotation == RR_Rotate_270) + { + widthMM = oi->mm_height; + heightMM = oi->mm_width; + } + else + { + widthMM = oi->mm_width; + heightMM = oi->mm_height; + } + + monitor = _glfwAllocMonitor(oi->name, widthMM, heightMM); + monitor->x11.output = ci->outputs[j]; + monitor->x11.crtc = oi->crtc; + + for (k = 0; k < screenCount; k++) + { + if (screens[k].x_org == ci->x && + screens[k].y_org == ci->y && + screens[k].width == ci->width && + screens[k].height == ci->height) + { + monitor->x11.index = k; + break; + } + } + + XRRFreeOutputInfo(oi); + + found++; + monitors[found - 1] = monitor; + + if (ci->outputs[j] == primary) + _GLFW_SWAP_POINTERS(monitors[0], monitors[found - 1]); + } + + XRRFreeCrtcInfo(ci); + } + + XRRFreeScreenResources(sr); + + if (screens) + XFree(screens); + + if (found == 0) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: RandR monitor support seems broken"); + + _glfw.x11.randr.monitorBroken = GLFW_TRUE; + free(monitors); + monitors = NULL; + } + } + + if (!monitors) + { + monitors = calloc(1, sizeof(_GLFWmonitor*)); + monitors[0] = _glfwAllocMonitor("Display", + DisplayWidthMM(_glfw.x11.display, + _glfw.x11.screen), + DisplayHeightMM(_glfw.x11.display, + _glfw.x11.screen)); + found = 1; + } + + *count = found; + return monitors; +} + +GLFWbool _glfwPlatformIsSameMonitor(_GLFWmonitor* first, _GLFWmonitor* second) +{ + return first->x11.crtc == second->x11.crtc; +} + +void _glfwPlatformGetMonitorPos(_GLFWmonitor* monitor, int* xpos, int* ypos) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr; + XRRCrtcInfo* ci; + + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + if (xpos) + *xpos = ci->x; + if (ypos) + *ypos = ci->y; + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } +} + +GLFWvidmode* _glfwPlatformGetVideoModes(_GLFWmonitor* monitor, int* count) +{ + GLFWvidmode* result; + + *count = 0; + + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + int i, j; + XRRScreenResources* sr; + XRRCrtcInfo* ci; + XRROutputInfo* oi; + + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + oi = XRRGetOutputInfo(_glfw.x11.display, sr, monitor->x11.output); + + result = calloc(oi->nmode, sizeof(GLFWvidmode)); + + for (i = 0; i < oi->nmode; i++) + { + const XRRModeInfo* mi = getModeInfo(sr, oi->modes[i]); + if (!modeIsGood(mi)) + continue; + + const GLFWvidmode mode = vidmodeFromModeInfo(mi, ci); + + for (j = 0; j < *count; j++) + { + if (_glfwCompareVideoModes(result + j, &mode) == 0) + break; + } + + // Skip duplicate modes + if (j < *count) + continue; + + (*count)++; + result[*count - 1] = mode; + } + + XRRFreeOutputInfo(oi); + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + *count = 1; + result = calloc(1, sizeof(GLFWvidmode)); + _glfwPlatformGetVideoMode(monitor, result); + } + + return result; +} + +void _glfwPlatformGetVideoMode(_GLFWmonitor* monitor, GLFWvidmode* mode) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.monitorBroken) + { + XRRScreenResources* sr; + XRRCrtcInfo* ci; + + sr = XRRGetScreenResourcesCurrent(_glfw.x11.display, _glfw.x11.root); + ci = XRRGetCrtcInfo(_glfw.x11.display, sr, monitor->x11.crtc); + + *mode = vidmodeFromModeInfo(getModeInfo(sr, ci->mode), ci); + + XRRFreeCrtcInfo(ci); + XRRFreeScreenResources(sr); + } + else + { + mode->width = DisplayWidth(_glfw.x11.display, _glfw.x11.screen); + mode->height = DisplayHeight(_glfw.x11.display, _glfw.x11.screen); + mode->refreshRate = 0; + + _glfwSplitBPP(DefaultDepth(_glfw.x11.display, _glfw.x11.screen), + &mode->redBits, &mode->greenBits, &mode->blueBits); + } +} + +void _glfwPlatformGetGammaRamp(_GLFWmonitor* monitor, GLFWgammaramp* ramp) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) + { + const size_t size = XRRGetCrtcGammaSize(_glfw.x11.display, + monitor->x11.crtc); + XRRCrtcGamma* gamma = XRRGetCrtcGamma(_glfw.x11.display, + monitor->x11.crtc); + + _glfwAllocGammaArrays(ramp, size); + + memcpy(ramp->red, gamma->red, size * sizeof(unsigned short)); + memcpy(ramp->green, gamma->green, size * sizeof(unsigned short)); + memcpy(ramp->blue, gamma->blue, size * sizeof(unsigned short)); + + XRRFreeGamma(gamma); + } +#if defined(_GLFW_HAS_XF86VM) + else if (_glfw.x11.vidmode.available) + { + int size; + XF86VidModeGetGammaRampSize(_glfw.x11.display, _glfw.x11.screen, &size); + + _glfwAllocGammaArrays(ramp, size); + + XF86VidModeGetGammaRamp(_glfw.x11.display, + _glfw.x11.screen, + ramp->size, ramp->red, ramp->green, ramp->blue); + } +#endif /*_GLFW_HAS_XF86VM*/ +} + +void _glfwPlatformSetGammaRamp(_GLFWmonitor* monitor, const GLFWgammaramp* ramp) +{ + if (_glfw.x11.randr.available && !_glfw.x11.randr.gammaBroken) + { + XRRCrtcGamma* gamma = XRRAllocGamma(ramp->size); + + memcpy(gamma->red, ramp->red, ramp->size * sizeof(unsigned short)); + memcpy(gamma->green, ramp->green, ramp->size * sizeof(unsigned short)); + memcpy(gamma->blue, ramp->blue, ramp->size * sizeof(unsigned short)); + + XRRSetCrtcGamma(_glfw.x11.display, monitor->x11.crtc, gamma); + XRRFreeGamma(gamma); + } +#if defined(_GLFW_HAS_XF86VM) + else if (_glfw.x11.vidmode.available) + { + XF86VidModeSetGammaRamp(_glfw.x11.display, + _glfw.x11.screen, + ramp->size, + (unsigned short*) ramp->red, + (unsigned short*) ramp->green, + (unsigned short*) ramp->blue); + } +#endif /*_GLFW_HAS_XF86VM*/ +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return monitor->x11.crtc; +} + +GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* handle) +{ + _GLFWmonitor* monitor = (_GLFWmonitor*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return monitor->x11.output; +} + diff --git a/apps/exampleViewer/common/glfw/src/x11_platform.h b/apps/exampleViewer/common/glfw/src/x11_platform.h new file mode 100644 index 0000000000..754d799098 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/x11_platform.h @@ -0,0 +1,302 @@ +//======================================================================== +// GLFW 3.3 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_x11_platform_h_ +#define _glfw3_x11_platform_h_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +// The XRandR extension provides mode setting and gamma control +#include + +// The Xkb extension provides improved keyboard support +#include + +// The Xinerama extension provides legacy monitor indices +#include + +#if defined(_GLFW_HAS_XF86VM) + // The Xf86VidMode extension provides fallback gamma control + #include +#endif + +typedef XID xcb_window_t; +typedef XID xcb_visualid_t; +typedef struct xcb_connection_t xcb_connection_t; +typedef xcb_connection_t* (* XGETXCBCONNECTION_T)(Display*); + +typedef VkFlags VkXlibSurfaceCreateFlagsKHR; +typedef VkFlags VkXcbSurfaceCreateFlagsKHR; + +typedef struct VkXlibSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkXlibSurfaceCreateFlagsKHR flags; + Display* dpy; + Window window; +} VkXlibSurfaceCreateInfoKHR; + +typedef struct VkXcbSurfaceCreateInfoKHR +{ + VkStructureType sType; + const void* pNext; + VkXcbSurfaceCreateFlagsKHR flags; + xcb_connection_t* connection; + xcb_window_t window; +} VkXcbSurfaceCreateInfoKHR; + +typedef VkResult (APIENTRY *PFN_vkCreateXlibSurfaceKHR)(VkInstance,const VkXlibSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR)(VkPhysicalDevice,uint32_t,Display*,VisualID); +typedef VkResult (APIENTRY *PFN_vkCreateXcbSurfaceKHR)(VkInstance,const VkXcbSurfaceCreateInfoKHR*,const VkAllocationCallbacks*,VkSurfaceKHR*); +typedef VkBool32 (APIENTRY *PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR)(VkPhysicalDevice,uint32_t,xcb_connection_t*,xcb_visualid_t); + +#include "posix_tls.h" +#include "posix_time.h" +#include "linux_joystick.h" +#include "xkb_unicode.h" +#include "glx_context.h" +#include "egl_context.h" + +#define _glfw_dlopen(name) dlopen(name, RTLD_LAZY | RTLD_LOCAL) +#define _glfw_dlclose(handle) dlclose(handle) +#define _glfw_dlsym(handle, name) dlsym(handle, name) + +#define _GLFW_EGL_NATIVE_WINDOW ((EGLNativeWindowType) window->x11.handle) +#define _GLFW_EGL_NATIVE_DISPLAY ((EGLNativeDisplayType) _glfw.x11.display) + +#define _GLFW_PLATFORM_WINDOW_STATE _GLFWwindowX11 x11 +#define _GLFW_PLATFORM_LIBRARY_WINDOW_STATE _GLFWlibraryX11 x11 +#define _GLFW_PLATFORM_MONITOR_STATE _GLFWmonitorX11 x11 +#define _GLFW_PLATFORM_CURSOR_STATE _GLFWcursorX11 x11 + + +// X11-specific per-window data +// +typedef struct _GLFWwindowX11 +{ + Colormap colormap; + Window handle; + XIC ic; + + GLFWbool overrideRedirect; + GLFWbool iconified; + GLFWbool maximized; + + // Cached position and size used to filter out duplicate events + int width, height; + int xpos, ypos; + + // The last received cursor position, regardless of source + int lastCursorPosX, lastCursorPosY; + // The last position the cursor was warped to by GLFW + int warpCursorPosX, warpCursorPosY; + + // The information from the last KeyPress event + unsigned int lastKeyCode; + Time lastKeyTime; + +} _GLFWwindowX11; + +// X11-specific global data +// +typedef struct _GLFWlibraryX11 +{ + Display* display; + int screen; + Window root; + + // Helper window for IPC + Window helperWindowHandle; + // Invisible cursor for hidden cursor mode + Cursor hiddenCursorHandle; + // Context for mapping window XIDs to _GLFWwindow pointers + XContext context; + // XIM input method + XIM im; + // Most recent error code received by X error handler + int errorCode; + // Clipboard string (while the selection is owned) + char* clipboardString; + // Key name string + char keyName[64]; + // X11 keycode to GLFW key LUT + short int keycodes[256]; + // GLFW key to X11 keycode LUT + short int scancodes[GLFW_KEY_LAST + 1]; + // Where to place the cursor when re-enabled + double restoreCursorPosX, restoreCursorPosY; + // The window whose disabled cursor mode is active + _GLFWwindow* disabledCursorWindow; + + // Window manager atoms + Atom WM_PROTOCOLS; + Atom WM_STATE; + Atom WM_DELETE_WINDOW; + Atom NET_WM_NAME; + Atom NET_WM_ICON_NAME; + Atom NET_WM_ICON; + Atom NET_WM_PID; + Atom NET_WM_PING; + Atom NET_WM_WINDOW_TYPE; + Atom NET_WM_WINDOW_TYPE_NORMAL; + Atom NET_WM_STATE; + Atom NET_WM_STATE_ABOVE; + Atom NET_WM_STATE_FULLSCREEN; + Atom NET_WM_STATE_MAXIMIZED_VERT; + Atom NET_WM_STATE_MAXIMIZED_HORZ; + Atom NET_WM_BYPASS_COMPOSITOR; + Atom NET_WM_FULLSCREEN_MONITORS; + Atom NET_ACTIVE_WINDOW; + Atom NET_FRAME_EXTENTS; + Atom NET_REQUEST_FRAME_EXTENTS; + Atom MOTIF_WM_HINTS; + + // Xdnd (drag and drop) atoms + Atom XdndAware; + Atom XdndEnter; + Atom XdndPosition; + Atom XdndStatus; + Atom XdndActionCopy; + Atom XdndDrop; + Atom XdndLeave; + Atom XdndFinished; + Atom XdndSelection; + + // Selection (clipboard) atoms + Atom TARGETS; + Atom MULTIPLE; + Atom CLIPBOARD; + Atom CLIPBOARD_MANAGER; + Atom SAVE_TARGETS; + Atom NULL_; + Atom UTF8_STRING; + Atom COMPOUND_STRING; + Atom ATOM_PAIR; + Atom GLFW_SELECTION; + + struct { + GLFWbool available; + int eventBase; + int errorBase; + int major; + int minor; + GLFWbool gammaBroken; + GLFWbool monitorBroken; + } randr; + + struct { + GLFWbool available; + GLFWbool detectable; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + } xkb; + + struct { + int count; + int timeout; + int interval; + int blanking; + int exposure; + } saver; + + struct { + Window source; + } xdnd; + + struct { + GLFWbool available; + int major; + int minor; + } xinerama; + + struct { + void* handle; + XGETXCBCONNECTION_T XGetXCBConnection; + } x11xcb; + +#if defined(_GLFW_HAS_XF86VM) + struct { + GLFWbool available; + int eventBase; + int errorBase; + } vidmode; +#endif /*_GLFW_HAS_XF86VM*/ + +} _GLFWlibraryX11; + +// X11-specific per-monitor data +// +typedef struct _GLFWmonitorX11 +{ + RROutput output; + RRCrtc crtc; + RRMode oldMode; + + // Index of corresponding Xinerama screen, + // for EWMH full screen window placement + int index; + +} _GLFWmonitorX11; + +// X11-specific per-cursor data +// +typedef struct _GLFWcursorX11 +{ + Cursor handle; + +} _GLFWcursorX11; + + +GLFWbool _glfwSetVideoModeX11(_GLFWmonitor* monitor, const GLFWvidmode* desired); +void _glfwRestoreVideoModeX11(_GLFWmonitor* monitor); + +Cursor _glfwCreateCursorX11(const GLFWimage* image, int xhot, int yhot); + +unsigned long _glfwGetWindowPropertyX11(Window window, + Atom property, + Atom type, + unsigned char** value); + +void _glfwGrabErrorHandlerX11(void); +void _glfwReleaseErrorHandlerX11(void); +void _glfwInputErrorX11(int error, const char* message); + +void _glfwPushSelectionToManagerX11(void); + +#endif // _glfw3_x11_platform_h_ diff --git a/apps/exampleViewer/common/glfw/src/x11_window.c b/apps/exampleViewer/common/glfw/src/x11_window.c new file mode 100644 index 0000000000..cbb039adbd --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/x11_window.c @@ -0,0 +1,2500 @@ +//======================================================================== +// GLFW 3.3 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +// Action for EWMH client messages +#define _NET_WM_STATE_REMOVE 0 +#define _NET_WM_STATE_ADD 1 +#define _NET_WM_STATE_TOGGLE 2 + +// Additional mouse button names for XButtonEvent +#define Button6 6 +#define Button7 7 + + +// Wait for data to arrive using select +// This avoids blocking other threads via the per-display Xlib lock that also +// covers GLX functions +// +static GLFWbool waitForEvent(double* timeout) +{ + fd_set fds; + const int fd = ConnectionNumber(_glfw.x11.display); + int count = fd + 1; + + FD_ZERO(&fds); + FD_SET(fd, &fds); +#if defined(__linux__) + FD_SET(_glfw.linux_js.inotify, &fds); + + if (fd < _glfw.linux_js.inotify) + count = _glfw.linux_js.inotify + 1; +#endif + for (;;) + { + if (timeout) + { + const long seconds = (long) *timeout; + const long microseconds = (long) ((*timeout - seconds) * 1e6); + struct timeval tv = { seconds, microseconds }; + const uint64_t base = _glfwPlatformGetTimerValue(); + + const int result = select(count, &fds, NULL, NULL, &tv); + const int error = errno; + + *timeout -= (_glfwPlatformGetTimerValue() - base) / + (double) _glfwPlatformGetTimerFrequency(); + + if (result > 0) + return GLFW_TRUE; + if ((result == -1 && error == EINTR) || *timeout <= 0.0) + return GLFW_FALSE; + } + else if (select(count, &fds, NULL, NULL, NULL) != -1 || errno != EINTR) + return GLFW_TRUE; + } +} + +// Waits until a VisibilityNotify event arrives for the specified window or the +// timeout period elapses (ICCCM section 4.2.2) +// +static GLFWbool waitForVisibilityNotify(_GLFWwindow* window) +{ + XEvent dummy; + double timeout = 0.1; + + while (!XCheckTypedWindowEvent(_glfw.x11.display, + window->x11.handle, + VisibilityNotify, + &dummy)) + { + if (!waitForEvent(&timeout)) + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +// Returns whether the window is iconified +// +static int getWindowState(_GLFWwindow* window) +{ + int result = WithdrawnState; + struct { + CARD32 state; + Window icon; + } *state = NULL; + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.WM_STATE, + _glfw.x11.WM_STATE, + (unsigned char**) &state) >= 2) + { + result = state->state; + } + + XFree(state); + return result; +} + +// Returns whether the event is a selection event +// +static Bool isSelectionEvent(Display* display, XEvent* event, XPointer pointer) +{ + if (event->xany.window != _glfw.x11.helperWindowHandle) + return False; + + return event->type == SelectionRequest || + event->type == SelectionNotify || + event->type == SelectionClear; +} + +// Returns whether it is a _NET_FRAME_EXTENTS event for the specified window +// +static Bool isFrameExtentsEvent(Display* display, XEvent* event, XPointer pointer) +{ + _GLFWwindow* window = (_GLFWwindow*) pointer; + return event->type == PropertyNotify && + event->xproperty.state == PropertyNewValue && + event->xproperty.window == window->x11.handle && + event->xproperty.atom == _glfw.x11.NET_FRAME_EXTENTS; +} + +// Translates a GLFW standard cursor to a font cursor shape +// +static int translateCursorShape(int shape) +{ + switch (shape) + { + case GLFW_ARROW_CURSOR: + return XC_left_ptr; + case GLFW_IBEAM_CURSOR: + return XC_xterm; + case GLFW_CROSSHAIR_CURSOR: + return XC_crosshair; + case GLFW_HAND_CURSOR: + return XC_hand1; + case GLFW_HRESIZE_CURSOR: + return XC_sb_h_double_arrow; + case GLFW_VRESIZE_CURSOR: + return XC_sb_v_double_arrow; + } + + return 0; +} + +// Translates an X event modifier state mask +// +static int translateState(int state) +{ + int mods = 0; + + if (state & ShiftMask) + mods |= GLFW_MOD_SHIFT; + if (state & ControlMask) + mods |= GLFW_MOD_CONTROL; + if (state & Mod1Mask) + mods |= GLFW_MOD_ALT; + if (state & Mod4Mask) + mods |= GLFW_MOD_SUPER; + + return mods; +} + +// Translates an X11 key code to a GLFW key token +// +static int translateKey(int scancode) +{ + // Use the pre-filled LUT (see createKeyTables() in x11_init.c) + if (scancode < 0 || scancode > 255) + return GLFW_KEY_UNKNOWN; + + return _glfw.x11.keycodes[scancode]; +} + +// Return the GLFW window corresponding to the specified X11 window +// +static _GLFWwindow* findWindowByHandle(Window handle) +{ + _GLFWwindow* window; + + if (XFindContext(_glfw.x11.display, + handle, + _glfw.x11.context, + (XPointer*) &window) != 0) + { + return NULL; + } + + return window; +} + +// Sends an EWMH or ICCCM event to the window manager +// +static void sendEventToWM(_GLFWwindow* window, Atom type, + long a, long b, long c, long d, long e) +{ + XEvent event; + memset(&event, 0, sizeof(event)); + + event.type = ClientMessage; + event.xclient.window = window->x11.handle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = type; + event.xclient.data.l[0] = a; + event.xclient.data.l[1] = b; + event.xclient.data.l[2] = c; + event.xclient.data.l[3] = d; + event.xclient.data.l[4] = e; + + XSendEvent(_glfw.x11.display, _glfw.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &event); +} + +// Updates the normal hints according to the window settings +// +static void updateNormalHints(_GLFWwindow* window, int width, int height) +{ + XSizeHints* hints = XAllocSizeHints(); + + if (!window->monitor) + { + if (window->resizable) + { + if (window->minwidth != GLFW_DONT_CARE && + window->minheight != GLFW_DONT_CARE) + { + hints->flags |= PMinSize; + hints->min_width = window->minwidth; + hints->min_height = window->minheight; + } + + if (window->maxwidth != GLFW_DONT_CARE && + window->maxheight != GLFW_DONT_CARE) + { + hints->flags |= PMaxSize; + hints->max_width = window->maxwidth; + hints->max_height = window->maxheight; + } + + if (window->numer != GLFW_DONT_CARE && + window->denom != GLFW_DONT_CARE) + { + hints->flags |= PAspect; + hints->min_aspect.x = hints->max_aspect.x = window->numer; + hints->min_aspect.y = hints->max_aspect.y = window->denom; + } + } + else + { + hints->flags |= (PMinSize | PMaxSize); + hints->min_width = hints->max_width = width; + hints->min_height = hints->max_height = height; + } + } + + hints->flags |= PWinGravity; + hints->win_gravity = StaticGravity; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); +} + +// Updates the full screen status of the window +// +static void updateWindowMode(_GLFWwindow* window) +{ + if (window->monitor) + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + sendEventToWM(window, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + window->monitor->x11.index, + 0); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + // This is the butcher's way of removing window decorations + // Setting the override-redirect attribute on a window makes the + // window manager ignore the window completely (ICCCM, section 4) + // The good thing is that this makes undecorated full screen windows + // easy to do; the bad thing is that we have to do everything + // manually and some things (like iconify/restore) won't work at + // all, as those are tasks usually performed by the window manager + + XSetWindowAttributes attributes; + attributes.override_redirect = True; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_TRUE; + } + + // Enable compositor bypass + { + const unsigned long value = 1; + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR, XA_CARDINAL, 32, + PropModeReplace, (unsigned char*) &value, 1); + } + } + else + { + if (_glfw.x11.xinerama.available && + _glfw.x11.NET_WM_FULLSCREEN_MONITORS) + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_FULLSCREEN_MONITORS); + } + + if (_glfw.x11.NET_WM_STATE && _glfw.x11.NET_WM_STATE_FULLSCREEN) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_FULLSCREEN, + 0, 1, 0); + } + else + { + XSetWindowAttributes attributes; + attributes.override_redirect = False; + XChangeWindowAttributes(_glfw.x11.display, + window->x11.handle, + CWOverrideRedirect, + &attributes); + + window->x11.overrideRedirect = GLFW_FALSE; + } + + // Disable compositor bypass + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_BYPASS_COMPOSITOR); + } + } +} + +// Splits and translates a text/uri-list into separate file paths +// NOTE: This function destroys the provided string +// +static char** parseUriList(char* text, int* count) +{ + const char* prefix = "file://"; + char** paths = NULL; + char* line; + + *count = 0; + + while ((line = strtok(text, "\r\n"))) + { + text = NULL; + + if (line[0] == '#') + continue; + + if (strncmp(line, prefix, strlen(prefix)) == 0) + line += strlen(prefix); + + (*count)++; + + char* path = calloc(strlen(line) + 1, 1); + paths = realloc(paths, *count * sizeof(char*)); + paths[*count - 1] = path; + + while (*line) + { + if (line[0] == '%' && line[1] && line[2]) + { + const char digits[3] = { line[1], line[2], '\0' }; + *path = strtol(digits, NULL, 16); + line += 2; + } + else + *path = *line; + + path++; + line++; + } + } + + return paths; +} + +// Centers the cursor over the window client area +// +static void centerCursor(_GLFWwindow* window) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); +} + +// Updates the cursor image according to its cursor mode +// +static void updateCursorImage(_GLFWwindow* window) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + if (window->cursor) + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + window->cursor->x11.handle); + } + else + XUndefineCursor(_glfw.x11.display, window->x11.handle); + } + else + { + XDefineCursor(_glfw.x11.display, window->x11.handle, + _glfw.x11.hiddenCursorHandle); + } +} + +// Create the X11 window (and its colormap) +// +static GLFWbool createNativeWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + Visual* visual, int depth) +{ + // Create a colormap based on the visual used by the current context + window->x11.colormap = XCreateColormap(_glfw.x11.display, + _glfw.x11.root, + visual, + AllocNone); + + // Create the actual window + { + XSetWindowAttributes wa; + const unsigned long wamask = CWBorderPixel | CWColormap | CWEventMask; + + wa.colormap = window->x11.colormap; + wa.border_pixel = 0; + wa.event_mask = StructureNotifyMask | KeyPressMask | KeyReleaseMask | + PointerMotionMask | ButtonPressMask | ButtonReleaseMask | + ExposureMask | FocusChangeMask | VisibilityChangeMask | + EnterWindowMask | LeaveWindowMask | PropertyChangeMask; + + _glfwGrabErrorHandlerX11(); + + window->x11.handle = XCreateWindow(_glfw.x11.display, + _glfw.x11.root, + 0, 0, + wndconfig->width, wndconfig->height, + 0, // Border width + depth, // Color depth + InputOutput, + visual, + wamask, + &wa); + + _glfwReleaseErrorHandlerX11(); + + if (!window->x11.handle) + { + _glfwInputErrorX11(GLFW_PLATFORM_ERROR, + "X11: Failed to create window"); + return GLFW_FALSE; + } + + XSaveContext(_glfw.x11.display, + window->x11.handle, + _glfw.x11.context, + (XPointer) window); + } + + if (!wndconfig->decorated) + { + struct + { + unsigned long flags; + unsigned long functions; + unsigned long decorations; + long input_mode; + unsigned long status; + } hints; + + hints.flags = 2; // Set decorations + hints.decorations = 0; // No decorations + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.MOTIF_WM_HINTS, + _glfw.x11.MOTIF_WM_HINTS, 32, + PropModeReplace, + (unsigned char*) &hints, + sizeof(hints) / sizeof(long)); + } + + if (_glfw.x11.NET_WM_STATE && !window->monitor) + { + Atom states[3]; + int count = 0; + + if (wndconfig->floating) + { + if (_glfw.x11.NET_WM_STATE_ABOVE) + states[count++] = _glfw.x11.NET_WM_STATE_ABOVE; + } + + if (wndconfig->maximized) + { + if (_glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT; + states[count++] = _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ; + window->x11.maximized = GLFW_TRUE; + } + } + + if (count) + { + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_STATE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &states, count); + } + } + + // Declare the WM protocols supported by GLFW + { + Atom protocols[] = + { + _glfw.x11.WM_DELETE_WINDOW, + _glfw.x11.NET_WM_PING + }; + + XSetWMProtocols(_glfw.x11.display, window->x11.handle, + protocols, sizeof(protocols) / sizeof(Atom)); + } + + // Declare our PID + { + const pid_t pid = getpid(); + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_PID, XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*) &pid, 1); + } + + if (_glfw.x11.NET_WM_WINDOW_TYPE && _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL) + { + Atom type = _glfw.x11.NET_WM_WINDOW_TYPE_NORMAL; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_WINDOW_TYPE, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &type, 1); + } + + // Set ICCCM WM_HINTS property + { + XWMHints* hints = XAllocWMHints(); + if (!hints) + { + _glfwInputError(GLFW_OUT_OF_MEMORY, + "X11: Failed to allocate WM hints"); + return GLFW_FALSE; + } + + hints->flags = StateHint; + hints->initial_state = NormalState; + + XSetWMHints(_glfw.x11.display, window->x11.handle, hints); + XFree(hints); + } + + updateNormalHints(window, wndconfig->width, wndconfig->height); + + // Set ICCCM WM_CLASS property + // HACK: Until a mechanism for specifying the application name is added, the + // initial window title is used as the window class name + if (strlen(wndconfig->title)) + { + XClassHint* hint = XAllocClassHint(); + hint->res_name = (char*) wndconfig->title; + hint->res_class = (char*) wndconfig->title; + + XSetClassHint(_glfw.x11.display, window->x11.handle, hint); + XFree(hint); + } + + if (_glfw.x11.XdndAware) + { + // Announce support for Xdnd (drag and drop) + const Atom version = 5; + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.XdndAware, XA_ATOM, 32, + PropModeReplace, (unsigned char*) &version, 1); + } + + _glfwPlatformSetWindowTitle(window, wndconfig->title); + + if (_glfw.x11.im) + { + window->x11.ic = XCreateIC(_glfw.x11.im, + XNInputStyle, + XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, + window->x11.handle, + XNFocusWindow, + window->x11.handle, + NULL); + } + + _glfwPlatformGetWindowPos(window, &window->x11.xpos, &window->x11.ypos); + _glfwPlatformGetWindowSize(window, &window->x11.width, &window->x11.height); + + return GLFW_TRUE; +} + +// Set the specified property to the selection converted to the requested target +// +static Atom writeTargetToProperty(const XSelectionRequestEvent* request) +{ + int i; + const Atom formats[] = { _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + const int formatCount = sizeof(formats) / sizeof(formats[0]); + + if (request->property == None) + { + // The requester is a legacy client (ICCCM section 2.2) + // We don't support legacy clients, so fail here + return None; + } + + if (request->target == _glfw.x11.TARGETS) + { + // The list of supported targets was requested + + const Atom targets[] = { _glfw.x11.TARGETS, + _glfw.x11.MULTIPLE, + _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + XA_ATOM, + 32, + PropModeReplace, + (unsigned char*) targets, + sizeof(targets) / sizeof(targets[0])); + + return request->property; + } + + if (request->target == _glfw.x11.MULTIPLE) + { + // Multiple conversions were requested + + Atom* targets; + unsigned long i, count; + + count = _glfwGetWindowPropertyX11(request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + (unsigned char**) &targets); + + for (i = 0; i < count; i += 2) + { + int j; + + for (j = 0; j < formatCount; j++) + { + if (targets[i] == formats[j]) + break; + } + + if (j < formatCount) + { + XChangeProperty(_glfw.x11.display, + request->requestor, + targets[i + 1], + targets[i], + 8, + PropModeReplace, + (unsigned char*) _glfw.x11.clipboardString, + strlen(_glfw.x11.clipboardString)); + } + else + targets[i + 1] = None; + } + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11.ATOM_PAIR, + 32, + PropModeReplace, + (unsigned char*) targets, + count); + + XFree(targets); + + return request->property; + } + + if (request->target == _glfw.x11.SAVE_TARGETS) + { + // The request is a check whether we support SAVE_TARGETS + // It should be handled as a no-op side effect target + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + _glfw.x11.NULL_, + 32, + PropModeReplace, + NULL, + 0); + + return request->property; + } + + // Conversion to a data target was requested + + for (i = 0; i < formatCount; i++) + { + if (request->target == formats[i]) + { + // The requested target is one we support + + XChangeProperty(_glfw.x11.display, + request->requestor, + request->property, + request->target, + 8, + PropModeReplace, + (unsigned char*) _glfw.x11.clipboardString, + strlen(_glfw.x11.clipboardString)); + + return request->property; + } + } + + // The requested target is not supported + + return None; +} + +static void handleSelectionClear(XEvent* event) +{ + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; +} + +static void handleSelectionRequest(XEvent* event) +{ + const XSelectionRequestEvent* request = &event->xselectionrequest; + + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.xselection.property = writeTargetToProperty(request); + reply.xselection.type = SelectionNotify; + reply.xselection.display = request->display; + reply.xselection.requestor = request->requestor; + reply.xselection.selection = request->selection; + reply.xselection.target = request->target; + reply.xselection.time = request->time; + + XSendEvent(_glfw.x11.display, request->requestor, False, 0, &reply); +} + +// Make the specified window and its video mode active on its monitor +// +static GLFWbool acquireMonitor(_GLFWwindow* window) +{ + GLFWbool status; + + if (_glfw.x11.saver.count == 0) + { + // Remember old screen saver settings + XGetScreenSaver(_glfw.x11.display, + &_glfw.x11.saver.timeout, + &_glfw.x11.saver.interval, + &_glfw.x11.saver.blanking, + &_glfw.x11.saver.exposure); + + // Disable screen saver + XSetScreenSaver(_glfw.x11.display, 0, 0, DontPreferBlanking, + DefaultExposures); + } + + if (!window->monitor->window) + _glfw.x11.saver.count++; + + status = _glfwSetVideoModeX11(window->monitor, &window->videoMode); + + if (window->x11.overrideRedirect) + { + int xpos, ypos; + GLFWvidmode mode; + + // Manually position the window over its monitor + _glfwPlatformGetMonitorPos(window->monitor, &xpos, &ypos); + _glfwPlatformGetVideoMode(window->monitor, &mode); + + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, mode.width, mode.height); + } + + _glfwInputMonitorWindowChange(window->monitor, window); + return status; +} + +// Remove the window and restore the original video mode +// +static void releaseMonitor(_GLFWwindow* window) +{ + if (window->monitor->window != window) + return; + + _glfwInputMonitorWindowChange(window->monitor, NULL); + _glfwRestoreVideoModeX11(window->monitor); + + _glfw.x11.saver.count--; + + if (_glfw.x11.saver.count == 0) + { + // Restore old screen saver settings + XSetScreenSaver(_glfw.x11.display, + _glfw.x11.saver.timeout, + _glfw.x11.saver.interval, + _glfw.x11.saver.blanking, + _glfw.x11.saver.exposure); + } +} + +// Decode a Unicode code point from a UTF-8 stream +// Based on cutef8 by Jeff Bezanson (Public Domain) +// +#if defined(X_HAVE_UTF8_STRING) +static unsigned int decodeUTF8(const char** s) +{ + unsigned int ch = 0, count = 0; + static const unsigned int offsets[] = + { + 0x00000000u, 0x00003080u, 0x000e2080u, + 0x03c82080u, 0xfa082080u, 0x82082080u + }; + + do + { + ch = (ch << 6) + (unsigned char) **s; + (*s)++; + count++; + } while ((**s & 0xc0) == 0x80); + + assert(count <= 6); + return ch - offsets[count - 1]; +} +#endif /*X_HAVE_UTF8_STRING*/ + +// Process the specified X event +// +static void processEvent(XEvent *event) +{ + _GLFWwindow* window = NULL; + int keycode = 0; + Bool filtered = False; + + // HACK: Save scancode as some IMs clear the field in XFilterEvent + if (event->type == KeyPress || event->type == KeyRelease) + keycode = event->xkey.keycode; + + if (_glfw.x11.im) + filtered = XFilterEvent(event, None); + + if (_glfw.x11.randr.available) + { + if (event->type == _glfw.x11.randr.eventBase + RRNotify) + { + XRRUpdateConfiguration(event); + _glfwInputMonitorChange(); + return; + } + } + + if (event->type != GenericEvent) + { + window = findWindowByHandle(event->xany.window); + if (window == NULL) + { + // This is an event for a window that has already been destroyed + return; + } + } + + switch (event->type) + { + case KeyPress: + { + const int key = translateKey(keycode); + const int mods = translateState(event->xkey.state); + const int plain = !(mods & (GLFW_MOD_CONTROL | GLFW_MOD_ALT)); + + if (window->x11.ic) + { + // HACK: Ignore duplicate key press events generated by ibus + // Corresponding release events are filtered out by the + // GLFW key repeat logic + if (window->x11.lastKeyCode != keycode || + window->x11.lastKeyTime != event->xkey.time) + { + if (keycode) + _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); + } + + window->x11.lastKeyCode = keycode; + window->x11.lastKeyTime = event->xkey.time; + + if (!filtered) + { + int count; + Status status; +#if defined(X_HAVE_UTF8_STRING) + char buffer[100]; + char* chars = buffer; + + count = Xutf8LookupString(window->x11.ic, + &event->xkey, + buffer, sizeof(buffer) - 1, + NULL, &status); + + if (status == XBufferOverflow) + { + chars = calloc(count + 1, 1); + count = Xutf8LookupString(window->x11.ic, + &event->xkey, + chars, count, + NULL, &status); + } + + if (status == XLookupChars || status == XLookupBoth) + { + const char* c = chars; + chars[count] = '\0'; + while (c - chars < count) + _glfwInputChar(window, decodeUTF8(&c), mods, plain); + } +#else /*X_HAVE_UTF8_STRING*/ + wchar_t buffer[16]; + wchar_t* chars = buffer; + + count = XwcLookupString(window->x11.ic, + &event->xkey, + buffer, sizeof(buffer) / sizeof(wchar_t), + NULL, &status); + + if (status == XBufferOverflow) + { + chars = calloc(count, sizeof(wchar_t)); + count = XwcLookupString(window->x11.ic, + &event->xkey, + chars, count, + NULL, &status); + } + + if (status == XLookupChars || status == XLookupBoth) + { + int i; + for (i = 0; i < count; i++) + _glfwInputChar(window, chars[i], mods, plain); + } +#endif /*X_HAVE_UTF8_STRING*/ + + if (chars != buffer) + free(chars); + } + } + else + { + KeySym keysym; + XLookupString(&event->xkey, NULL, 0, &keysym, NULL); + + _glfwInputKey(window, key, keycode, GLFW_PRESS, mods); + + const long character = _glfwKeySym2Unicode(keysym); + if (character != -1) + _glfwInputChar(window, character, mods, plain); + } + + return; + } + + case KeyRelease: + { + const int key = translateKey(keycode); + const int mods = translateState(event->xkey.state); + + if (!_glfw.x11.xkb.detectable) + { + // HACK: Key repeat events will arrive as KeyRelease/KeyPress + // pairs with similar or identical time stamps + // The key repeat logic in _glfwInputKey expects only key + // presses to repeat, so detect and discard release events + if (XEventsQueued(_glfw.x11.display, QueuedAfterReading)) + { + XEvent next; + XPeekEvent(_glfw.x11.display, &next); + + if (next.type == KeyPress && + next.xkey.window == event->xkey.window && + next.xkey.keycode == keycode) + { + // HACK: The time of repeat events sometimes doesn't + // match that of the press event, so add an + // epsilon + // Toshiyuki Takahashi can press a button + // 16 times per second so it's fairly safe to + // assume that no human is pressing the key 50 + // times per second (value is ms) + if ((next.xkey.time - event->xkey.time) < 20) + { + // This is very likely a server-generated key repeat + // event, so ignore it + return; + } + } + } + } + + _glfwInputKey(window, key, keycode, GLFW_RELEASE, mods); + return; + } + + case ButtonPress: + { + const int mods = translateState(event->xbutton.state); + + if (event->xbutton.button == Button1) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, mods); + else if (event->xbutton.button == Button2) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_MIDDLE, GLFW_PRESS, mods); + else if (event->xbutton.button == Button3) + _glfwInputMouseClick(window, GLFW_MOUSE_BUTTON_RIGHT, GLFW_PRESS, mods); + + // Modern X provides scroll events as mouse button presses + else if (event->xbutton.button == Button4) + _glfwInputScroll(window, 0.0, 1.0); + else if (event->xbutton.button == Button5) + _glfwInputScroll(window, 0.0, -1.0); + else if (event->xbutton.button == Button6) + _glfwInputScroll(window, 1.0, 0.0); + else if (event->xbutton.button == Button7) + _glfwInputScroll(window, -1.0, 0.0); + + else + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->xbutton.button - Button1 - 4, + GLFW_PRESS, + mods); + } + + return; + } + + case ButtonRelease: + { + const int mods = translateState(event->xbutton.state); + + if (event->xbutton.button == Button1) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_LEFT, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button == Button2) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_MIDDLE, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button == Button3) + { + _glfwInputMouseClick(window, + GLFW_MOUSE_BUTTON_RIGHT, + GLFW_RELEASE, + mods); + } + else if (event->xbutton.button > Button7) + { + // Additional buttons after 7 are treated as regular buttons + // We subtract 4 to fill the gap left by scroll input above + _glfwInputMouseClick(window, + event->xbutton.button - Button1 - 4, + GLFW_RELEASE, + mods); + } + + return; + } + + case EnterNotify: + { + // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise + // ignore the defined cursor for hidden cursor mode + if (window->cursorMode == GLFW_CURSOR_HIDDEN) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN); + + _glfwInputCursorEnter(window, GLFW_TRUE); + return; + } + + case LeaveNotify: + { + _glfwInputCursorEnter(window, GLFW_FALSE); + return; + } + + case MotionNotify: + { + const int x = event->xmotion.x; + const int y = event->xmotion.y; + + if (x != window->x11.warpCursorPosX || y != window->x11.warpCursorPosY) + { + // The cursor was moved by something other than GLFW + + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (_glfw.x11.disabledCursorWindow != window) + return; + + const int dx = x - window->x11.lastCursorPosX; + const int dy = y - window->x11.lastCursorPosY; + + _glfwInputCursorPos(window, + window->virtualCursorPosX + dx, + window->virtualCursorPosY + dy); + } + else + _glfwInputCursorPos(window, x, y); + } + + window->x11.lastCursorPosX = x; + window->x11.lastCursorPosY = y; + return; + } + + case ConfigureNotify: + { + if (event->xconfigure.width != window->x11.width || + event->xconfigure.height != window->x11.height) + { + _glfwInputFramebufferSize(window, + event->xconfigure.width, + event->xconfigure.height); + + _glfwInputWindowSize(window, + event->xconfigure.width, + event->xconfigure.height); + + window->x11.width = event->xconfigure.width; + window->x11.height = event->xconfigure.height; + } + + if (event->xconfigure.x != window->x11.xpos || + event->xconfigure.y != window->x11.ypos) + { + if (window->x11.overrideRedirect || event->xany.send_event) + { + _glfwInputWindowPos(window, + event->xconfigure.x, + event->xconfigure.y); + + window->x11.xpos = event->xconfigure.x; + window->x11.ypos = event->xconfigure.y; + } + } + + return; + } + + case ClientMessage: + { + // Custom client message, probably from the window manager + + if (filtered) + return; + + if (event->xclient.message_type == None) + return; + + if (event->xclient.message_type == _glfw.x11.WM_PROTOCOLS) + { + const Atom protocol = event->xclient.data.l[0]; + if (protocol == None) + return; + + if (protocol == _glfw.x11.WM_DELETE_WINDOW) + { + // The window manager was asked to close the window, for example by + // the user pressing a 'close' window decoration button + _glfwInputWindowCloseRequest(window); + } + else if (protocol == _glfw.x11.NET_WM_PING) + { + // The window manager is pinging the application to ensure it's + // still responding to events + + XEvent reply = *event; + reply.xclient.window = _glfw.x11.root; + + XSendEvent(_glfw.x11.display, _glfw.x11.root, + False, + SubstructureNotifyMask | SubstructureRedirectMask, + &reply); + } + } + else if (event->xclient.message_type == _glfw.x11.XdndEnter) + { + // A drag operation has entered the window + // TODO: Check if UTF-8 string is supported by the source + } + else if (event->xclient.message_type == _glfw.x11.XdndDrop) + { + // The drag operation has finished dropping on + // the window, ask to convert it to a UTF-8 string + _glfw.x11.xdnd.source = event->xclient.data.l[0]; + XConvertSelection(_glfw.x11.display, + _glfw.x11.XdndSelection, + _glfw.x11.UTF8_STRING, + _glfw.x11.XdndSelection, + window->x11.handle, CurrentTime); + } + else if (event->xclient.message_type == _glfw.x11.XdndPosition) + { + // The drag operation has moved over the window + const int absX = (event->xclient.data.l[2] >> 16) & 0xFFFF; + const int absY = (event->xclient.data.l[2]) & 0xFFFF; + int x, y; + + _glfwPlatformGetWindowPos(window, &x, &y); + _glfwInputCursorPos(window, absX - x, absY - y); + + // Reply that we are ready to copy the dragged data + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.type = ClientMessage; + reply.xclient.window = event->xclient.data.l[0]; + reply.xclient.message_type = _glfw.x11.XdndStatus; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = 1; // Always accept the dnd with no rectangle + reply.xclient.data.l[2] = 0; // Specify an empty rectangle + reply.xclient.data.l[3] = 0; + reply.xclient.data.l[4] = _glfw.x11.XdndActionCopy; + + XSendEvent(_glfw.x11.display, event->xclient.data.l[0], + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } + + return; + } + + case SelectionNotify: + { + if (event->xselection.property) + { + // The converted data from the drag operation has arrived + char* data; + const int result = + _glfwGetWindowPropertyX11(event->xselection.requestor, + event->xselection.property, + event->xselection.target, + (unsigned char**) &data); + + if (result) + { + int i, count; + char** paths = parseUriList(data, &count); + + _glfwInputDrop(window, count, (const char**) paths); + + for (i = 0; i < count; i++) + free(paths[i]); + free(paths); + } + + XFree(data); + + XEvent reply; + memset(&reply, 0, sizeof(reply)); + + reply.type = ClientMessage; + reply.xclient.window = _glfw.x11.xdnd.source; + reply.xclient.message_type = _glfw.x11.XdndFinished; + reply.xclient.format = 32; + reply.xclient.data.l[0] = window->x11.handle; + reply.xclient.data.l[1] = result; + reply.xclient.data.l[2] = _glfw.x11.XdndActionCopy; + + // Reply that all is well + XSendEvent(_glfw.x11.display, _glfw.x11.xdnd.source, + False, NoEventMask, &reply); + XFlush(_glfw.x11.display); + } + + return; + } + + case FocusIn: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); + + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->x11.ic) + XSetICFocus(window->x11.ic); + + _glfwInputWindowFocus(window, GLFW_TRUE); + return; + } + + case FocusOut: + { + if (window->cursorMode == GLFW_CURSOR_DISABLED) + _glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); + + if (event->xfocus.mode == NotifyGrab || + event->xfocus.mode == NotifyUngrab) + { + // Ignore focus events from popup indicator windows, window menu + // key chords and window dragging + return; + } + + if (window->x11.ic) + XUnsetICFocus(window->x11.ic); + + if (window->monitor && window->autoIconify) + _glfwPlatformIconifyWindow(window); + + _glfwInputWindowFocus(window, GLFW_FALSE); + return; + } + + case Expose: + { + _glfwInputWindowDamage(window); + return; + } + + case PropertyNotify: + { + if (event->xproperty.state != PropertyNewValue) + return; + + if (event->xproperty.atom == _glfw.x11.WM_STATE) + { + const int state = getWindowState(window); + if (state != IconicState && state != NormalState) + return; + + const GLFWbool iconified = (state == IconicState); + if (window->x11.iconified != iconified) + { + if (window->monitor) + { + if (iconified) + releaseMonitor(window); + else + acquireMonitor(window); + } + + window->x11.iconified = iconified; + _glfwInputWindowIconify(window, iconified); + } + } + else if (event->xproperty.atom == _glfw.x11.NET_WM_STATE) + { + const GLFWbool maximized = _glfwPlatformWindowMaximized(window); + if (window->x11.maximized != maximized) + { + window->x11.maximized = maximized; + _glfwInputWindowMaximize(window, maximized); + } + } + + return; + } + + case SelectionClear: + { + handleSelectionClear(event); + return; + } + + case SelectionRequest: + { + handleSelectionRequest(event); + return; + } + + case DestroyNotify: + return; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Retrieve a single window property of the specified type +// Inspired by fghGetWindowProperty from freeglut +// +unsigned long _glfwGetWindowPropertyX11(Window window, + Atom property, + Atom type, + unsigned char** value) +{ + Atom actualType; + int actualFormat; + unsigned long itemCount, bytesAfter; + + XGetWindowProperty(_glfw.x11.display, + window, + property, + 0, + LONG_MAX, + False, + type, + &actualType, + &actualFormat, + &itemCount, + &bytesAfter, + value); + + if (type != AnyPropertyType && actualType != type) + return 0; + + return itemCount; +} + +// Push contents of our selection to clipboard manager +// +void _glfwPushSelectionToManagerX11(void) +{ + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD_MANAGER, + _glfw.x11.SAVE_TARGETS, + None, + _glfw.x11.helperWindowHandle, + CurrentTime); + + for (;;) + { + XEvent event; + + while (XCheckIfEvent(_glfw.x11.display, &event, isSelectionEvent, NULL)) + { + switch (event.type) + { + case SelectionRequest: + handleSelectionRequest(&event); + break; + + case SelectionClear: + handleSelectionClear(&event); + break; + + case SelectionNotify: + { + if (event.xselection.target == _glfw.x11.SAVE_TARGETS) + { + // This means one of two things; either the selection was + // not owned, which means there is no clipboard manager, or + // the transfer to the clipboard manager has completed + // In either case, it means we are done here + return; + } + + break; + } + } + } + + waitForEvent(NULL); + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW platform API ////// +////////////////////////////////////////////////////////////////////////// + +int _glfwPlatformCreateWindow(_GLFWwindow* window, + const _GLFWwndconfig* wndconfig, + const _GLFWctxconfig* ctxconfig, + const _GLFWfbconfig* fbconfig) +{ + Visual* visual; + int depth; + + if (ctxconfig->client == GLFW_NO_API) + { + visual = DefaultVisual(_glfw.x11.display, _glfw.x11.screen); + depth = DefaultDepth(_glfw.x11.display, _glfw.x11.screen); + } + else + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwInitGLX()) + return GLFW_FALSE; + if (!_glfwChooseVisualGLX(ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } + else + { + if (!_glfwInitEGL()) + return GLFW_FALSE; + if (!_glfwChooseVisualEGL(ctxconfig, fbconfig, &visual, &depth)) + return GLFW_FALSE; + } + } + + if (!createNativeWindow(window, wndconfig, visual, depth)) + return GLFW_FALSE; + + if (ctxconfig->client != GLFW_NO_API) + { + if (ctxconfig->source == GLFW_NATIVE_CONTEXT_API) + { + if (!_glfwCreateContextGLX(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + else + { + if (!_glfwCreateContextEGL(window, ctxconfig, fbconfig)) + return GLFW_FALSE; + } + } + + if (window->monitor) + { + _glfwPlatformShowWindow(window); + updateWindowMode(window); + if (!acquireMonitor(window)) + return GLFW_FALSE; + + centerCursor(window); + } + + XFlush(_glfw.x11.display); + return GLFW_TRUE; +} + +void _glfwPlatformDestroyWindow(_GLFWwindow* window) +{ + if (_glfw.x11.disabledCursorWindow == window) + _glfw.x11.disabledCursorWindow = NULL; + + if (window->monitor) + releaseMonitor(window); + + if (window->x11.ic) + { + XDestroyIC(window->x11.ic); + window->x11.ic = NULL; + } + + if (window->context.destroy) + window->context.destroy(window); + + if (window->x11.handle) + { + XDeleteContext(_glfw.x11.display, window->x11.handle, _glfw.x11.context); + XUnmapWindow(_glfw.x11.display, window->x11.handle); + XDestroyWindow(_glfw.x11.display, window->x11.handle); + window->x11.handle = (Window) 0; + } + + if (window->x11.colormap) + { + XFreeColormap(_glfw.x11.display, window->x11.colormap); + window->x11.colormap = (Colormap) 0; + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) +{ +#if defined(X_HAVE_UTF8_STRING) + Xutf8SetWMProperties(_glfw.x11.display, + window->x11.handle, + title, title, + NULL, 0, + NULL, NULL, NULL); +#else + // This may be a slightly better fallback than using XStoreName and + // XSetIconName, which always store their arguments using STRING + XmbSetWMProperties(_glfw.x11.display, + window->x11.handle, + title, title, + NULL, 0, + NULL, NULL, NULL); +#endif + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON_NAME, _glfw.x11.UTF8_STRING, 8, + PropModeReplace, + (unsigned char*) title, strlen(title)); + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowIcon(_GLFWwindow* window, + int count, const GLFWimage* images) +{ + if (count) + { + int i, j, longCount = 0; + + for (i = 0; i < count; i++) + longCount += 2 + images[i].width * images[i].height; + + long* icon = calloc(longCount, sizeof(long)); + long* target = icon; + + for (i = 0; i < count; i++) + { + *target++ = images[i].width; + *target++ = images[i].height; + + for (j = 0; j < images[i].width * images[i].height; j++) + { + *target++ = (images[i].pixels[j * 4 + 0] << 16) | + (images[i].pixels[j * 4 + 1] << 8) | + (images[i].pixels[j * 4 + 2] << 0) | + (images[i].pixels[j * 4 + 3] << 24); + } + } + + XChangeProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON, + XA_CARDINAL, 32, + PropModeReplace, + (unsigned char*) icon, + longCount); + + free(icon); + } + else + { + XDeleteProperty(_glfw.x11.display, window->x11.handle, + _glfw.x11.NET_WM_ICON); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetWindowPos(_GLFWwindow* window, int* xpos, int* ypos) +{ + Window dummy; + int x, y; + + XTranslateCoordinates(_glfw.x11.display, window->x11.handle, _glfw.x11.root, + 0, 0, &x, &y, &dummy); + + if (xpos) + *xpos = x; + if (ypos) + *ypos = y; +} + +void _glfwPlatformSetWindowPos(_GLFWwindow* window, int xpos, int ypos) +{ + // HACK: Explicitly setting PPosition to any value causes some WMs, notably + // Compiz and Metacity, to honor the position of unmapped windows + if (!_glfwPlatformWindowVisible(window)) + { + long supplied; + XSizeHints* hints = XAllocSizeHints(); + + if (XGetWMNormalHints(_glfw.x11.display, window->x11.handle, hints, &supplied)) + { + hints->flags |= PPosition; + hints->x = hints->y = 0; + + XSetWMNormalHints(_glfw.x11.display, window->x11.handle, hints); + } + + XFree(hints); + } + + XMoveWindow(_glfw.x11.display, window->x11.handle, xpos, ypos); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetWindowSize(_GLFWwindow* window, int* width, int* height) +{ + XWindowAttributes attribs; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &attribs); + + if (width) + *width = attribs.width; + if (height) + *height = attribs.height; +} + +void _glfwPlatformSetWindowSize(_GLFWwindow* window, int width, int height) +{ + if (window->monitor) + { + if (window->monitor->window == window) + acquireMonitor(window); + } + else + { + if (!window->resizable) + updateNormalHints(window, width, height); + + XResizeWindow(_glfw.x11.display, window->x11.handle, width, height); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowSizeLimits(_GLFWwindow* window, + int minwidth, int minheight, + int maxwidth, int maxheight) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowAspectRatio(_GLFWwindow* window, int numer, int denom) +{ + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + updateNormalHints(window, width, height); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetFramebufferSize(_GLFWwindow* window, int* width, int* height) +{ + _glfwPlatformGetWindowSize(window, width, height); +} + +void _glfwPlatformGetWindowFrameSize(_GLFWwindow* window, + int* left, int* top, + int* right, int* bottom) +{ + long* extents = NULL; + + if (window->monitor || !window->decorated) + return; + + if (_glfw.x11.NET_FRAME_EXTENTS == None) + return; + + if (!_glfwPlatformWindowVisible(window) && + _glfw.x11.NET_REQUEST_FRAME_EXTENTS) + { + XEvent event; + double timeout = 0.5; + + // Ensure _NET_FRAME_EXTENTS is set, allowing glfwGetWindowFrameSize to + // function before the window is mapped + sendEventToWM(window, _glfw.x11.NET_REQUEST_FRAME_EXTENTS, + 0, 0, 0, 0, 0); + + // HACK: Use a timeout because earlier versions of some window managers + // (at least Unity, Fluxbox and Xfwm) failed to send the reply + // They have been fixed but broken versions are still in the wild + // If you are affected by this and your window manager is NOT + // listed above, PLEASE report it to their and our issue trackers + while (!XCheckIfEvent(_glfw.x11.display, + &event, + isFrameExtentsEvent, + (XPointer) window)) + { + if (!waitForEvent(&timeout)) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: The window manager has a broken _NET_REQUEST_FRAME_EXTENTS implementation; please report this issue"); + return; + } + } + } + + if (_glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_FRAME_EXTENTS, + XA_CARDINAL, + (unsigned char**) &extents) == 4) + { + if (left) + *left = extents[0]; + if (top) + *top = extents[2]; + if (right) + *right = extents[1]; + if (bottom) + *bottom = extents[3]; + } + + if (extents) + XFree(extents); +} + +void _glfwPlatformIconifyWindow(_GLFWwindow* window) +{ + if (window->x11.overrideRedirect) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + XIconifyWindow(_glfw.x11.display, window->x11.handle, _glfw.x11.screen); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformRestoreWindow(_GLFWwindow* window) +{ + if (window->x11.overrideRedirect) + { + // Override-redirect windows cannot be iconified or restored, as those + // tasks are performed by the window manager + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Iconification of full screen windows requires a WM that supports EWMH full screen"); + return; + } + + if (_glfwPlatformWindowIconified(window)) + { + XMapWindow(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); + } + else if (_glfwPlatformWindowVisible(window)) + { + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_REMOVE, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + } + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformMaximizeWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_WM_STATE && + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT && + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + sendEventToWM(window, + _glfw.x11.NET_WM_STATE, + _NET_WM_STATE_ADD, + _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT, + _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ, + 1, 0); + XFlush(_glfw.x11.display); + } +} + +void _glfwPlatformShowWindow(_GLFWwindow* window) +{ + if (_glfwPlatformWindowVisible(window)) + return; + + XMapWindow(_glfw.x11.display, window->x11.handle); + waitForVisibilityNotify(window); +} + +void _glfwPlatformHideWindow(_GLFWwindow* window) +{ + XUnmapWindow(_glfw.x11.display, window->x11.handle); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformFocusWindow(_GLFWwindow* window) +{ + if (_glfw.x11.NET_ACTIVE_WINDOW) + sendEventToWM(window, _glfw.x11.NET_ACTIVE_WINDOW, 1, 0, 0, 0, 0); + else + { + XRaiseWindow(_glfw.x11.display, window->x11.handle); + XSetInputFocus(_glfw.x11.display, window->x11.handle, + RevertToParent, CurrentTime); + } + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetWindowMonitor(_GLFWwindow* window, + _GLFWmonitor* monitor, + int xpos, int ypos, + int width, int height, + int refreshRate) +{ + if (window->monitor == monitor) + { + if (monitor) + { + if (monitor->window == window) + acquireMonitor(window); + } + else + { + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + return; + } + + if (window->monitor) + releaseMonitor(window); + + _glfwInputWindowMonitorChange(window, monitor); + updateNormalHints(window, width, height); + updateWindowMode(window); + + if (window->monitor) + { + XMapRaised(_glfw.x11.display, window->x11.handle); + if (waitForVisibilityNotify(window)) + acquireMonitor(window); + } + else + { + XMoveResizeWindow(_glfw.x11.display, window->x11.handle, + xpos, ypos, width, height); + } + + XFlush(_glfw.x11.display); +} + +int _glfwPlatformWindowFocused(_GLFWwindow* window) +{ + Window focused; + int state; + + XGetInputFocus(_glfw.x11.display, &focused, &state); + return window->x11.handle == focused; +} + +int _glfwPlatformWindowIconified(_GLFWwindow* window) +{ + return getWindowState(window) == IconicState; +} + +int _glfwPlatformWindowVisible(_GLFWwindow* window) +{ + XWindowAttributes wa; + XGetWindowAttributes(_glfw.x11.display, window->x11.handle, &wa); + return wa.map_state == IsViewable; +} + +int _glfwPlatformWindowMaximized(_GLFWwindow* window) +{ + Atom* states; + unsigned long i; + GLFWbool maximized = GLFW_FALSE; + const unsigned long count = + _glfwGetWindowPropertyX11(window->x11.handle, + _glfw.x11.NET_WM_STATE, + XA_ATOM, + (unsigned char**) &states); + + for (i = 0; i < count; i++) + { + if (states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_VERT || + states[i] == _glfw.x11.NET_WM_STATE_MAXIMIZED_HORZ) + { + maximized = GLFW_TRUE; + break; + } + } + + XFree(states); + return maximized; +} + +void _glfwPlatformPollEvents(void) +{ + _glfwPollJoystickEvents(); + + int count = XPending(_glfw.x11.display); + while (count--) + { + XEvent event; + XNextEvent(_glfw.x11.display, &event); + processEvent(&event); + } + + if (_glfw.x11.disabledCursorWindow) + centerCursor(_glfw.x11.disabledCursorWindow); + + XFlush(_glfw.x11.display); +} + +void _glfwPlatformWaitEvents(void) +{ + while (!XPending(_glfw.x11.display)) + waitForEvent(NULL); + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformWaitEventsTimeout(double timeout) +{ + while (!XPending(_glfw.x11.display)) + { + if (!waitForEvent(&timeout)) + break; + } + + _glfwPlatformPollEvents(); +} + +void _glfwPlatformPostEmptyEvent(void) +{ + XEvent event; + + memset(&event, 0, sizeof(event)); + event.type = ClientMessage; + event.xclient.window = _glfw.x11.helperWindowHandle; + event.xclient.format = 32; // Data is 32-bit longs + event.xclient.message_type = _glfw.x11.NULL_; + + XSendEvent(_glfw.x11.display, _glfw.x11.helperWindowHandle, False, 0, &event); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer(_glfw.x11.display, window->x11.handle, + &root, &child, + &rootX, &rootY, &childX, &childY, + &mask); + + if (xpos) + *xpos = childX; + if (ypos) + *ypos = childY; +} + +void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) +{ + // Store the new position so it can be recognized later + window->x11.warpCursorPosX = (int) x; + window->x11.warpCursorPosY = (int) y; + + XWarpPointer(_glfw.x11.display, None, window->x11.handle, + 0,0,0,0, (int) x, (int) y); + XFlush(_glfw.x11.display); +} + +void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) +{ + if (mode == GLFW_CURSOR_DISABLED) + { + _glfw.x11.disabledCursorWindow = window; + _glfwPlatformGetCursorPos(window, + &_glfw.x11.restoreCursorPosX, + &_glfw.x11.restoreCursorPosY); + centerCursor(window); + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, + _glfw.x11.hiddenCursorHandle, + CurrentTime); + } + else if (_glfw.x11.disabledCursorWindow == window) + { + _glfw.x11.disabledCursorWindow = NULL; + XUngrabPointer(_glfw.x11.display, CurrentTime); + _glfwPlatformSetCursorPos(window, + _glfw.x11.restoreCursorPosX, + _glfw.x11.restoreCursorPosY); + } + + updateCursorImage(window); + XFlush(_glfw.x11.display); +} + +const char* _glfwPlatformGetKeyName(int key, int scancode) +{ + KeySym keysym; + int extra; + + if (!_glfw.x11.xkb.available) + return NULL; + + if (key != GLFW_KEY_UNKNOWN) + scancode = _glfw.x11.scancodes[key]; + + if (!_glfwIsPrintable(_glfw.x11.keycodes[scancode])) + return NULL; + + keysym = XkbKeycodeToKeysym(_glfw.x11.display, scancode, 0, 0); + if (keysym == NoSymbol) + return NULL; + + XkbTranslateKeySym(_glfw.x11.display, &keysym, 0, + _glfw.x11.keyName, sizeof(_glfw.x11.keyName), + &extra); + + if (!strlen(_glfw.x11.keyName)) + return NULL; + + return _glfw.x11.keyName; +} + +int _glfwPlatformGetKeyScancode(int key) +{ + return _glfw.x11.scancodes[key]; +} + +int _glfwPlatformCreateCursor(_GLFWcursor* cursor, + const GLFWimage* image, + int xhot, int yhot) +{ + cursor->x11.handle = _glfwCreateCursorX11(image, xhot, yhot); + if (!cursor->x11.handle) + return GLFW_FALSE; + + return GLFW_TRUE; +} + +int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, int shape) +{ + cursor->x11.handle = XCreateFontCursor(_glfw.x11.display, + translateCursorShape(shape)); + if (!cursor->x11.handle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create standard cursor"); + return GLFW_FALSE; + } + + return GLFW_TRUE; +} + +void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) +{ + if (cursor->x11.handle) + XFreeCursor(_glfw.x11.display, cursor->x11.handle); +} + +void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) +{ + if (window->cursorMode == GLFW_CURSOR_NORMAL) + { + updateCursorImage(window); + XFlush(_glfw.x11.display); + } +} + +void _glfwPlatformSetClipboardString(_GLFWwindow* window, const char* string) +{ + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = strdup(string); + + XSetSelectionOwner(_glfw.x11.display, + _glfw.x11.CLIPBOARD, + _glfw.x11.helperWindowHandle, + CurrentTime); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) != + _glfw.x11.helperWindowHandle) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to become owner of clipboard selection"); + } +} + +const char* _glfwPlatformGetClipboardString(_GLFWwindow* window) +{ + size_t i; + const Atom formats[] = { _glfw.x11.UTF8_STRING, + _glfw.x11.COMPOUND_STRING, + XA_STRING }; + const size_t formatCount = sizeof(formats) / sizeof(formats[0]); + + if (XGetSelectionOwner(_glfw.x11.display, _glfw.x11.CLIPBOARD) == + _glfw.x11.helperWindowHandle) + { + // Instead of doing a large number of X round-trips just to put this + // string into a window property and then read it back, just return it + return _glfw.x11.clipboardString; + } + + free(_glfw.x11.clipboardString); + _glfw.x11.clipboardString = NULL; + + for (i = 0; i < formatCount; i++) + { + char* data; + XEvent event; + + XConvertSelection(_glfw.x11.display, + _glfw.x11.CLIPBOARD, + formats[i], + _glfw.x11.GLFW_SELECTION, + _glfw.x11.helperWindowHandle, + CurrentTime); + + while (!XCheckTypedWindowEvent(_glfw.x11.display, + _glfw.x11.helperWindowHandle, + SelectionNotify, + &event)) + { + waitForEvent(NULL); + } + + if (event.xselection.property == None) + continue; + + if (_glfwGetWindowPropertyX11(event.xselection.requestor, + event.xselection.property, + event.xselection.target, + (unsigned char**) &data)) + { + _glfw.x11.clipboardString = strdup(data); + } + + XFree(data); + + XDeleteProperty(_glfw.x11.display, + event.xselection.requestor, + event.xselection.property); + + if (_glfw.x11.clipboardString) + break; + } + + if (_glfw.x11.clipboardString == NULL) + { + _glfwInputError(GLFW_FORMAT_UNAVAILABLE, + "X11: Failed to convert clipboard to string"); + } + + return _glfw.x11.clipboardString; +} + +void _glfwPlatformGetRequiredInstanceExtensions(char** extensions) +{ + if (!_glfw.vk.KHR_surface) + return; + + if (!_glfw.vk.KHR_xcb_surface || !_glfw.x11.x11xcb.handle) + { + if (!_glfw.vk.KHR_xlib_surface) + return; + } + + extensions[0] = "VK_KHR_surface"; + + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + extensions[1] = "VK_KHR_xcb_surface"; + else + extensions[1] = "VK_KHR_xlib_surface"; +} + +int _glfwPlatformGetPhysicalDevicePresentationSupport(VkInstance instance, + VkPhysicalDevice device, + uint32_t queuefamily) +{ + VisualID visualID = XVisualIDFromVisual(DefaultVisual(_glfw.x11.display, + _glfw.x11.screen)); + + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + { + PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR vkGetPhysicalDeviceXcbPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXcbPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXcbPresentationSupportKHR"); + if (!vkGetPhysicalDeviceXcbPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); + return GLFW_FALSE; + } + + xcb_connection_t* connection = + _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + if (!connection) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to retrieve XCB connection"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceXcbPresentationSupportKHR(device, + queuefamily, + connection, + visualID); + } + else + { + PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR vkGetPhysicalDeviceXlibPresentationSupportKHR = + (PFN_vkGetPhysicalDeviceXlibPresentationSupportKHR) + vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceXlibPresentationSupportKHR"); + if (!vkGetPhysicalDeviceXlibPresentationSupportKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return GLFW_FALSE; + } + + return vkGetPhysicalDeviceXlibPresentationSupportKHR(device, + queuefamily, + _glfw.x11.display, + visualID); + } +} + +VkResult _glfwPlatformCreateWindowSurface(VkInstance instance, + _GLFWwindow* window, + const VkAllocationCallbacks* allocator, + VkSurfaceKHR* surface) +{ + if (_glfw.vk.KHR_xcb_surface && _glfw.x11.x11xcb.handle) + { + VkResult err; + VkXcbSurfaceCreateInfoKHR sci; + PFN_vkCreateXcbSurfaceKHR vkCreateXcbSurfaceKHR; + + xcb_connection_t* connection = + _glfw.x11.x11xcb.XGetXCBConnection(_glfw.x11.display); + if (!connection) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to retrieve XCB connection"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + vkCreateXcbSurfaceKHR = (PFN_vkCreateXcbSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateXcbSurfaceKHR"); + if (!vkCreateXcbSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xcb_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_XCB_SURFACE_CREATE_INFO_KHR; + sci.connection = connection; + sci.window = window->x11.handle; + + err = vkCreateXcbSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create Vulkan XCB surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; + } + else + { + VkResult err; + VkXlibSurfaceCreateInfoKHR sci; + PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR; + + vkCreateXlibSurfaceKHR = (PFN_vkCreateXlibSurfaceKHR) + vkGetInstanceProcAddr(instance, "vkCreateXlibSurfaceKHR"); + if (!vkCreateXlibSurfaceKHR) + { + _glfwInputError(GLFW_API_UNAVAILABLE, + "X11: Vulkan instance missing VK_KHR_xlib_surface extension"); + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + + memset(&sci, 0, sizeof(sci)); + sci.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR; + sci.dpy = _glfw.x11.display; + sci.window = window->x11.handle; + + err = vkCreateXlibSurfaceKHR(instance, &sci, allocator, surface); + if (err) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "X11: Failed to create Vulkan X11 surface: %s", + _glfwGetVulkanResultString(err)); + } + + return err; + } +} + + +////////////////////////////////////////////////////////////////////////// +////// GLFW native API ////// +////////////////////////////////////////////////////////////////////////// + +GLFWAPI Display* glfwGetX11Display(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(NULL); + return _glfw.x11.display; +} + +GLFWAPI Window glfwGetX11Window(GLFWwindow* handle) +{ + _GLFWwindow* window = (_GLFWwindow*) handle; + _GLFW_REQUIRE_INIT_OR_RETURN(None); + return window->x11.handle; +} + diff --git a/apps/exampleViewer/common/glfw/src/xkb_unicode.c b/apps/exampleViewer/common/glfw/src/xkb_unicode.c new file mode 100644 index 0000000000..e16a070ca7 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/xkb_unicode.c @@ -0,0 +1,889 @@ +//======================================================================== +// GLFW 3.3 X11 - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2002-2006 Marcus Geelnard +// Copyright (c) 2006-2016 Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#include "internal.h" + + +/* + * Marcus: This code was originally written by Markus G. Kuhn. + * I have made some slight changes (trimmed it down a bit from >60 KB to + * 20 KB), but the functionality is the same. + */ + +/* + * This module converts keysym values into the corresponding ISO 10646 + * (UCS, Unicode) values. + * + * The array keysymtab[] contains pairs of X11 keysym values for graphical + * characters and the corresponding Unicode value. The function + * _glfwKeySym2Unicode() maps a keysym onto a Unicode value using a binary + * search, therefore keysymtab[] must remain SORTED by keysym value. + * + * We allow to represent any UCS character in the range U-00000000 to + * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff. + * This admittedly does not cover the entire 31-bit space of UCS, but + * it does cover all of the characters up to U-10FFFF, which can be + * represented by UTF-16, and more, and it is very unlikely that higher + * UCS codes will ever be assigned by ISO. So to get Unicode character + * U+ABCD you can directly use keysym 0x0100abcd. + * + * Original author: Markus G. Kuhn , University of + * Cambridge, April 2001 + * + * Special thanks to Richard Verhoeven for preparing + * an initial draft of the mapping table. + * + */ + + +//************************************************************************ +//**** KeySym to Unicode mapping table **** +//************************************************************************ + +static const struct codepair { + unsigned short keysym; + unsigned short ucs; +} keysymtab[] = { + { 0x01a1, 0x0104 }, + { 0x01a2, 0x02d8 }, + { 0x01a3, 0x0141 }, + { 0x01a5, 0x013d }, + { 0x01a6, 0x015a }, + { 0x01a9, 0x0160 }, + { 0x01aa, 0x015e }, + { 0x01ab, 0x0164 }, + { 0x01ac, 0x0179 }, + { 0x01ae, 0x017d }, + { 0x01af, 0x017b }, + { 0x01b1, 0x0105 }, + { 0x01b2, 0x02db }, + { 0x01b3, 0x0142 }, + { 0x01b5, 0x013e }, + { 0x01b6, 0x015b }, + { 0x01b7, 0x02c7 }, + { 0x01b9, 0x0161 }, + { 0x01ba, 0x015f }, + { 0x01bb, 0x0165 }, + { 0x01bc, 0x017a }, + { 0x01bd, 0x02dd }, + { 0x01be, 0x017e }, + { 0x01bf, 0x017c }, + { 0x01c0, 0x0154 }, + { 0x01c3, 0x0102 }, + { 0x01c5, 0x0139 }, + { 0x01c6, 0x0106 }, + { 0x01c8, 0x010c }, + { 0x01ca, 0x0118 }, + { 0x01cc, 0x011a }, + { 0x01cf, 0x010e }, + { 0x01d0, 0x0110 }, + { 0x01d1, 0x0143 }, + { 0x01d2, 0x0147 }, + { 0x01d5, 0x0150 }, + { 0x01d8, 0x0158 }, + { 0x01d9, 0x016e }, + { 0x01db, 0x0170 }, + { 0x01de, 0x0162 }, + { 0x01e0, 0x0155 }, + { 0x01e3, 0x0103 }, + { 0x01e5, 0x013a }, + { 0x01e6, 0x0107 }, + { 0x01e8, 0x010d }, + { 0x01ea, 0x0119 }, + { 0x01ec, 0x011b }, + { 0x01ef, 0x010f }, + { 0x01f0, 0x0111 }, + { 0x01f1, 0x0144 }, + { 0x01f2, 0x0148 }, + { 0x01f5, 0x0151 }, + { 0x01f8, 0x0159 }, + { 0x01f9, 0x016f }, + { 0x01fb, 0x0171 }, + { 0x01fe, 0x0163 }, + { 0x01ff, 0x02d9 }, + { 0x02a1, 0x0126 }, + { 0x02a6, 0x0124 }, + { 0x02a9, 0x0130 }, + { 0x02ab, 0x011e }, + { 0x02ac, 0x0134 }, + { 0x02b1, 0x0127 }, + { 0x02b6, 0x0125 }, + { 0x02b9, 0x0131 }, + { 0x02bb, 0x011f }, + { 0x02bc, 0x0135 }, + { 0x02c5, 0x010a }, + { 0x02c6, 0x0108 }, + { 0x02d5, 0x0120 }, + { 0x02d8, 0x011c }, + { 0x02dd, 0x016c }, + { 0x02de, 0x015c }, + { 0x02e5, 0x010b }, + { 0x02e6, 0x0109 }, + { 0x02f5, 0x0121 }, + { 0x02f8, 0x011d }, + { 0x02fd, 0x016d }, + { 0x02fe, 0x015d }, + { 0x03a2, 0x0138 }, + { 0x03a3, 0x0156 }, + { 0x03a5, 0x0128 }, + { 0x03a6, 0x013b }, + { 0x03aa, 0x0112 }, + { 0x03ab, 0x0122 }, + { 0x03ac, 0x0166 }, + { 0x03b3, 0x0157 }, + { 0x03b5, 0x0129 }, + { 0x03b6, 0x013c }, + { 0x03ba, 0x0113 }, + { 0x03bb, 0x0123 }, + { 0x03bc, 0x0167 }, + { 0x03bd, 0x014a }, + { 0x03bf, 0x014b }, + { 0x03c0, 0x0100 }, + { 0x03c7, 0x012e }, + { 0x03cc, 0x0116 }, + { 0x03cf, 0x012a }, + { 0x03d1, 0x0145 }, + { 0x03d2, 0x014c }, + { 0x03d3, 0x0136 }, + { 0x03d9, 0x0172 }, + { 0x03dd, 0x0168 }, + { 0x03de, 0x016a }, + { 0x03e0, 0x0101 }, + { 0x03e7, 0x012f }, + { 0x03ec, 0x0117 }, + { 0x03ef, 0x012b }, + { 0x03f1, 0x0146 }, + { 0x03f2, 0x014d }, + { 0x03f3, 0x0137 }, + { 0x03f9, 0x0173 }, + { 0x03fd, 0x0169 }, + { 0x03fe, 0x016b }, + { 0x047e, 0x203e }, + { 0x04a1, 0x3002 }, + { 0x04a2, 0x300c }, + { 0x04a3, 0x300d }, + { 0x04a4, 0x3001 }, + { 0x04a5, 0x30fb }, + { 0x04a6, 0x30f2 }, + { 0x04a7, 0x30a1 }, + { 0x04a8, 0x30a3 }, + { 0x04a9, 0x30a5 }, + { 0x04aa, 0x30a7 }, + { 0x04ab, 0x30a9 }, + { 0x04ac, 0x30e3 }, + { 0x04ad, 0x30e5 }, + { 0x04ae, 0x30e7 }, + { 0x04af, 0x30c3 }, + { 0x04b0, 0x30fc }, + { 0x04b1, 0x30a2 }, + { 0x04b2, 0x30a4 }, + { 0x04b3, 0x30a6 }, + { 0x04b4, 0x30a8 }, + { 0x04b5, 0x30aa }, + { 0x04b6, 0x30ab }, + { 0x04b7, 0x30ad }, + { 0x04b8, 0x30af }, + { 0x04b9, 0x30b1 }, + { 0x04ba, 0x30b3 }, + { 0x04bb, 0x30b5 }, + { 0x04bc, 0x30b7 }, + { 0x04bd, 0x30b9 }, + { 0x04be, 0x30bb }, + { 0x04bf, 0x30bd }, + { 0x04c0, 0x30bf }, + { 0x04c1, 0x30c1 }, + { 0x04c2, 0x30c4 }, + { 0x04c3, 0x30c6 }, + { 0x04c4, 0x30c8 }, + { 0x04c5, 0x30ca }, + { 0x04c6, 0x30cb }, + { 0x04c7, 0x30cc }, + { 0x04c8, 0x30cd }, + { 0x04c9, 0x30ce }, + { 0x04ca, 0x30cf }, + { 0x04cb, 0x30d2 }, + { 0x04cc, 0x30d5 }, + { 0x04cd, 0x30d8 }, + { 0x04ce, 0x30db }, + { 0x04cf, 0x30de }, + { 0x04d0, 0x30df }, + { 0x04d1, 0x30e0 }, + { 0x04d2, 0x30e1 }, + { 0x04d3, 0x30e2 }, + { 0x04d4, 0x30e4 }, + { 0x04d5, 0x30e6 }, + { 0x04d6, 0x30e8 }, + { 0x04d7, 0x30e9 }, + { 0x04d8, 0x30ea }, + { 0x04d9, 0x30eb }, + { 0x04da, 0x30ec }, + { 0x04db, 0x30ed }, + { 0x04dc, 0x30ef }, + { 0x04dd, 0x30f3 }, + { 0x04de, 0x309b }, + { 0x04df, 0x309c }, + { 0x05ac, 0x060c }, + { 0x05bb, 0x061b }, + { 0x05bf, 0x061f }, + { 0x05c1, 0x0621 }, + { 0x05c2, 0x0622 }, + { 0x05c3, 0x0623 }, + { 0x05c4, 0x0624 }, + { 0x05c5, 0x0625 }, + { 0x05c6, 0x0626 }, + { 0x05c7, 0x0627 }, + { 0x05c8, 0x0628 }, + { 0x05c9, 0x0629 }, + { 0x05ca, 0x062a }, + { 0x05cb, 0x062b }, + { 0x05cc, 0x062c }, + { 0x05cd, 0x062d }, + { 0x05ce, 0x062e }, + { 0x05cf, 0x062f }, + { 0x05d0, 0x0630 }, + { 0x05d1, 0x0631 }, + { 0x05d2, 0x0632 }, + { 0x05d3, 0x0633 }, + { 0x05d4, 0x0634 }, + { 0x05d5, 0x0635 }, + { 0x05d6, 0x0636 }, + { 0x05d7, 0x0637 }, + { 0x05d8, 0x0638 }, + { 0x05d9, 0x0639 }, + { 0x05da, 0x063a }, + { 0x05e0, 0x0640 }, + { 0x05e1, 0x0641 }, + { 0x05e2, 0x0642 }, + { 0x05e3, 0x0643 }, + { 0x05e4, 0x0644 }, + { 0x05e5, 0x0645 }, + { 0x05e6, 0x0646 }, + { 0x05e7, 0x0647 }, + { 0x05e8, 0x0648 }, + { 0x05e9, 0x0649 }, + { 0x05ea, 0x064a }, + { 0x05eb, 0x064b }, + { 0x05ec, 0x064c }, + { 0x05ed, 0x064d }, + { 0x05ee, 0x064e }, + { 0x05ef, 0x064f }, + { 0x05f0, 0x0650 }, + { 0x05f1, 0x0651 }, + { 0x05f2, 0x0652 }, + { 0x06a1, 0x0452 }, + { 0x06a2, 0x0453 }, + { 0x06a3, 0x0451 }, + { 0x06a4, 0x0454 }, + { 0x06a5, 0x0455 }, + { 0x06a6, 0x0456 }, + { 0x06a7, 0x0457 }, + { 0x06a8, 0x0458 }, + { 0x06a9, 0x0459 }, + { 0x06aa, 0x045a }, + { 0x06ab, 0x045b }, + { 0x06ac, 0x045c }, + { 0x06ae, 0x045e }, + { 0x06af, 0x045f }, + { 0x06b0, 0x2116 }, + { 0x06b1, 0x0402 }, + { 0x06b2, 0x0403 }, + { 0x06b3, 0x0401 }, + { 0x06b4, 0x0404 }, + { 0x06b5, 0x0405 }, + { 0x06b6, 0x0406 }, + { 0x06b7, 0x0407 }, + { 0x06b8, 0x0408 }, + { 0x06b9, 0x0409 }, + { 0x06ba, 0x040a }, + { 0x06bb, 0x040b }, + { 0x06bc, 0x040c }, + { 0x06be, 0x040e }, + { 0x06bf, 0x040f }, + { 0x06c0, 0x044e }, + { 0x06c1, 0x0430 }, + { 0x06c2, 0x0431 }, + { 0x06c3, 0x0446 }, + { 0x06c4, 0x0434 }, + { 0x06c5, 0x0435 }, + { 0x06c6, 0x0444 }, + { 0x06c7, 0x0433 }, + { 0x06c8, 0x0445 }, + { 0x06c9, 0x0438 }, + { 0x06ca, 0x0439 }, + { 0x06cb, 0x043a }, + { 0x06cc, 0x043b }, + { 0x06cd, 0x043c }, + { 0x06ce, 0x043d }, + { 0x06cf, 0x043e }, + { 0x06d0, 0x043f }, + { 0x06d1, 0x044f }, + { 0x06d2, 0x0440 }, + { 0x06d3, 0x0441 }, + { 0x06d4, 0x0442 }, + { 0x06d5, 0x0443 }, + { 0x06d6, 0x0436 }, + { 0x06d7, 0x0432 }, + { 0x06d8, 0x044c }, + { 0x06d9, 0x044b }, + { 0x06da, 0x0437 }, + { 0x06db, 0x0448 }, + { 0x06dc, 0x044d }, + { 0x06dd, 0x0449 }, + { 0x06de, 0x0447 }, + { 0x06df, 0x044a }, + { 0x06e0, 0x042e }, + { 0x06e1, 0x0410 }, + { 0x06e2, 0x0411 }, + { 0x06e3, 0x0426 }, + { 0x06e4, 0x0414 }, + { 0x06e5, 0x0415 }, + { 0x06e6, 0x0424 }, + { 0x06e7, 0x0413 }, + { 0x06e8, 0x0425 }, + { 0x06e9, 0x0418 }, + { 0x06ea, 0x0419 }, + { 0x06eb, 0x041a }, + { 0x06ec, 0x041b }, + { 0x06ed, 0x041c }, + { 0x06ee, 0x041d }, + { 0x06ef, 0x041e }, + { 0x06f0, 0x041f }, + { 0x06f1, 0x042f }, + { 0x06f2, 0x0420 }, + { 0x06f3, 0x0421 }, + { 0x06f4, 0x0422 }, + { 0x06f5, 0x0423 }, + { 0x06f6, 0x0416 }, + { 0x06f7, 0x0412 }, + { 0x06f8, 0x042c }, + { 0x06f9, 0x042b }, + { 0x06fa, 0x0417 }, + { 0x06fb, 0x0428 }, + { 0x06fc, 0x042d }, + { 0x06fd, 0x0429 }, + { 0x06fe, 0x0427 }, + { 0x06ff, 0x042a }, + { 0x07a1, 0x0386 }, + { 0x07a2, 0x0388 }, + { 0x07a3, 0x0389 }, + { 0x07a4, 0x038a }, + { 0x07a5, 0x03aa }, + { 0x07a7, 0x038c }, + { 0x07a8, 0x038e }, + { 0x07a9, 0x03ab }, + { 0x07ab, 0x038f }, + { 0x07ae, 0x0385 }, + { 0x07af, 0x2015 }, + { 0x07b1, 0x03ac }, + { 0x07b2, 0x03ad }, + { 0x07b3, 0x03ae }, + { 0x07b4, 0x03af }, + { 0x07b5, 0x03ca }, + { 0x07b6, 0x0390 }, + { 0x07b7, 0x03cc }, + { 0x07b8, 0x03cd }, + { 0x07b9, 0x03cb }, + { 0x07ba, 0x03b0 }, + { 0x07bb, 0x03ce }, + { 0x07c1, 0x0391 }, + { 0x07c2, 0x0392 }, + { 0x07c3, 0x0393 }, + { 0x07c4, 0x0394 }, + { 0x07c5, 0x0395 }, + { 0x07c6, 0x0396 }, + { 0x07c7, 0x0397 }, + { 0x07c8, 0x0398 }, + { 0x07c9, 0x0399 }, + { 0x07ca, 0x039a }, + { 0x07cb, 0x039b }, + { 0x07cc, 0x039c }, + { 0x07cd, 0x039d }, + { 0x07ce, 0x039e }, + { 0x07cf, 0x039f }, + { 0x07d0, 0x03a0 }, + { 0x07d1, 0x03a1 }, + { 0x07d2, 0x03a3 }, + { 0x07d4, 0x03a4 }, + { 0x07d5, 0x03a5 }, + { 0x07d6, 0x03a6 }, + { 0x07d7, 0x03a7 }, + { 0x07d8, 0x03a8 }, + { 0x07d9, 0x03a9 }, + { 0x07e1, 0x03b1 }, + { 0x07e2, 0x03b2 }, + { 0x07e3, 0x03b3 }, + { 0x07e4, 0x03b4 }, + { 0x07e5, 0x03b5 }, + { 0x07e6, 0x03b6 }, + { 0x07e7, 0x03b7 }, + { 0x07e8, 0x03b8 }, + { 0x07e9, 0x03b9 }, + { 0x07ea, 0x03ba }, + { 0x07eb, 0x03bb }, + { 0x07ec, 0x03bc }, + { 0x07ed, 0x03bd }, + { 0x07ee, 0x03be }, + { 0x07ef, 0x03bf }, + { 0x07f0, 0x03c0 }, + { 0x07f1, 0x03c1 }, + { 0x07f2, 0x03c3 }, + { 0x07f3, 0x03c2 }, + { 0x07f4, 0x03c4 }, + { 0x07f5, 0x03c5 }, + { 0x07f6, 0x03c6 }, + { 0x07f7, 0x03c7 }, + { 0x07f8, 0x03c8 }, + { 0x07f9, 0x03c9 }, + { 0x08a1, 0x23b7 }, + { 0x08a2, 0x250c }, + { 0x08a3, 0x2500 }, + { 0x08a4, 0x2320 }, + { 0x08a5, 0x2321 }, + { 0x08a6, 0x2502 }, + { 0x08a7, 0x23a1 }, + { 0x08a8, 0x23a3 }, + { 0x08a9, 0x23a4 }, + { 0x08aa, 0x23a6 }, + { 0x08ab, 0x239b }, + { 0x08ac, 0x239d }, + { 0x08ad, 0x239e }, + { 0x08ae, 0x23a0 }, + { 0x08af, 0x23a8 }, + { 0x08b0, 0x23ac }, + { 0x08bc, 0x2264 }, + { 0x08bd, 0x2260 }, + { 0x08be, 0x2265 }, + { 0x08bf, 0x222b }, + { 0x08c0, 0x2234 }, + { 0x08c1, 0x221d }, + { 0x08c2, 0x221e }, + { 0x08c5, 0x2207 }, + { 0x08c8, 0x223c }, + { 0x08c9, 0x2243 }, + { 0x08cd, 0x21d4 }, + { 0x08ce, 0x21d2 }, + { 0x08cf, 0x2261 }, + { 0x08d6, 0x221a }, + { 0x08da, 0x2282 }, + { 0x08db, 0x2283 }, + { 0x08dc, 0x2229 }, + { 0x08dd, 0x222a }, + { 0x08de, 0x2227 }, + { 0x08df, 0x2228 }, + { 0x08ef, 0x2202 }, + { 0x08f6, 0x0192 }, + { 0x08fb, 0x2190 }, + { 0x08fc, 0x2191 }, + { 0x08fd, 0x2192 }, + { 0x08fe, 0x2193 }, + { 0x09e0, 0x25c6 }, + { 0x09e1, 0x2592 }, + { 0x09e2, 0x2409 }, + { 0x09e3, 0x240c }, + { 0x09e4, 0x240d }, + { 0x09e5, 0x240a }, + { 0x09e8, 0x2424 }, + { 0x09e9, 0x240b }, + { 0x09ea, 0x2518 }, + { 0x09eb, 0x2510 }, + { 0x09ec, 0x250c }, + { 0x09ed, 0x2514 }, + { 0x09ee, 0x253c }, + { 0x09ef, 0x23ba }, + { 0x09f0, 0x23bb }, + { 0x09f1, 0x2500 }, + { 0x09f2, 0x23bc }, + { 0x09f3, 0x23bd }, + { 0x09f4, 0x251c }, + { 0x09f5, 0x2524 }, + { 0x09f6, 0x2534 }, + { 0x09f7, 0x252c }, + { 0x09f8, 0x2502 }, + { 0x0aa1, 0x2003 }, + { 0x0aa2, 0x2002 }, + { 0x0aa3, 0x2004 }, + { 0x0aa4, 0x2005 }, + { 0x0aa5, 0x2007 }, + { 0x0aa6, 0x2008 }, + { 0x0aa7, 0x2009 }, + { 0x0aa8, 0x200a }, + { 0x0aa9, 0x2014 }, + { 0x0aaa, 0x2013 }, + { 0x0aae, 0x2026 }, + { 0x0aaf, 0x2025 }, + { 0x0ab0, 0x2153 }, + { 0x0ab1, 0x2154 }, + { 0x0ab2, 0x2155 }, + { 0x0ab3, 0x2156 }, + { 0x0ab4, 0x2157 }, + { 0x0ab5, 0x2158 }, + { 0x0ab6, 0x2159 }, + { 0x0ab7, 0x215a }, + { 0x0ab8, 0x2105 }, + { 0x0abb, 0x2012 }, + { 0x0abc, 0x2329 }, + { 0x0abe, 0x232a }, + { 0x0ac3, 0x215b }, + { 0x0ac4, 0x215c }, + { 0x0ac5, 0x215d }, + { 0x0ac6, 0x215e }, + { 0x0ac9, 0x2122 }, + { 0x0aca, 0x2613 }, + { 0x0acc, 0x25c1 }, + { 0x0acd, 0x25b7 }, + { 0x0ace, 0x25cb }, + { 0x0acf, 0x25af }, + { 0x0ad0, 0x2018 }, + { 0x0ad1, 0x2019 }, + { 0x0ad2, 0x201c }, + { 0x0ad3, 0x201d }, + { 0x0ad4, 0x211e }, + { 0x0ad6, 0x2032 }, + { 0x0ad7, 0x2033 }, + { 0x0ad9, 0x271d }, + { 0x0adb, 0x25ac }, + { 0x0adc, 0x25c0 }, + { 0x0add, 0x25b6 }, + { 0x0ade, 0x25cf }, + { 0x0adf, 0x25ae }, + { 0x0ae0, 0x25e6 }, + { 0x0ae1, 0x25ab }, + { 0x0ae2, 0x25ad }, + { 0x0ae3, 0x25b3 }, + { 0x0ae4, 0x25bd }, + { 0x0ae5, 0x2606 }, + { 0x0ae6, 0x2022 }, + { 0x0ae7, 0x25aa }, + { 0x0ae8, 0x25b2 }, + { 0x0ae9, 0x25bc }, + { 0x0aea, 0x261c }, + { 0x0aeb, 0x261e }, + { 0x0aec, 0x2663 }, + { 0x0aed, 0x2666 }, + { 0x0aee, 0x2665 }, + { 0x0af0, 0x2720 }, + { 0x0af1, 0x2020 }, + { 0x0af2, 0x2021 }, + { 0x0af3, 0x2713 }, + { 0x0af4, 0x2717 }, + { 0x0af5, 0x266f }, + { 0x0af6, 0x266d }, + { 0x0af7, 0x2642 }, + { 0x0af8, 0x2640 }, + { 0x0af9, 0x260e }, + { 0x0afa, 0x2315 }, + { 0x0afb, 0x2117 }, + { 0x0afc, 0x2038 }, + { 0x0afd, 0x201a }, + { 0x0afe, 0x201e }, + { 0x0ba3, 0x003c }, + { 0x0ba6, 0x003e }, + { 0x0ba8, 0x2228 }, + { 0x0ba9, 0x2227 }, + { 0x0bc0, 0x00af }, + { 0x0bc2, 0x22a5 }, + { 0x0bc3, 0x2229 }, + { 0x0bc4, 0x230a }, + { 0x0bc6, 0x005f }, + { 0x0bca, 0x2218 }, + { 0x0bcc, 0x2395 }, + { 0x0bce, 0x22a4 }, + { 0x0bcf, 0x25cb }, + { 0x0bd3, 0x2308 }, + { 0x0bd6, 0x222a }, + { 0x0bd8, 0x2283 }, + { 0x0bda, 0x2282 }, + { 0x0bdc, 0x22a2 }, + { 0x0bfc, 0x22a3 }, + { 0x0cdf, 0x2017 }, + { 0x0ce0, 0x05d0 }, + { 0x0ce1, 0x05d1 }, + { 0x0ce2, 0x05d2 }, + { 0x0ce3, 0x05d3 }, + { 0x0ce4, 0x05d4 }, + { 0x0ce5, 0x05d5 }, + { 0x0ce6, 0x05d6 }, + { 0x0ce7, 0x05d7 }, + { 0x0ce8, 0x05d8 }, + { 0x0ce9, 0x05d9 }, + { 0x0cea, 0x05da }, + { 0x0ceb, 0x05db }, + { 0x0cec, 0x05dc }, + { 0x0ced, 0x05dd }, + { 0x0cee, 0x05de }, + { 0x0cef, 0x05df }, + { 0x0cf0, 0x05e0 }, + { 0x0cf1, 0x05e1 }, + { 0x0cf2, 0x05e2 }, + { 0x0cf3, 0x05e3 }, + { 0x0cf4, 0x05e4 }, + { 0x0cf5, 0x05e5 }, + { 0x0cf6, 0x05e6 }, + { 0x0cf7, 0x05e7 }, + { 0x0cf8, 0x05e8 }, + { 0x0cf9, 0x05e9 }, + { 0x0cfa, 0x05ea }, + { 0x0da1, 0x0e01 }, + { 0x0da2, 0x0e02 }, + { 0x0da3, 0x0e03 }, + { 0x0da4, 0x0e04 }, + { 0x0da5, 0x0e05 }, + { 0x0da6, 0x0e06 }, + { 0x0da7, 0x0e07 }, + { 0x0da8, 0x0e08 }, + { 0x0da9, 0x0e09 }, + { 0x0daa, 0x0e0a }, + { 0x0dab, 0x0e0b }, + { 0x0dac, 0x0e0c }, + { 0x0dad, 0x0e0d }, + { 0x0dae, 0x0e0e }, + { 0x0daf, 0x0e0f }, + { 0x0db0, 0x0e10 }, + { 0x0db1, 0x0e11 }, + { 0x0db2, 0x0e12 }, + { 0x0db3, 0x0e13 }, + { 0x0db4, 0x0e14 }, + { 0x0db5, 0x0e15 }, + { 0x0db6, 0x0e16 }, + { 0x0db7, 0x0e17 }, + { 0x0db8, 0x0e18 }, + { 0x0db9, 0x0e19 }, + { 0x0dba, 0x0e1a }, + { 0x0dbb, 0x0e1b }, + { 0x0dbc, 0x0e1c }, + { 0x0dbd, 0x0e1d }, + { 0x0dbe, 0x0e1e }, + { 0x0dbf, 0x0e1f }, + { 0x0dc0, 0x0e20 }, + { 0x0dc1, 0x0e21 }, + { 0x0dc2, 0x0e22 }, + { 0x0dc3, 0x0e23 }, + { 0x0dc4, 0x0e24 }, + { 0x0dc5, 0x0e25 }, + { 0x0dc6, 0x0e26 }, + { 0x0dc7, 0x0e27 }, + { 0x0dc8, 0x0e28 }, + { 0x0dc9, 0x0e29 }, + { 0x0dca, 0x0e2a }, + { 0x0dcb, 0x0e2b }, + { 0x0dcc, 0x0e2c }, + { 0x0dcd, 0x0e2d }, + { 0x0dce, 0x0e2e }, + { 0x0dcf, 0x0e2f }, + { 0x0dd0, 0x0e30 }, + { 0x0dd1, 0x0e31 }, + { 0x0dd2, 0x0e32 }, + { 0x0dd3, 0x0e33 }, + { 0x0dd4, 0x0e34 }, + { 0x0dd5, 0x0e35 }, + { 0x0dd6, 0x0e36 }, + { 0x0dd7, 0x0e37 }, + { 0x0dd8, 0x0e38 }, + { 0x0dd9, 0x0e39 }, + { 0x0dda, 0x0e3a }, + { 0x0ddf, 0x0e3f }, + { 0x0de0, 0x0e40 }, + { 0x0de1, 0x0e41 }, + { 0x0de2, 0x0e42 }, + { 0x0de3, 0x0e43 }, + { 0x0de4, 0x0e44 }, + { 0x0de5, 0x0e45 }, + { 0x0de6, 0x0e46 }, + { 0x0de7, 0x0e47 }, + { 0x0de8, 0x0e48 }, + { 0x0de9, 0x0e49 }, + { 0x0dea, 0x0e4a }, + { 0x0deb, 0x0e4b }, + { 0x0dec, 0x0e4c }, + { 0x0ded, 0x0e4d }, + { 0x0df0, 0x0e50 }, + { 0x0df1, 0x0e51 }, + { 0x0df2, 0x0e52 }, + { 0x0df3, 0x0e53 }, + { 0x0df4, 0x0e54 }, + { 0x0df5, 0x0e55 }, + { 0x0df6, 0x0e56 }, + { 0x0df7, 0x0e57 }, + { 0x0df8, 0x0e58 }, + { 0x0df9, 0x0e59 }, + { 0x0ea1, 0x3131 }, + { 0x0ea2, 0x3132 }, + { 0x0ea3, 0x3133 }, + { 0x0ea4, 0x3134 }, + { 0x0ea5, 0x3135 }, + { 0x0ea6, 0x3136 }, + { 0x0ea7, 0x3137 }, + { 0x0ea8, 0x3138 }, + { 0x0ea9, 0x3139 }, + { 0x0eaa, 0x313a }, + { 0x0eab, 0x313b }, + { 0x0eac, 0x313c }, + { 0x0ead, 0x313d }, + { 0x0eae, 0x313e }, + { 0x0eaf, 0x313f }, + { 0x0eb0, 0x3140 }, + { 0x0eb1, 0x3141 }, + { 0x0eb2, 0x3142 }, + { 0x0eb3, 0x3143 }, + { 0x0eb4, 0x3144 }, + { 0x0eb5, 0x3145 }, + { 0x0eb6, 0x3146 }, + { 0x0eb7, 0x3147 }, + { 0x0eb8, 0x3148 }, + { 0x0eb9, 0x3149 }, + { 0x0eba, 0x314a }, + { 0x0ebb, 0x314b }, + { 0x0ebc, 0x314c }, + { 0x0ebd, 0x314d }, + { 0x0ebe, 0x314e }, + { 0x0ebf, 0x314f }, + { 0x0ec0, 0x3150 }, + { 0x0ec1, 0x3151 }, + { 0x0ec2, 0x3152 }, + { 0x0ec3, 0x3153 }, + { 0x0ec4, 0x3154 }, + { 0x0ec5, 0x3155 }, + { 0x0ec6, 0x3156 }, + { 0x0ec7, 0x3157 }, + { 0x0ec8, 0x3158 }, + { 0x0ec9, 0x3159 }, + { 0x0eca, 0x315a }, + { 0x0ecb, 0x315b }, + { 0x0ecc, 0x315c }, + { 0x0ecd, 0x315d }, + { 0x0ece, 0x315e }, + { 0x0ecf, 0x315f }, + { 0x0ed0, 0x3160 }, + { 0x0ed1, 0x3161 }, + { 0x0ed2, 0x3162 }, + { 0x0ed3, 0x3163 }, + { 0x0ed4, 0x11a8 }, + { 0x0ed5, 0x11a9 }, + { 0x0ed6, 0x11aa }, + { 0x0ed7, 0x11ab }, + { 0x0ed8, 0x11ac }, + { 0x0ed9, 0x11ad }, + { 0x0eda, 0x11ae }, + { 0x0edb, 0x11af }, + { 0x0edc, 0x11b0 }, + { 0x0edd, 0x11b1 }, + { 0x0ede, 0x11b2 }, + { 0x0edf, 0x11b3 }, + { 0x0ee0, 0x11b4 }, + { 0x0ee1, 0x11b5 }, + { 0x0ee2, 0x11b6 }, + { 0x0ee3, 0x11b7 }, + { 0x0ee4, 0x11b8 }, + { 0x0ee5, 0x11b9 }, + { 0x0ee6, 0x11ba }, + { 0x0ee7, 0x11bb }, + { 0x0ee8, 0x11bc }, + { 0x0ee9, 0x11bd }, + { 0x0eea, 0x11be }, + { 0x0eeb, 0x11bf }, + { 0x0eec, 0x11c0 }, + { 0x0eed, 0x11c1 }, + { 0x0eee, 0x11c2 }, + { 0x0eef, 0x316d }, + { 0x0ef0, 0x3171 }, + { 0x0ef1, 0x3178 }, + { 0x0ef2, 0x317f }, + { 0x0ef3, 0x3181 }, + { 0x0ef4, 0x3184 }, + { 0x0ef5, 0x3186 }, + { 0x0ef6, 0x318d }, + { 0x0ef7, 0x318e }, + { 0x0ef8, 0x11eb }, + { 0x0ef9, 0x11f0 }, + { 0x0efa, 0x11f9 }, + { 0x0eff, 0x20a9 }, + { 0x13a4, 0x20ac }, + { 0x13bc, 0x0152 }, + { 0x13bd, 0x0153 }, + { 0x13be, 0x0178 }, + { 0x20ac, 0x20ac }, + // Numeric keypad with numlock on + { 0xff80 /*XKB_KEY_KP_Space*/, ' ' }, + { 0xffbd /*XKB_KEY_KP_Equal*/, '=' }, + { 0xffaa /*XKB_KEY_KP_Multiply*/, '*' }, + { 0xffab /*XKB_KEY_KP_Add*/, '+' }, + { 0xffac /*XKB_KEY_KP_Separator*/, ',' }, + { 0xffad /*XKB_KEY_KP_Subtract*/, '-' }, + { 0xffae /*XKB_KEY_KP_Decimal*/, '.' }, + { 0xffaf /*XKB_KEY_KP_Divide*/, '/' }, + { 0xffb0 /*XKB_KEY_KP_0*/, 0x0030 }, + { 0xffb1 /*XKB_KEY_KP_1*/, 0x0031 }, + { 0xffb2 /*XKB_KEY_KP_2*/, 0x0032 }, + { 0xffb3 /*XKB_KEY_KP_3*/, 0x0033 }, + { 0xffb4 /*XKB_KEY_KP_4*/, 0x0034 }, + { 0xffb5 /*XKB_KEY_KP_5*/, 0x0035 }, + { 0xffb6 /*XKB_KEY_KP_6*/, 0x0036 }, + { 0xffb7 /*XKB_KEY_KP_7*/, 0x0037 }, + { 0xffb8 /*XKB_KEY_KP_8*/, 0x0038 }, + { 0xffb9 /*XKB_KEY_KP_9*/, 0x0039 } +}; + + +////////////////////////////////////////////////////////////////////////// +////// GLFW internal API ////// +////////////////////////////////////////////////////////////////////////// + +// Convert XKB KeySym to Unicode +// +long _glfwKeySym2Unicode(unsigned int keysym) +{ + int min = 0; + int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; + int mid; + + // First check for Latin-1 characters (1:1 mapping) + if ((keysym >= 0x0020 && keysym <= 0x007e) || + (keysym >= 0x00a0 && keysym <= 0x00ff)) + { + return keysym; + } + + // Also check for directly encoded 24-bit UCS characters + if ((keysym & 0xff000000) == 0x01000000) + return keysym & 0x00ffffff; + + // Binary search in table + while (max >= min) + { + mid = (min + max) / 2; + if (keysymtab[mid].keysym < keysym) + min = mid + 1; + else if (keysymtab[mid].keysym > keysym) + max = mid - 1; + else + return keysymtab[mid].ucs; + } + + // No matching Unicode value found + return -1; +} + diff --git a/apps/exampleViewer/common/glfw/src/xkb_unicode.h b/apps/exampleViewer/common/glfw/src/xkb_unicode.h new file mode 100644 index 0000000000..164a6fa8a1 --- /dev/null +++ b/apps/exampleViewer/common/glfw/src/xkb_unicode.h @@ -0,0 +1,33 @@ +//======================================================================== +// GLFW 3.3 Linux - www.glfw.org +//------------------------------------------------------------------------ +// Copyright (c) 2014 Jonas Ådahl +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#ifndef _glfw3_xkb_unicode_h_ +#define _glfw3_xkb_unicode_h_ + + +long _glfwKeySym2Unicode(unsigned int keysym); + +#endif // _glfw3_xkb_unicode_h_ diff --git a/apps/exampleViewer/common/glfw/tests/CMakeLists.txt b/apps/exampleViewer/common/glfw/tests/CMakeLists.txt new file mode 100644 index 0000000000..4a97b03822 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/CMakeLists.txt @@ -0,0 +1,83 @@ + +link_libraries(glfw) + +if (BUILD_SHARED_LIBS) + link_libraries("${MATH_LIBRARY}") +endif() + +if (MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +include_directories("${GLFW_SOURCE_DIR}/deps") + +set(GLAD "${GLFW_SOURCE_DIR}/deps/glad/glad.h" + "${GLFW_SOURCE_DIR}/deps/glad.c") +set(GETOPT "${GLFW_SOURCE_DIR}/deps/getopt.h" + "${GLFW_SOURCE_DIR}/deps/getopt.c") +set(TINYCTHREAD "${GLFW_SOURCE_DIR}/deps/tinycthread.h" + "${GLFW_SOURCE_DIR}/deps/tinycthread.c") + +add_executable(clipboard clipboard.c ${GETOPT} ${GLAD}) +add_executable(events events.c ${GETOPT} ${GLAD}) +add_executable(msaa msaa.c ${GETOPT} ${GLAD}) +add_executable(glfwinfo glfwinfo.c ${GETOPT} ${GLAD}) +add_executable(iconify iconify.c ${GETOPT} ${GLAD}) +add_executable(monitors monitors.c ${GETOPT} ${GLAD}) +add_executable(reopen reopen.c ${GLAD}) +add_executable(cursor cursor.c ${GLAD}) + +add_executable(empty WIN32 MACOSX_BUNDLE empty.c ${TINYCTHREAD} ${GLAD}) +add_executable(gamma WIN32 MACOSX_BUNDLE gamma.c ${GLAD}) +add_executable(icon WIN32 MACOSX_BUNDLE icon.c ${GLAD}) +add_executable(joysticks WIN32 MACOSX_BUNDLE joysticks.c ${GLAD}) +add_executable(sharing WIN32 MACOSX_BUNDLE sharing.c ${GETOPT} ${GLAD}) +add_executable(tearing WIN32 MACOSX_BUNDLE tearing.c ${GETOPT} ${GLAD}) +add_executable(threads WIN32 MACOSX_BUNDLE threads.c ${TINYCTHREAD} ${GLAD}) +add_executable(timeout WIN32 MACOSX_BUNDLE timeout.c ${GLAD}) +add_executable(title WIN32 MACOSX_BUNDLE title.c ${GLAD}) +add_executable(windows WIN32 MACOSX_BUNDLE windows.c ${GETOPT} ${GLAD}) + +target_link_libraries(empty "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") +target_link_libraries(threads "${CMAKE_THREAD_LIBS_INIT}" "${RT_LIBRARY}") + +set(WINDOWS_BINARIES empty gamma icon joysticks sharing tearing threads timeout + title windows) +set(CONSOLE_BINARIES clipboard events msaa glfwinfo iconify monitors reopen + cursor) + +if (VULKAN_FOUND) + add_executable(vulkan WIN32 vulkan.c ${ICON}) + target_include_directories(vulkan PRIVATE "${VULKAN_INCLUDE_DIR}") + if (NOT GLFW_VULKAN_STATIC) + target_link_libraries(vulkan "${VULKAN_LIBRARY}") + endif() + list(APPEND WINDOWS_BINARIES vulkan) +endif() + +set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + FOLDER "GLFW3/Tests") + +if (MSVC) + # Tell MSVC to use main instead of WinMain for Windows subsystem executables + set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + LINK_FLAGS "/ENTRY:mainCRTStartup") +endif() + +if (APPLE) + set_target_properties(empty PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Empty Event") + set_target_properties(gamma PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Gamma") + set_target_properties(joysticks PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Joysticks") + set_target_properties(sharing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Sharing") + set_target_properties(tearing PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Tearing") + set_target_properties(threads PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Threads") + set_target_properties(timeout PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Timeout") + set_target_properties(title PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Title") + set_target_properties(windows PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Windows") + + set_target_properties(${WINDOWS_BINARIES} ${CONSOLE_BINARIES} PROPERTIES + MACOSX_BUNDLE_SHORT_VERSION_STRING ${GLFW_VERSION} + MACOSX_BUNDLE_LONG_VERSION_STRING ${GLFW_VERSION_FULL} + MACOSX_BUNDLE_INFO_PLIST "${GLFW_SOURCE_DIR}/CMake/MacOSXBundleInfo.plist.in") +endif() + diff --git a/apps/exampleViewer/common/glfw/tests/clipboard.c b/apps/exampleViewer/common/glfw/tests/clipboard.c new file mode 100644 index 0000000000..4e1ac0315e --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/clipboard.c @@ -0,0 +1,150 @@ +//======================================================================== +// Clipboard test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the clipboard functionality. +// +//======================================================================== + +#include +#include + +#include +#include + +#include "getopt.h" + +#if defined(__APPLE__) + #define MODIFIER GLFW_MOD_SUPER +#else + #define MODIFIER GLFW_MOD_CONTROL +#endif + +static void usage(void) +{ + printf("Usage: clipboard [-h]\n"); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + + case GLFW_KEY_V: + if (mods == MODIFIER) + { + const char* string; + + string = glfwGetClipboardString(window); + if (string) + printf("Clipboard contains \"%s\"\n", string); + else + printf("Clipboard does not contain a string\n"); + } + break; + + case GLFW_KEY_C: + if (mods == MODIFIER) + { + const char* string = "Hello GLFW World!"; + glfwSetClipboardString(window, string); + printf("Setting clipboard to \"%s\"\n", string); + } + break; + } +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(int argc, char** argv) +{ + int ch; + GLFWwindow* window; + + while ((ch = getopt(argc, argv, "h")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + + default: + usage(); + exit(EXIT_FAILURE); + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + window = glfwCreateWindow(200, 200, "Clipboard Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glfwSetKeyCallback(window, key_callback); + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + glClearColor(0.5f, 0.5f, 0.5f, 0); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + + glfwSwapBuffers(window); + glfwWaitEvents(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/cursor.c b/apps/exampleViewer/common/glfw/tests/cursor.c new file mode 100644 index 0000000000..c3f515b6ff --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/cursor.c @@ -0,0 +1,371 @@ +//======================================================================== +// Cursor & input mode tests +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test provides an interface to the cursor image and cursor mode +// parts of the API. +// +// Custom cursor image generation by urraka. +// +//======================================================================== + +#include +#include + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include +#include +#include + +#include "linmath.h" + +#define CURSOR_FRAME_COUNT 60 + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec2 vPos;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +"}\n"; + +static const char* fragment_shader_text = +"void main()\n" +"{\n" +" gl_FragColor = vec4(1.0);\n" +"}\n"; + +static double cursor_x; +static double cursor_y; +static int swap_interval = 1; +static int wait_events = GLFW_TRUE; +static int animate_cursor = GLFW_FALSE; +static int track_cursor = GLFW_FALSE; +static GLFWcursor* standard_cursors[6]; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static float star(int x, int y, float t) +{ + const float c = 64 / 2.f; + + const float i = (0.25f * (float) sin(2.f * M_PI * t) + 0.75f); + const float k = 64 * 0.046875f * i; + + const float dist = (float) sqrt((x - c) * (x - c) + (y - c) * (y - c)); + + const float salpha = 1.f - dist / c; + const float xalpha = (float) x == c ? c : k / (float) fabs(x - c); + const float yalpha = (float) y == c ? c : k / (float) fabs(y - c); + + return (float) fmax(0.f, fmin(1.f, i * salpha * 0.2f + salpha * xalpha * yalpha)); +} + +static GLFWcursor* create_cursor_frame(float t) +{ + int i = 0, x, y; + unsigned char buffer[64 * 64 * 4]; + const GLFWimage image = { 64, 64, buffer }; + + for (y = 0; y < image.width; y++) + { + for (x = 0; x < image.height; x++) + { + buffer[i++] = 255; + buffer[i++] = 255; + buffer[i++] = 255; + buffer[i++] = (unsigned char) (255 * star(x, y, t)); + } + } + + return glfwCreateCursor(&image, image.width / 2, image.height / 2); +} + +static void cursor_position_callback(GLFWwindow* window, double x, double y) +{ + printf("%0.3f: Cursor position: %f %f (%+f %+f)\n", + glfwGetTime(), + x, y, x - cursor_x, y - cursor_y); + + cursor_x = x; + cursor_y = y; +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_A: + { + animate_cursor = !animate_cursor; + if (!animate_cursor) + glfwSetCursor(window, NULL); + + break; + } + + case GLFW_KEY_ESCAPE: + { + if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) + { + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + } + + /* FALLTHROUGH */ + } + + case GLFW_KEY_N: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + printf("(( cursor is normal ))\n"); + break; + + case GLFW_KEY_D: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + printf("(( cursor is disabled ))\n"); + break; + + case GLFW_KEY_H: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + printf("(( cursor is hidden ))\n"); + break; + + case GLFW_KEY_SPACE: + swap_interval = 1 - swap_interval; + printf("(( swap interval: %i ))\n", swap_interval); + glfwSwapInterval(swap_interval); + break; + + case GLFW_KEY_W: + wait_events = !wait_events; + printf("(( %sing for events ))\n", wait_events ? "wait" : "poll"); + break; + + case GLFW_KEY_T: + track_cursor = !track_cursor; + break; + + case GLFW_KEY_0: + glfwSetCursor(window, NULL); + break; + + case GLFW_KEY_1: + glfwSetCursor(window, standard_cursors[0]); + break; + + case GLFW_KEY_2: + glfwSetCursor(window, standard_cursors[1]); + break; + + case GLFW_KEY_3: + glfwSetCursor(window, standard_cursors[2]); + break; + + case GLFW_KEY_4: + glfwSetCursor(window, standard_cursors[3]); + break; + + case GLFW_KEY_5: + glfwSetCursor(window, standard_cursors[4]); + break; + + case GLFW_KEY_6: + glfwSetCursor(window, standard_cursors[5]); + break; + } +} + +int main(void) +{ + int i; + GLFWwindow* window; + GLFWcursor* star_cursors[CURSOR_FRAME_COUNT]; + GLFWcursor* current_frame = NULL; + GLuint vertex_buffer, vertex_shader, fragment_shader, program; + GLint mvp_location, vpos_location; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + for (i = 0; i < CURSOR_FRAME_COUNT; i++) + { + star_cursors[i] = create_cursor_frame(i / (float) CURSOR_FRAME_COUNT); + if (!star_cursors[i]) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++) + { + const int shapes[] = { + GLFW_ARROW_CURSOR, + GLFW_IBEAM_CURSOR, + GLFW_CROSSHAIR_CURSOR, + GLFW_HAND_CURSOR, + GLFW_HRESIZE_CURSOR, + GLFW_VRESIZE_CURSOR + }; + + standard_cursors[i] = glfwCreateStandardCursor(shapes[i]); + if (!standard_cursors[i]) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(640, 480, "Cursor Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vec2), (void*) 0); + glUseProgram(program); + + glfwGetCursorPos(window, &cursor_x, &cursor_y); + printf("Cursor position: %f %f\n", cursor_x, cursor_y); + + glfwSetCursorPosCallback(window, cursor_position_callback); + glfwSetKeyCallback(window, key_callback); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + + if (track_cursor) + { + int wnd_width, wnd_height, fb_width, fb_height; + float scale; + vec2 vertices[4]; + mat4x4 mvp; + + glfwGetWindowSize(window, &wnd_width, &wnd_height); + glfwGetFramebufferSize(window, &fb_width, &fb_height); + + glViewport(0, 0, fb_width, fb_height); + + scale = (float) fb_width / (float) wnd_width; + vertices[0][0] = 0.f; + vertices[0][1] = (float) (fb_height - cursor_y * scale); + vertices[1][0] = (float) fb_width; + vertices[1][1] = (float) (fb_height - cursor_y * scale); + vertices[2][0] = (float) (cursor_x * scale); + vertices[2][1] = 0.f; + vertices[3][0] = (float) (cursor_x * scale); + vertices[3][1] = (float) fb_height; + + glBufferData(GL_ARRAY_BUFFER, + sizeof(vertices), + vertices, + GL_STREAM_DRAW); + + mat4x4_ortho(mvp, 0.f, (float) fb_width, 0.f, (float) fb_height, 0.f, 1.f); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + + glDrawArrays(GL_LINES, 0, 4); + } + + glfwSwapBuffers(window); + + if (animate_cursor) + { + const int i = (int) (glfwGetTime() * 30.0) % CURSOR_FRAME_COUNT; + if (current_frame != star_cursors[i]) + { + glfwSetCursor(window, star_cursors[i]); + current_frame = star_cursors[i]; + } + } + else + current_frame = NULL; + + if (wait_events) + { + if (animate_cursor) + glfwWaitEventsTimeout(1.0 / 30.0); + else + glfwWaitEvents(); + } + else + glfwPollEvents(); + + // Workaround for an issue with msvcrt and mintty + fflush(stdout); + } + + glfwDestroyWindow(window); + + for (i = 0; i < CURSOR_FRAME_COUNT; i++) + glfwDestroyCursor(star_cursors[i]); + + for (i = 0; i < sizeof(standard_cursors) / sizeof(standard_cursors[0]); i++) + glfwDestroyCursor(standard_cursors[i]); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/empty.c b/apps/exampleViewer/common/glfw/tests/empty.c new file mode 100644 index 0000000000..f4a51b70bb --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/empty.c @@ -0,0 +1,131 @@ +//======================================================================== +// Empty event test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test is intended to verify that posting of empty events works +// +//======================================================================== + +#include "tinycthread.h" + +#include +#include + +#include +#include +#include + +static volatile int running = GLFW_TRUE; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static int thread_main(void* data) +{ + struct timespec time; + + while (running) + { + clock_gettime(CLOCK_REALTIME, &time); + time.tv_sec += 1; + thrd_sleep(&time, NULL); + + glfwPostEmptyEvent(); + } + + return 0; +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static float nrand(void) +{ + return (float) rand() / (float) RAND_MAX; +} + +int main(void) +{ + int result; + thrd_t thread; + GLFWwindow* window; + + srand((unsigned int) time(NULL)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = glfwCreateWindow(640, 480, "Empty Event Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSetKeyCallback(window, key_callback); + + if (thrd_create(&thread, thread_main, NULL) != thrd_success) + { + fprintf(stderr, "Failed to create secondary thread\n"); + + glfwTerminate(); + exit(EXIT_FAILURE); + } + + while (running) + { + int width, height; + float r = nrand(), g = nrand(), b = nrand(); + float l = (float) sqrt(r * r + g * g + b * b); + + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearColor(r / l, g / l, b / l, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + + glfwWaitEvents(); + + if (glfwWindowShouldClose(window)) + running = GLFW_FALSE; + } + + glfwHideWindow(window); + thrd_join(thread, &result); + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/events.c b/apps/exampleViewer/common/glfw/tests/events.c new file mode 100644 index 0000000000..fa14ae11b0 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/events.c @@ -0,0 +1,629 @@ +//======================================================================== +// Event linter (event spewer) +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test hooks every available callback and outputs their arguments +// +// Log messages go to stdout, error messages to stderr +// +// Every event also gets a (sequential) number to aid discussion of logs +// +//======================================================================== + +#include +#include + +#include +#include +#include +#include +#include + +#include "getopt.h" + +// Event index +static unsigned int counter = 0; + +typedef struct +{ + GLFWwindow* window; + int number; + int closeable; +} Slot; + +static void usage(void) +{ + printf("Usage: events [-f] [-h] [-n WINDOWS]\n"); + printf("Options:\n"); + printf(" -f use full screen\n"); + printf(" -h show this help\n"); + printf(" -n the number of windows to create\n"); +} + +static const char* get_key_name(int key) +{ + switch (key) + { + // Printable keys + case GLFW_KEY_A: return "A"; + case GLFW_KEY_B: return "B"; + case GLFW_KEY_C: return "C"; + case GLFW_KEY_D: return "D"; + case GLFW_KEY_E: return "E"; + case GLFW_KEY_F: return "F"; + case GLFW_KEY_G: return "G"; + case GLFW_KEY_H: return "H"; + case GLFW_KEY_I: return "I"; + case GLFW_KEY_J: return "J"; + case GLFW_KEY_K: return "K"; + case GLFW_KEY_L: return "L"; + case GLFW_KEY_M: return "M"; + case GLFW_KEY_N: return "N"; + case GLFW_KEY_O: return "O"; + case GLFW_KEY_P: return "P"; + case GLFW_KEY_Q: return "Q"; + case GLFW_KEY_R: return "R"; + case GLFW_KEY_S: return "S"; + case GLFW_KEY_T: return "T"; + case GLFW_KEY_U: return "U"; + case GLFW_KEY_V: return "V"; + case GLFW_KEY_W: return "W"; + case GLFW_KEY_X: return "X"; + case GLFW_KEY_Y: return "Y"; + case GLFW_KEY_Z: return "Z"; + case GLFW_KEY_1: return "1"; + case GLFW_KEY_2: return "2"; + case GLFW_KEY_3: return "3"; + case GLFW_KEY_4: return "4"; + case GLFW_KEY_5: return "5"; + case GLFW_KEY_6: return "6"; + case GLFW_KEY_7: return "7"; + case GLFW_KEY_8: return "8"; + case GLFW_KEY_9: return "9"; + case GLFW_KEY_0: return "0"; + case GLFW_KEY_SPACE: return "SPACE"; + case GLFW_KEY_MINUS: return "MINUS"; + case GLFW_KEY_EQUAL: return "EQUAL"; + case GLFW_KEY_LEFT_BRACKET: return "LEFT BRACKET"; + case GLFW_KEY_RIGHT_BRACKET: return "RIGHT BRACKET"; + case GLFW_KEY_BACKSLASH: return "BACKSLASH"; + case GLFW_KEY_SEMICOLON: return "SEMICOLON"; + case GLFW_KEY_APOSTROPHE: return "APOSTROPHE"; + case GLFW_KEY_GRAVE_ACCENT: return "GRAVE ACCENT"; + case GLFW_KEY_COMMA: return "COMMA"; + case GLFW_KEY_PERIOD: return "PERIOD"; + case GLFW_KEY_SLASH: return "SLASH"; + case GLFW_KEY_WORLD_1: return "WORLD 1"; + case GLFW_KEY_WORLD_2: return "WORLD 2"; + + // Function keys + case GLFW_KEY_ESCAPE: return "ESCAPE"; + case GLFW_KEY_F1: return "F1"; + case GLFW_KEY_F2: return "F2"; + case GLFW_KEY_F3: return "F3"; + case GLFW_KEY_F4: return "F4"; + case GLFW_KEY_F5: return "F5"; + case GLFW_KEY_F6: return "F6"; + case GLFW_KEY_F7: return "F7"; + case GLFW_KEY_F8: return "F8"; + case GLFW_KEY_F9: return "F9"; + case GLFW_KEY_F10: return "F10"; + case GLFW_KEY_F11: return "F11"; + case GLFW_KEY_F12: return "F12"; + case GLFW_KEY_F13: return "F13"; + case GLFW_KEY_F14: return "F14"; + case GLFW_KEY_F15: return "F15"; + case GLFW_KEY_F16: return "F16"; + case GLFW_KEY_F17: return "F17"; + case GLFW_KEY_F18: return "F18"; + case GLFW_KEY_F19: return "F19"; + case GLFW_KEY_F20: return "F20"; + case GLFW_KEY_F21: return "F21"; + case GLFW_KEY_F22: return "F22"; + case GLFW_KEY_F23: return "F23"; + case GLFW_KEY_F24: return "F24"; + case GLFW_KEY_F25: return "F25"; + case GLFW_KEY_UP: return "UP"; + case GLFW_KEY_DOWN: return "DOWN"; + case GLFW_KEY_LEFT: return "LEFT"; + case GLFW_KEY_RIGHT: return "RIGHT"; + case GLFW_KEY_LEFT_SHIFT: return "LEFT SHIFT"; + case GLFW_KEY_RIGHT_SHIFT: return "RIGHT SHIFT"; + case GLFW_KEY_LEFT_CONTROL: return "LEFT CONTROL"; + case GLFW_KEY_RIGHT_CONTROL: return "RIGHT CONTROL"; + case GLFW_KEY_LEFT_ALT: return "LEFT ALT"; + case GLFW_KEY_RIGHT_ALT: return "RIGHT ALT"; + case GLFW_KEY_TAB: return "TAB"; + case GLFW_KEY_ENTER: return "ENTER"; + case GLFW_KEY_BACKSPACE: return "BACKSPACE"; + case GLFW_KEY_INSERT: return "INSERT"; + case GLFW_KEY_DELETE: return "DELETE"; + case GLFW_KEY_PAGE_UP: return "PAGE UP"; + case GLFW_KEY_PAGE_DOWN: return "PAGE DOWN"; + case GLFW_KEY_HOME: return "HOME"; + case GLFW_KEY_END: return "END"; + case GLFW_KEY_KP_0: return "KEYPAD 0"; + case GLFW_KEY_KP_1: return "KEYPAD 1"; + case GLFW_KEY_KP_2: return "KEYPAD 2"; + case GLFW_KEY_KP_3: return "KEYPAD 3"; + case GLFW_KEY_KP_4: return "KEYPAD 4"; + case GLFW_KEY_KP_5: return "KEYPAD 5"; + case GLFW_KEY_KP_6: return "KEYPAD 6"; + case GLFW_KEY_KP_7: return "KEYPAD 7"; + case GLFW_KEY_KP_8: return "KEYPAD 8"; + case GLFW_KEY_KP_9: return "KEYPAD 9"; + case GLFW_KEY_KP_DIVIDE: return "KEYPAD DIVIDE"; + case GLFW_KEY_KP_MULTIPLY: return "KEYPAD MULTPLY"; + case GLFW_KEY_KP_SUBTRACT: return "KEYPAD SUBTRACT"; + case GLFW_KEY_KP_ADD: return "KEYPAD ADD"; + case GLFW_KEY_KP_DECIMAL: return "KEYPAD DECIMAL"; + case GLFW_KEY_KP_EQUAL: return "KEYPAD EQUAL"; + case GLFW_KEY_KP_ENTER: return "KEYPAD ENTER"; + case GLFW_KEY_PRINT_SCREEN: return "PRINT SCREEN"; + case GLFW_KEY_NUM_LOCK: return "NUM LOCK"; + case GLFW_KEY_CAPS_LOCK: return "CAPS LOCK"; + case GLFW_KEY_SCROLL_LOCK: return "SCROLL LOCK"; + case GLFW_KEY_PAUSE: return "PAUSE"; + case GLFW_KEY_LEFT_SUPER: return "LEFT SUPER"; + case GLFW_KEY_RIGHT_SUPER: return "RIGHT SUPER"; + case GLFW_KEY_MENU: return "MENU"; + + default: return "UNKNOWN"; + } +} + +static const char* get_action_name(int action) +{ + switch (action) + { + case GLFW_PRESS: + return "pressed"; + case GLFW_RELEASE: + return "released"; + case GLFW_REPEAT: + return "repeated"; + } + + return "caused unknown action"; +} + +static const char* get_button_name(int button) +{ + switch (button) + { + case GLFW_MOUSE_BUTTON_LEFT: + return "left"; + case GLFW_MOUSE_BUTTON_RIGHT: + return "right"; + case GLFW_MOUSE_BUTTON_MIDDLE: + return "middle"; + default: + { + static char name[16]; + snprintf(name, sizeof(name), "%i", button); + return name; + } + } +} + +static const char* get_mods_name(int mods) +{ + static char name[512]; + + if (mods == 0) + return " no mods"; + + name[0] = '\0'; + + if (mods & GLFW_MOD_SHIFT) + strcat(name, " shift"); + if (mods & GLFW_MOD_CONTROL) + strcat(name, " control"); + if (mods & GLFW_MOD_ALT) + strcat(name, " alt"); + if (mods & GLFW_MOD_SUPER) + strcat(name, " super"); + + return name; +} + +static const char* get_character_string(int codepoint) +{ + // This assumes UTF-8, which is stupid + static char result[6 + 1]; + + int length = wctomb(result, codepoint); + if (length == -1) + length = 0; + + result[length] = '\0'; + return result; +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void window_pos_callback(GLFWwindow* window, int x, int y) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window position: %i %i\n", + counter++, slot->number, glfwGetTime(), x, y); +} + +static void window_size_callback(GLFWwindow* window, int width, int height) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window size: %i %i\n", + counter++, slot->number, glfwGetTime(), width, height); +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Framebuffer size: %i %i\n", + counter++, slot->number, glfwGetTime(), width, height); + + glViewport(0, 0, width, height); +} + +static void window_close_callback(GLFWwindow* window) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window close\n", + counter++, slot->number, glfwGetTime()); + + glfwSetWindowShouldClose(window, slot->closeable); +} + +static void window_refresh_callback(GLFWwindow* window) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window refresh\n", + counter++, slot->number, glfwGetTime()); + + glfwMakeContextCurrent(window); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); +} + +static void window_focus_callback(GLFWwindow* window, int focused) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window %s\n", + counter++, slot->number, glfwGetTime(), + focused ? "focused" : "defocused"); +} + +static void window_iconify_callback(GLFWwindow* window, int iconified) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window was %s\n", + counter++, slot->number, glfwGetTime(), + iconified ? "iconified" : "uniconified"); +} + +static void window_maximize_callback(GLFWwindow* window, int maximized) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Window was %s\n", + counter++, slot->number, glfwGetTime(), + maximized ? "maximized" : "unmaximized"); +} + +static void mouse_button_callback(GLFWwindow* window, int button, int action, int mods) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Mouse button %i (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), button, + get_button_name(button), + get_mods_name(mods), + get_action_name(action)); +} + +static void cursor_position_callback(GLFWwindow* window, double x, double y) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Cursor position: %f %f\n", + counter++, slot->number, glfwGetTime(), x, y); +} + +static void cursor_enter_callback(GLFWwindow* window, int entered) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Cursor %s window\n", + counter++, slot->number, glfwGetTime(), + entered ? "entered" : "left"); +} + +static void scroll_callback(GLFWwindow* window, double x, double y) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Scroll: %0.3f %0.3f\n", + counter++, slot->number, glfwGetTime(), x, y); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + Slot* slot = glfwGetWindowUserPointer(window); + const char* name = glfwGetKeyName(key, scancode); + + if (name) + { + printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), key, scancode, + get_key_name(key), + name, + get_mods_name(mods), + get_action_name(action)); + } + else + { + printf("%08x to %i at %0.3f: Key 0x%04x Scancode 0x%04x (%s) (with%s) was %s\n", + counter++, slot->number, glfwGetTime(), key, scancode, + get_key_name(key), + get_mods_name(mods), + get_action_name(action)); + } + + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_C: + { + slot->closeable = !slot->closeable; + + printf("(( closing %s ))\n", slot->closeable ? "enabled" : "disabled"); + break; + } + } +} + +static void char_callback(GLFWwindow* window, unsigned int codepoint) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Character 0x%08x (%s) input\n", + counter++, slot->number, glfwGetTime(), codepoint, + get_character_string(codepoint)); +} + +static void char_mods_callback(GLFWwindow* window, unsigned int codepoint, int mods) +{ + Slot* slot = glfwGetWindowUserPointer(window); + printf("%08x to %i at %0.3f: Character 0x%08x (%s) with modifiers (with%s) input\n", + counter++, slot->number, glfwGetTime(), codepoint, + get_character_string(codepoint), + get_mods_name(mods)); +} + +static void drop_callback(GLFWwindow* window, int count, const char** paths) +{ + int i; + Slot* slot = glfwGetWindowUserPointer(window); + + printf("%08x to %i at %0.3f: Drop input\n", + counter++, slot->number, glfwGetTime()); + + for (i = 0; i < count; i++) + printf(" %i: \"%s\"\n", i, paths[i]); +} + +static void monitor_callback(GLFWmonitor* monitor, int event) +{ + if (event == GLFW_CONNECTED) + { + int x, y, widthMM, heightMM; + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + glfwGetMonitorPos(monitor, &x, &y); + glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); + + printf("%08x at %0.3f: Monitor %s (%ix%i at %ix%i, %ix%i mm) was connected\n", + counter++, + glfwGetTime(), + glfwGetMonitorName(monitor), + mode->width, mode->height, + x, y, + widthMM, heightMM); + } + else if (event == GLFW_DISCONNECTED) + { + printf("%08x at %0.3f: Monitor %s was disconnected\n", + counter++, + glfwGetTime(), + glfwGetMonitorName(monitor)); + } +} + +static void joystick_callback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + { + int axisCount, buttonCount; + + glfwGetJoystickAxes(jid, &axisCount); + glfwGetJoystickButtons(jid, &buttonCount); + + printf("%08x at %0.3f: Joystick %i (%s) was connected with %i axes and %i buttons\n", + counter++, glfwGetTime(), + jid, + glfwGetJoystickName(jid), + axisCount, + buttonCount); + } + else + { + printf("%08x at %0.3f: Joystick %i was disconnected\n", + counter++, glfwGetTime(), jid); + } +} + +int main(int argc, char** argv) +{ + Slot* slots; + GLFWmonitor* monitor = NULL; + int ch, i, width, height, count = 1; + + setlocale(LC_ALL, ""); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + printf("Library initialized\n"); + + glfwSetMonitorCallback(monitor_callback); + glfwSetJoystickCallback(joystick_callback); + + while ((ch = getopt(argc, argv, "hfn:")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + + case 'f': + monitor = glfwGetPrimaryMonitor(); + break; + + case 'n': + count = (int) strtol(optarg, NULL, 10); + break; + + default: + usage(); + exit(EXIT_FAILURE); + } + } + + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + + width = mode->width; + height = mode->height; + } + else + { + width = 640; + height = 480; + } + + if (!count) + { + fprintf(stderr, "Invalid user\n"); + exit(EXIT_FAILURE); + } + + slots = calloc(count, sizeof(Slot)); + + for (i = 0; i < count; i++) + { + char title[128]; + + slots[i].closeable = GLFW_TRUE; + slots[i].number = i + 1; + + snprintf(title, sizeof(title), "Event Linter (Window %i)", slots[i].number); + + if (monitor) + { + printf("Creating full screen window %i (%ix%i on %s)\n", + slots[i].number, + width, height, + glfwGetMonitorName(monitor)); + } + else + { + printf("Creating windowed mode window %i (%ix%i)\n", + slots[i].number, + width, height); + } + + slots[i].window = glfwCreateWindow(width, height, title, monitor, NULL); + if (!slots[i].window) + { + free(slots); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetWindowUserPointer(slots[i].window, slots + i); + + glfwSetWindowPosCallback(slots[i].window, window_pos_callback); + glfwSetWindowSizeCallback(slots[i].window, window_size_callback); + glfwSetFramebufferSizeCallback(slots[i].window, framebuffer_size_callback); + glfwSetWindowCloseCallback(slots[i].window, window_close_callback); + glfwSetWindowRefreshCallback(slots[i].window, window_refresh_callback); + glfwSetWindowFocusCallback(slots[i].window, window_focus_callback); + glfwSetWindowIconifyCallback(slots[i].window, window_iconify_callback); + glfwSetWindowMaximizeCallback(slots[i].window, window_maximize_callback); + glfwSetMouseButtonCallback(slots[i].window, mouse_button_callback); + glfwSetCursorPosCallback(slots[i].window, cursor_position_callback); + glfwSetCursorEnterCallback(slots[i].window, cursor_enter_callback); + glfwSetScrollCallback(slots[i].window, scroll_callback); + glfwSetKeyCallback(slots[i].window, key_callback); + glfwSetCharCallback(slots[i].window, char_callback); + glfwSetCharModsCallback(slots[i].window, char_mods_callback); + glfwSetDropCallback(slots[i].window, drop_callback); + + glfwMakeContextCurrent(slots[i].window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + } + + printf("Main loop starting\n"); + + for (;;) + { + for (i = 0; i < count; i++) + { + if (glfwWindowShouldClose(slots[i].window)) + break; + } + + if (i < count) + break; + + glfwWaitEvents(); + + // Workaround for an issue with msvcrt and mintty + fflush(stdout); + } + + free(slots); + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/gamma.c b/apps/exampleViewer/common/glfw/tests/gamma.c new file mode 100644 index 0000000000..79fba39569 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/gamma.c @@ -0,0 +1,156 @@ +//======================================================================== +// Gamma correction test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the gamma correction functionality for +// both full screen and windowed mode windows +// +//======================================================================== + +#include +#include + +#define NK_IMPLEMENTATION +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_STANDARD_VARARGS +#include + +#define NK_GLFW_GL2_IMPLEMENTATION +#include + +#include +#include + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static void chart_ramp_array(struct nk_context* nk, + struct nk_color color, + int count, unsigned short int* values) +{ + if (nk_chart_begin_colored(nk, NK_CHART_LINES, + color, nk_rgb(255, 255, 255), + count, 0, 65535)) + { + int i; + for (i = 0; i < count; i++) + { + char buffer[1024]; + if (nk_chart_push(nk, values[i])) + { + snprintf(buffer, sizeof(buffer), "#%u: %u (%0.5f) ", + i, values[i], values[i] / 65535.f); + nk_tooltip(nk, buffer); + } + } + + nk_chart_end(nk); + } +} + +int main(int argc, char** argv) +{ + GLFWmonitor* monitor = NULL; + GLFWwindow* window; + struct nk_context* nk; + struct nk_font_atlas* atlas; + float gamma_value = 1.f; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + monitor = glfwGetPrimaryMonitor(); + + window = glfwCreateWindow(800, 400, "Gamma Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS); + nk_glfw3_font_stash_begin(&atlas); + nk_glfw3_font_stash_end(); + + glfwSetKeyCallback(window, key_callback); + + while (!glfwWindowShouldClose(window)) + { + int width, height; + struct nk_panel layout; + struct nk_rect area; + + glfwGetWindowSize(window, &width, &height); + area = nk_rect(0.f, 0.f, (float) width, (float) height); + + glClear(GL_COLOR_BUFFER_BIT); + nk_glfw3_new_frame(); + if (nk_begin(nk, &layout, "", area, 0)) + { + const GLFWgammaramp* ramp = glfwGetGammaRamp(monitor); + nk_window_set_bounds(nk, area); + + nk_layout_row_dynamic(nk, 30, 2); + if (nk_slider_float(nk, 0.1f, &gamma_value, 5.f, 0.1f)) + glfwSetGamma(monitor, gamma_value); + nk_labelf(nk, NK_TEXT_LEFT, "%0.1f", gamma_value); + nk_layout_row_end(nk); + + nk_layout_row_dynamic(nk, height - 60.f, 3); + chart_ramp_array(nk, nk_rgb(255, 0, 0), ramp->size, ramp->red); + chart_ramp_array(nk, nk_rgb(0, 255, 0), ramp->size, ramp->green); + chart_ramp_array(nk, nk_rgb(0,0, 255), ramp->size, ramp->blue); + nk_layout_row_end(nk); + } + + nk_end(nk); + nk_glfw3_render(NK_ANTI_ALIASING_ON, 10000, 1000); + + glfwSwapBuffers(window); + glfwWaitEventsTimeout(1.0); + } + + nk_glfw3_shutdown(); + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/glfwinfo.c b/apps/exampleViewer/common/glfw/tests/glfwinfo.c new file mode 100644 index 0000000000..26d4b94556 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/glfwinfo.c @@ -0,0 +1,899 @@ +//======================================================================== +// Context creation and information tool +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== + +#define VK_NO_PROTOTYPES +#include +#include +#include + +#include +#include +#include + +#include "getopt.h" + +#ifdef _MSC_VER +#define strcasecmp(x, y) _stricmp(x, y) +#endif + +#define API_NAME_OPENGL "gl" +#define API_NAME_OPENGL_ES "es" + +#define API_NAME_NATIVE "native" +#define API_NAME_EGL "egl" + +#define PROFILE_NAME_CORE "core" +#define PROFILE_NAME_COMPAT "compat" + +#define STRATEGY_NAME_NONE "none" +#define STRATEGY_NAME_LOSE "lose" + +#define BEHAVIOR_NAME_NONE "none" +#define BEHAVIOR_NAME_FLUSH "flush" + +static void usage(void) +{ + printf("Usage: glfwinfo [OPTION]...\n"); + printf("Options:\n"); + printf(" -a, --client-api=API the client API to use (" + API_NAME_OPENGL " or " + API_NAME_OPENGL_ES ")\n"); + printf(" -b, --behavior=BEHAVIOR the release behavior to use (" + BEHAVIOR_NAME_NONE " or " + BEHAVIOR_NAME_FLUSH ")\n"); + printf(" -c, --context-api=API the context creation API to use (" + API_NAME_NATIVE " or " + API_NAME_EGL ")\n"); + printf(" -d, --debug request a debug context\n"); + printf(" -f, --forward require a forward-compatible context\n"); + printf(" -h, --help show this help\n"); + printf(" -l, --list-extensions list all Vulkan and client API extensions\n"); + printf(" --list-layers list all Vulkan layers\n"); + printf(" -m, --major=MAJOR the major number of the required " + "client API version\n"); + printf(" -n, --minor=MINOR the minor number of the required " + "client API version\n"); + printf(" -p, --profile=PROFILE the OpenGL profile to use (" + PROFILE_NAME_CORE " or " + PROFILE_NAME_COMPAT ")\n"); + printf(" -s, --robustness=STRATEGY the robustness strategy to use (" + STRATEGY_NAME_NONE " or " + STRATEGY_NAME_LOSE ")\n"); + printf(" -v, --version print version information\n"); + printf(" --red-bits=N the number of red bits to request\n"); + printf(" --green-bits=N the number of green bits to request\n"); + printf(" --blue-bits=N the number of blue bits to request\n"); + printf(" --alpha-bits=N the number of alpha bits to request\n"); + printf(" --depth-bits=N the number of depth bits to request\n"); + printf(" --stencil-bits=N the number of stencil bits to request\n"); + printf(" --accum-red-bits=N the number of red bits to request\n"); + printf(" --accum-green-bits=N the number of green bits to request\n"); + printf(" --accum-blue-bits=N the number of blue bits to request\n"); + printf(" --accum-alpha-bits=N the number of alpha bits to request\n"); + printf(" --aux-buffers=N the number of aux buffers to request\n"); + printf(" --samples=N the number of MSAA samples to request\n"); + printf(" --stereo request stereo rendering\n"); + printf(" --srgb request an sRGB capable framebuffer\n"); + printf(" --singlebuffer request single-buffering\n"); + printf(" --no-error request a context that does not emit errors\n"); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static const char* get_device_type_name(VkPhysicalDeviceType type) +{ + if (type == VK_PHYSICAL_DEVICE_TYPE_OTHER) + return "other"; + else if (type == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) + return "integrated GPU"; + else if (type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) + return "discrete GPU"; + else if (type == VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU) + return "virtual GPU"; + else if (type == VK_PHYSICAL_DEVICE_TYPE_CPU) + return "CPU"; + + return "unknown"; +} + +static const char* get_api_name(int api) +{ + if (api == GLFW_OPENGL_API) + return "OpenGL"; + else if (api == GLFW_OPENGL_ES_API) + return "OpenGL ES"; + + return "Unknown API"; +} + +static const char* get_profile_name_gl(GLint mask) +{ + if (mask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) + return PROFILE_NAME_COMPAT; + if (mask & GL_CONTEXT_CORE_PROFILE_BIT) + return PROFILE_NAME_CORE; + + return "unknown"; +} + +static const char* get_profile_name_glfw(int profile) +{ + if (profile == GLFW_OPENGL_COMPAT_PROFILE) + return PROFILE_NAME_COMPAT; + if (profile == GLFW_OPENGL_CORE_PROFILE) + return PROFILE_NAME_CORE; + + return "unknown"; +} + +static const char* get_strategy_name_gl(GLint strategy) +{ + if (strategy == GL_LOSE_CONTEXT_ON_RESET_ARB) + return STRATEGY_NAME_LOSE; + if (strategy == GL_NO_RESET_NOTIFICATION_ARB) + return STRATEGY_NAME_NONE; + + return "unknown"; +} + +static const char* get_strategy_name_glfw(int strategy) +{ + if (strategy == GLFW_LOSE_CONTEXT_ON_RESET) + return STRATEGY_NAME_LOSE; + if (strategy == GLFW_NO_RESET_NOTIFICATION) + return STRATEGY_NAME_NONE; + + return "unknown"; +} + +static void list_context_extensions(int client, int major, int minor) +{ + int i; + GLint count; + const GLubyte* extensions; + + printf("%s context extensions:\n", get_api_name(client)); + + if (client == GLFW_OPENGL_API && major > 2) + { + glGetIntegerv(GL_NUM_EXTENSIONS, &count); + + for (i = 0; i < count; i++) + printf(" %s\n", (const char*) glGetStringi(GL_EXTENSIONS, i)); + } + else + { + extensions = glGetString(GL_EXTENSIONS); + while (*extensions != '\0') + { + putchar(' '); + + while (*extensions != '\0' && *extensions != ' ') + { + putchar(*extensions); + extensions++; + } + + while (*extensions == ' ') + extensions++; + + putchar('\n'); + } + } +} + +static void list_vulkan_instance_extensions(void) +{ + uint32_t i, ep_count = 0; + VkExtensionProperties* ep; + PFN_vkEnumerateInstanceExtensionProperties vkEnumerateInstanceExtensionProperties = + (PFN_vkEnumerateInstanceExtensionProperties) + glfwGetInstanceProcAddress(NULL, "vkEnumerateInstanceExtensionProperties"); + + printf("Vulkan instance extensions:\n"); + + if (vkEnumerateInstanceExtensionProperties(NULL, &ep_count, NULL) != VK_SUCCESS) + return; + + ep = calloc(ep_count, sizeof(VkExtensionProperties)); + + if (vkEnumerateInstanceExtensionProperties(NULL, &ep_count, ep) != VK_SUCCESS) + { + free(ep); + return; + } + + for (i = 0; i < ep_count; i++) + printf(" %s (v%u)\n", ep[i].extensionName, ep[i].specVersion); + + free(ep); +} + +static void list_vulkan_instance_layers(void) +{ + uint32_t i, lp_count = 0; + VkLayerProperties* lp; + PFN_vkEnumerateInstanceLayerProperties vkEnumerateInstanceLayerProperties = + (PFN_vkEnumerateInstanceLayerProperties) + glfwGetInstanceProcAddress(NULL, "vkEnumerateInstanceLayerProperties"); + + printf("Vulkan instance layers:\n"); + + if (vkEnumerateInstanceLayerProperties(&lp_count, NULL) != VK_SUCCESS) + return; + + lp = calloc(lp_count, sizeof(VkLayerProperties)); + + if (vkEnumerateInstanceLayerProperties(&lp_count, lp) != VK_SUCCESS) + { + free(lp); + return; + } + + for (i = 0; i < lp_count; i++) + { + printf(" %s (v%u) \"%s\"\n", + lp[i].layerName, + lp[i].specVersion >> 22, + lp[i].description); + } + + free(lp); +} + +static void list_vulkan_device_extensions(VkInstance instance, VkPhysicalDevice device) +{ + uint32_t i, ep_count; + VkExtensionProperties* ep; + PFN_vkEnumerateDeviceExtensionProperties vkEnumerateDeviceExtensionProperties = + (PFN_vkEnumerateDeviceExtensionProperties) + glfwGetInstanceProcAddress(instance, "vkEnumerateDeviceExtensionProperties"); + + printf("Vulkan device extensions:\n"); + + if (vkEnumerateDeviceExtensionProperties(device, NULL, &ep_count, NULL) != VK_SUCCESS) + return; + + ep = calloc(ep_count, sizeof(VkExtensionProperties)); + + if (vkEnumerateDeviceExtensionProperties(device, NULL, &ep_count, ep) != VK_SUCCESS) + { + free(ep); + return; + } + + for (i = 0; i < ep_count; i++) + printf(" %s (v%u)\n", ep[i].extensionName, ep[i].specVersion); + + free(ep); +} + +static void list_vulkan_device_layers(VkInstance instance, VkPhysicalDevice device) +{ + uint32_t i, lp_count; + VkLayerProperties* lp; + PFN_vkEnumerateDeviceLayerProperties vkEnumerateDeviceLayerProperties = + (PFN_vkEnumerateDeviceLayerProperties) + glfwGetInstanceProcAddress(instance, "vkEnumerateDeviceLayerProperties"); + + printf("Vulkan device layers:\n"); + + if (vkEnumerateDeviceLayerProperties(device, &lp_count, NULL) != VK_SUCCESS) + return; + + lp = calloc(lp_count, sizeof(VkLayerProperties)); + + if (vkEnumerateDeviceLayerProperties(device, &lp_count, lp) != VK_SUCCESS) + { + free(lp); + return; + } + + for (i = 0; i < lp_count; i++) + { + printf(" %s (v%u) \"%s\"\n", + lp[i].layerName, + lp[i].specVersion >> 22, + lp[i].description); + } + + free(lp); +} + +static int valid_version(void) +{ + int major, minor, revision; + glfwGetVersion(&major, &minor, &revision); + + if (major != GLFW_VERSION_MAJOR) + { + printf("*** ERROR: GLFW major version mismatch! ***\n"); + return GLFW_FALSE; + } + + if (minor != GLFW_VERSION_MINOR || revision != GLFW_VERSION_REVISION) + printf("*** WARNING: GLFW version mismatch! ***\n"); + + return GLFW_TRUE; +} + +static void print_version(void) +{ + int major, minor, revision; + glfwGetVersion(&major, &minor, &revision); + + printf("GLFW header version: %u.%u.%u\n", + GLFW_VERSION_MAJOR, + GLFW_VERSION_MINOR, + GLFW_VERSION_REVISION); + printf("GLFW library version: %u.%u.%u\n", major, minor, revision); + printf("GLFW library version string: \"%s\"\n", glfwGetVersionString()); +} + +int main(int argc, char** argv) +{ + int ch, client, major, minor, revision, profile; + GLint redbits, greenbits, bluebits, alphabits, depthbits, stencilbits; + int list_extensions = GLFW_FALSE, list_layers = GLFW_FALSE; + GLenum error; + GLFWwindow* window; + + enum { CLIENT, CONTEXT, BEHAVIOR, DEBUG, FORWARD, HELP, EXTENSIONS, LAYERS, + MAJOR, MINOR, PROFILE, ROBUSTNESS, VERSION, + REDBITS, GREENBITS, BLUEBITS, ALPHABITS, DEPTHBITS, STENCILBITS, + ACCUMREDBITS, ACCUMGREENBITS, ACCUMBLUEBITS, ACCUMALPHABITS, + AUXBUFFERS, SAMPLES, STEREO, SRGB, SINGLEBUFFER, NOERROR_SRSLY }; + const struct option options[] = + { + { "behavior", 1, NULL, BEHAVIOR }, + { "client-api", 1, NULL, CLIENT }, + { "context-api", 1, NULL, CONTEXT }, + { "debug", 0, NULL, DEBUG }, + { "forward", 0, NULL, FORWARD }, + { "help", 0, NULL, HELP }, + { "list-extensions", 0, NULL, EXTENSIONS }, + { "list-layers", 0, NULL, LAYERS }, + { "major", 1, NULL, MAJOR }, + { "minor", 1, NULL, MINOR }, + { "profile", 1, NULL, PROFILE }, + { "robustness", 1, NULL, ROBUSTNESS }, + { "version", 0, NULL, VERSION }, + { "red-bits", 1, NULL, REDBITS }, + { "green-bits", 1, NULL, GREENBITS }, + { "blue-bits", 1, NULL, BLUEBITS }, + { "alpha-bits", 1, NULL, ALPHABITS }, + { "depth-bits", 1, NULL, DEPTHBITS }, + { "stencil-bits", 1, NULL, STENCILBITS }, + { "accum-red-bits", 1, NULL, ACCUMREDBITS }, + { "accum-green-bits", 1, NULL, ACCUMGREENBITS }, + { "accum-blue-bits", 1, NULL, ACCUMBLUEBITS }, + { "accum-alpha-bits", 1, NULL, ACCUMALPHABITS }, + { "aux-buffers", 1, NULL, AUXBUFFERS }, + { "samples", 1, NULL, SAMPLES }, + { "stereo", 0, NULL, STEREO }, + { "srgb", 0, NULL, SRGB }, + { "singlebuffer", 0, NULL, SINGLEBUFFER }, + { "no-error", 0, NULL, NOERROR_SRSLY }, + { NULL, 0, NULL, 0 } + }; + + // Initialize GLFW and create window + + if (!valid_version()) + exit(EXIT_FAILURE); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + while ((ch = getopt_long(argc, argv, "a:b:c:dfhlm:n:p:s:v", options, NULL)) != -1) + { + switch (ch) + { + case 'a': + case CLIENT: + if (strcasecmp(optarg, API_NAME_OPENGL) == 0) + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_API); + else if (strcasecmp(optarg, API_NAME_OPENGL_ES) == 0) + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + case 'b': + case BEHAVIOR: + if (strcasecmp(optarg, BEHAVIOR_NAME_NONE) == 0) + { + glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR, + GLFW_RELEASE_BEHAVIOR_NONE); + } + else if (strcasecmp(optarg, BEHAVIOR_NAME_FLUSH) == 0) + { + glfwWindowHint(GLFW_CONTEXT_RELEASE_BEHAVIOR, + GLFW_RELEASE_BEHAVIOR_FLUSH); + } + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + case 'c': + case CONTEXT: + if (strcasecmp(optarg, API_NAME_NATIVE) == 0) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API); + else if (strcasecmp(optarg, API_NAME_EGL) == 0) + glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API); + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + case 'd': + case DEBUG: + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + break; + case 'f': + case FORWARD: + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE); + break; + case 'h': + case HELP: + usage(); + exit(EXIT_SUCCESS); + case 'l': + case EXTENSIONS: + list_extensions = GLFW_TRUE; + break; + case LAYERS: + list_layers = GLFW_TRUE; + break; + case 'm': + case MAJOR: + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, atoi(optarg)); + break; + case 'n': + case MINOR: + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, atoi(optarg)); + break; + case 'p': + case PROFILE: + if (strcasecmp(optarg, PROFILE_NAME_CORE) == 0) + { + glfwWindowHint(GLFW_OPENGL_PROFILE, + GLFW_OPENGL_CORE_PROFILE); + } + else if (strcasecmp(optarg, PROFILE_NAME_COMPAT) == 0) + { + glfwWindowHint(GLFW_OPENGL_PROFILE, + GLFW_OPENGL_COMPAT_PROFILE); + } + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + case 's': + case ROBUSTNESS: + if (strcasecmp(optarg, STRATEGY_NAME_NONE) == 0) + { + glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS, + GLFW_NO_RESET_NOTIFICATION); + } + else if (strcasecmp(optarg, STRATEGY_NAME_LOSE) == 0) + { + glfwWindowHint(GLFW_CONTEXT_ROBUSTNESS, + GLFW_LOSE_CONTEXT_ON_RESET); + } + else + { + usage(); + exit(EXIT_FAILURE); + } + break; + case 'v': + case VERSION: + print_version(); + exit(EXIT_SUCCESS); + case REDBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_RED_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_RED_BITS, atoi(optarg)); + break; + case GREENBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_GREEN_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_GREEN_BITS, atoi(optarg)); + break; + case BLUEBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_BLUE_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_BLUE_BITS, atoi(optarg)); + break; + case ALPHABITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_ALPHA_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_ALPHA_BITS, atoi(optarg)); + break; + case DEPTHBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_DEPTH_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_DEPTH_BITS, atoi(optarg)); + break; + case STENCILBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_STENCIL_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_STENCIL_BITS, atoi(optarg)); + break; + case ACCUMREDBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_ACCUM_RED_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_ACCUM_RED_BITS, atoi(optarg)); + break; + case ACCUMGREENBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_ACCUM_GREEN_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_ACCUM_GREEN_BITS, atoi(optarg)); + break; + case ACCUMBLUEBITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_ACCUM_BLUE_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_ACCUM_BLUE_BITS, atoi(optarg)); + break; + case ACCUMALPHABITS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_ACCUM_ALPHA_BITS, atoi(optarg)); + break; + case AUXBUFFERS: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_AUX_BUFFERS, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_AUX_BUFFERS, atoi(optarg)); + break; + case SAMPLES: + if (strcmp(optarg, "-") == 0) + glfwWindowHint(GLFW_SAMPLES, GLFW_DONT_CARE); + else + glfwWindowHint(GLFW_SAMPLES, atoi(optarg)); + break; + case STEREO: + glfwWindowHint(GLFW_STEREO, GLFW_TRUE); + break; + case SRGB: + glfwWindowHint(GLFW_SRGB_CAPABLE, GLFW_TRUE); + break; + case SINGLEBUFFER: + glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE); + break; + case NOERROR_SRSLY: + glfwWindowHint(GLFW_CONTEXT_NO_ERROR, GLFW_TRUE); + break; + default: + usage(); + exit(EXIT_FAILURE); + } + } + + print_version(); + + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + window = glfwCreateWindow(200, 200, "Version", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + error = glGetError(); + if (error != GL_NO_ERROR) + printf("*** OpenGL error after make current: 0x%08x ***\n", error); + + // Report client API version + + client = glfwGetWindowAttrib(window, GLFW_CLIENT_API); + major = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MAJOR); + minor = glfwGetWindowAttrib(window, GLFW_CONTEXT_VERSION_MINOR); + revision = glfwGetWindowAttrib(window, GLFW_CONTEXT_REVISION); + profile = glfwGetWindowAttrib(window, GLFW_OPENGL_PROFILE); + + printf("%s context version string: \"%s\"\n", + get_api_name(client), + glGetString(GL_VERSION)); + + printf("%s context version parsed by GLFW: %u.%u.%u\n", + get_api_name(client), + major, minor, revision); + + // Report client API context properties + + if (client == GLFW_OPENGL_API) + { + if (major >= 3) + { + GLint flags; + + glGetIntegerv(GL_CONTEXT_FLAGS, &flags); + printf("%s context flags (0x%08x):", get_api_name(client), flags); + + if (flags & GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT) + printf(" forward-compatible"); + if (flags & 2/*GL_CONTEXT_FLAG_DEBUG_BIT*/) + printf(" debug"); + if (flags & GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB) + printf(" robustness"); + if (flags & 8/*GL_CONTEXT_FLAG_NO_ERROR_BIT_KHR*/) + printf(" no-error"); + putchar('\n'); + + printf("%s context flags parsed by GLFW:", get_api_name(client)); + + if (glfwGetWindowAttrib(window, GLFW_OPENGL_FORWARD_COMPAT)) + printf(" forward-compatible"); + if (glfwGetWindowAttrib(window, GLFW_OPENGL_DEBUG_CONTEXT)) + printf(" debug"); + if (glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS) == GLFW_LOSE_CONTEXT_ON_RESET) + printf(" robustness"); + if (glfwGetWindowAttrib(window, GLFW_CONTEXT_NO_ERROR)) + printf(" no-error"); + putchar('\n'); + } + + if (major >= 4 || (major == 3 && minor >= 2)) + { + GLint mask; + glGetIntegerv(GL_CONTEXT_PROFILE_MASK, &mask); + + printf("%s profile mask (0x%08x): %s\n", + get_api_name(client), + mask, + get_profile_name_gl(mask)); + + printf("%s profile mask parsed by GLFW: %s\n", + get_api_name(client), + get_profile_name_glfw(profile)); + } + + if (GLAD_GL_ARB_robustness) + { + const int robustness = glfwGetWindowAttrib(window, GLFW_CONTEXT_ROBUSTNESS); + GLint strategy; + glGetIntegerv(GL_RESET_NOTIFICATION_STRATEGY_ARB, &strategy); + + printf("%s robustness strategy (0x%08x): %s\n", + get_api_name(client), + strategy, + get_strategy_name_gl(strategy)); + + printf("%s robustness strategy parsed by GLFW: %s\n", + get_api_name(client), + get_strategy_name_glfw(robustness)); + } + } + + printf("%s context renderer string: \"%s\"\n", + get_api_name(client), + glGetString(GL_RENDERER)); + printf("%s context vendor string: \"%s\"\n", + get_api_name(client), + glGetString(GL_VENDOR)); + + if (major >= 2) + { + printf("%s context shading language version: \"%s\"\n", + get_api_name(client), + glGetString(GL_SHADING_LANGUAGE_VERSION)); + } + + printf("%s framebuffer:\n", get_api_name(client)); + + if (client == GLFW_OPENGL_API && profile == GLFW_OPENGL_CORE_PROFILE) + { + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, + &redbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, + &greenbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, + &bluebits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_BACK_LEFT, + GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, + &alphabits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_DEPTH, + GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, + &depthbits); + glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, + GL_STENCIL, + GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, + &stencilbits); + } + else + { + glGetIntegerv(GL_RED_BITS, &redbits); + glGetIntegerv(GL_GREEN_BITS, &greenbits); + glGetIntegerv(GL_BLUE_BITS, &bluebits); + glGetIntegerv(GL_ALPHA_BITS, &alphabits); + glGetIntegerv(GL_DEPTH_BITS, &depthbits); + glGetIntegerv(GL_STENCIL_BITS, &stencilbits); + } + + printf(" red: %u green: %u blue: %u alpha: %u depth: %u stencil: %u\n", + redbits, greenbits, bluebits, alphabits, depthbits, stencilbits); + + if (client == GLFW_OPENGL_ES_API || + GLAD_GL_ARB_multisample || + major > 1 || minor >= 3) + { + GLint samples, samplebuffers; + glGetIntegerv(GL_SAMPLES, &samples); + glGetIntegerv(GL_SAMPLE_BUFFERS, &samplebuffers); + + printf(" samples: %u sample buffers: %u\n", samples, samplebuffers); + } + + if (client == GLFW_OPENGL_API && profile != GLFW_OPENGL_CORE_PROFILE) + { + GLint accumredbits, accumgreenbits, accumbluebits, accumalphabits; + GLint auxbuffers; + + glGetIntegerv(GL_ACCUM_RED_BITS, &accumredbits); + glGetIntegerv(GL_ACCUM_GREEN_BITS, &accumgreenbits); + glGetIntegerv(GL_ACCUM_BLUE_BITS, &accumbluebits); + glGetIntegerv(GL_ACCUM_ALPHA_BITS, &accumalphabits); + glGetIntegerv(GL_AUX_BUFFERS, &auxbuffers); + + printf(" accum red: %u accum green: %u accum blue: %u accum alpha: %u aux buffers: %u\n", + accumredbits, accumgreenbits, accumbluebits, accumalphabits, auxbuffers); + } + + if (list_extensions) + list_context_extensions(client, major, minor); + + printf("Vulkan loader: %s\n", + glfwVulkanSupported() ? "available" : "missing"); + + if (glfwVulkanSupported()) + { + uint32_t i, re_count, pd_count; + const char** re; + VkApplicationInfo ai = {0}; + VkInstanceCreateInfo ici = {0}; + VkInstance instance; + VkPhysicalDevice* pd; + PFN_vkCreateInstance vkCreateInstance = (PFN_vkCreateInstance) + glfwGetInstanceProcAddress(NULL, "vkCreateInstance"); + PFN_vkDestroyInstance vkDestroyInstance; + PFN_vkEnumeratePhysicalDevices vkEnumeratePhysicalDevices; + PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + + re = glfwGetRequiredInstanceExtensions(&re_count); + + printf("Vulkan required instance extensions:"); + for (i = 0; i < re_count; i++) + printf(" %s", re[i]); + putchar('\n'); + + if (list_extensions) + list_vulkan_instance_extensions(); + + if (list_layers) + list_vulkan_instance_layers(); + + ai.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + ai.pApplicationName = "glfwinfo"; + ai.applicationVersion = GLFW_VERSION_MAJOR; + ai.pEngineName = "GLFW"; + ai.engineVersion = GLFW_VERSION_MAJOR; + ai.apiVersion = VK_API_VERSION_1_0; + + ici.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + ici.pApplicationInfo = &ai; + ici.enabledExtensionCount = re_count; + ici.ppEnabledExtensionNames = re; + + if (vkCreateInstance(&ici, NULL, &instance) != VK_SUCCESS) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + vkDestroyInstance = (PFN_vkDestroyInstance) + glfwGetInstanceProcAddress(instance, "vkDestroyInstance"); + vkEnumeratePhysicalDevices = (PFN_vkEnumeratePhysicalDevices) + glfwGetInstanceProcAddress(instance, "vkEnumeratePhysicalDevices"); + vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties) + glfwGetInstanceProcAddress(instance, "vkGetPhysicalDeviceProperties"); + + if (vkEnumeratePhysicalDevices(instance, &pd_count, NULL) != VK_SUCCESS) + { + vkDestroyInstance(instance, NULL); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + pd = calloc(pd_count, sizeof(VkPhysicalDevice)); + + if (vkEnumeratePhysicalDevices(instance, &pd_count, pd) != VK_SUCCESS) + { + free(pd); + vkDestroyInstance(instance, NULL); + glfwTerminate(); + exit(EXIT_FAILURE); + } + + for (i = 0; i < pd_count; i++) + { + VkPhysicalDeviceProperties pdp; + + vkGetPhysicalDeviceProperties(pd[i], &pdp); + + printf("Vulkan %s device: \"%s\"\n", + get_device_type_name(pdp.deviceType), + pdp.deviceName); + + if (list_extensions) + list_vulkan_device_extensions(instance, pd[i]); + + if (list_layers) + list_vulkan_device_layers(instance, pd[i]); + } + + free(pd); + vkDestroyInstance(instance, NULL); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/icon.c b/apps/exampleViewer/common/glfw/tests/icon.c new file mode 100644 index 0000000000..d866ff1a22 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/icon.c @@ -0,0 +1,148 @@ +//======================================================================== +// Window icon test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the icon feature. +// +//======================================================================== + +#include +#include + +#include +#include +#include + +// a simple glfw logo +const char* const logo[] = +{ + "................", + "................", + "...0000..0......", + "...0.....0......", + "...0.00..0......", + "...0..0..0......", + "...0000..0000...", + "................", + "................", + "...000..0...0...", + "...0....0...0...", + "...000..0.0.0...", + "...0....0.0.0...", + "...0....00000...", + "................", + "................" +}; + +const unsigned char icon_colors[5][4] = +{ + { 0, 0, 0, 255 }, // black + { 255, 0, 0, 255 }, // red + { 0, 255, 0, 255 }, // green + { 0, 0, 255, 255 }, // blue + { 255, 255, 255, 255 } // white +}; + +static int cur_icon_color = 0; + +static void set_icon(GLFWwindow* window, int icon_color) +{ + int x, y; + unsigned char pixels[16 * 16 * 4]; + unsigned char* target = pixels; + GLFWimage img = { 16, 16, pixels }; + + for (y = 0; y < img.width; y++) + { + for (x = 0; x < img.height; x++) + { + if (logo[y][x] == '0') + memcpy(target, icon_colors[icon_color], 4); + else + memset(target, 0, 4); + + target += 4; + } + } + + glfwSetWindowIcon(window, 1, &img); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_SPACE: + cur_icon_color = (cur_icon_color + 1) % 5; + set_icon(window, cur_icon_color); + break; + case GLFW_KEY_X: + glfwSetWindowIcon(window, 0, NULL); + break; + } +} + +int main(int argc, char** argv) +{ + GLFWwindow* window; + + if (!glfwInit()) + { + fprintf(stderr, "Failed to initialize GLFW\n"); + exit(EXIT_FAILURE); + } + + window = glfwCreateWindow(200, 200, "Window Icon", NULL, NULL); + if (!window) + { + glfwTerminate(); + + fprintf(stderr, "Failed to open GLFW window\n"); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + glfwSetKeyCallback(window, key_callback); + set_icon(window, cur_icon_color); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + glfwWaitEvents(); + } + + glfwDestroyWindow(window); + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/iconify.c b/apps/exampleViewer/common/glfw/tests/iconify.c new file mode 100644 index 0000000000..ba6425c48a --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/iconify.c @@ -0,0 +1,306 @@ +//======================================================================== +// Iconify/restore test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test the iconify/restore functionality for +// both full screen and windowed mode windows +// +//======================================================================== + +#include +#include + +#include +#include + +#include "getopt.h" + +static int windowed_xpos, windowed_ypos, windowed_width, windowed_height; + +static void usage(void) +{ + printf("Usage: iconify [-h] [-f [-a] [-n]]\n"); + printf("Options:\n"); + printf(" -a create windows for all monitors\n"); + printf(" -f create full screen window(s)\n"); + printf(" -h show this help\n"); + printf(" -n no automatic iconification of full screen windows\n"); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + printf("%0.2f Key %s\n", + glfwGetTime(), + action == GLFW_PRESS ? "pressed" : "released"); + + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_I: + glfwIconifyWindow(window); + break; + case GLFW_KEY_M: + glfwMaximizeWindow(window); + break; + case GLFW_KEY_R: + glfwRestoreWindow(window); + break; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + case GLFW_KEY_F11: + case GLFW_KEY_ENTER: + { + if (mods != GLFW_MOD_ALT) + return; + + if (glfwGetWindowMonitor(window)) + { + glfwSetWindowMonitor(window, NULL, + windowed_xpos, windowed_ypos, + windowed_width, windowed_height, + 0); + } + else + { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + glfwGetWindowPos(window, &windowed_xpos, &windowed_ypos); + glfwGetWindowSize(window, &windowed_width, &windowed_height); + glfwSetWindowMonitor(window, monitor, + 0, 0, mode->width, mode->height, + mode->refreshRate); + } + } + + break; + } + } +} + +static void window_size_callback(GLFWwindow* window, int width, int height) +{ + printf("%0.2f Window resized to %ix%i\n", glfwGetTime(), width, height); +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + printf("%0.2f Framebuffer resized to %ix%i\n", glfwGetTime(), width, height); + + glViewport(0, 0, width, height); +} + +static void window_focus_callback(GLFWwindow* window, int focused) +{ + printf("%0.2f Window %s\n", + glfwGetTime(), + focused ? "focused" : "defocused"); +} + +static void window_iconify_callback(GLFWwindow* window, int iconified) +{ + printf("%0.2f Window %s\n", + glfwGetTime(), + iconified ? "iconified" : "uniconified"); +} + +static void window_maximize_callback(GLFWwindow* window, int maximized) +{ + printf("%0.2f Window %s\n", + glfwGetTime(), + maximized ? "maximized" : "unmaximized"); +} + +static void window_refresh_callback(GLFWwindow* window) +{ + int width, height; + + printf("%0.2f Window refresh\n", glfwGetTime()); + + glfwGetFramebufferSize(window, &width, &height); + + glfwMakeContextCurrent(window); + + glEnable(GL_SCISSOR_TEST); + + glScissor(0, 0, width, height); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(0, 0, 640, 480); + glClearColor(1, 1, 1, 0); + glClear(GL_COLOR_BUFFER_BIT); + + glfwSwapBuffers(window); +} + +static GLFWwindow* create_window(GLFWmonitor* monitor) +{ + int width, height; + GLFWwindow* window; + + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + + width = mode->width; + height = mode->height; + } + else + { + width = 640; + height = 480; + } + + window = glfwCreateWindow(width, height, "Iconify", monitor, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + return window; +} + +int main(int argc, char** argv) +{ + int ch, i, window_count; + int auto_iconify = GLFW_TRUE, fullscreen = GLFW_FALSE, all_monitors = GLFW_FALSE; + GLFWwindow** windows; + + while ((ch = getopt(argc, argv, "afhn")) != -1) + { + switch (ch) + { + case 'a': + all_monitors = GLFW_TRUE; + break; + + case 'h': + usage(); + exit(EXIT_SUCCESS); + + case 'f': + fullscreen = GLFW_TRUE; + break; + + case 'n': + auto_iconify = GLFW_FALSE; + break; + + default: + usage(); + exit(EXIT_FAILURE); + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_AUTO_ICONIFY, auto_iconify); + + if (fullscreen && all_monitors) + { + int monitor_count; + GLFWmonitor** monitors = glfwGetMonitors(&monitor_count); + + window_count = monitor_count; + windows = calloc(window_count, sizeof(GLFWwindow*)); + + for (i = 0; i < monitor_count; i++) + { + windows[i] = create_window(monitors[i]); + if (!windows[i]) + break; + } + } + else + { + GLFWmonitor* monitor = NULL; + + if (fullscreen) + monitor = glfwGetPrimaryMonitor(); + + window_count = 1; + windows = calloc(window_count, sizeof(GLFWwindow*)); + windows[0] = create_window(monitor); + } + + for (i = 0; i < window_count; i++) + { + glfwSetKeyCallback(windows[i], key_callback); + glfwSetFramebufferSizeCallback(windows[i], framebuffer_size_callback); + glfwSetWindowSizeCallback(windows[i], window_size_callback); + glfwSetWindowFocusCallback(windows[i], window_focus_callback); + glfwSetWindowIconifyCallback(windows[i], window_iconify_callback); + glfwSetWindowMaximizeCallback(windows[i], window_maximize_callback); + glfwSetWindowRefreshCallback(windows[i], window_refresh_callback); + + window_refresh_callback(windows[i]); + + printf("Window is %s and %s\n", + glfwGetWindowAttrib(windows[i], GLFW_ICONIFIED) ? "iconified" : "restored", + glfwGetWindowAttrib(windows[i], GLFW_FOCUSED) ? "focused" : "defocused"); + } + + for (;;) + { + glfwWaitEvents(); + + for (i = 0; i < window_count; i++) + { + if (glfwWindowShouldClose(windows[i])) + break; + } + + if (i < window_count) + break; + + // Workaround for an issue with msvcrt and mintty + fflush(stdout); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/joysticks.c b/apps/exampleViewer/common/glfw/tests/joysticks.c new file mode 100644 index 0000000000..ca51511fd9 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/joysticks.c @@ -0,0 +1,215 @@ +//======================================================================== +// Joystick input test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test displays the state of every button and axis of every connected +// joystick and/or gamepad +// +//======================================================================== + +#include +#include + +#define NK_IMPLEMENTATION +#define NK_INCLUDE_FIXED_TYPES +#define NK_INCLUDE_FONT_BAKING +#define NK_INCLUDE_DEFAULT_FONT +#define NK_INCLUDE_DEFAULT_ALLOCATOR +#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT +#define NK_INCLUDE_STANDARD_VARARGS +#define NK_BUTTON_TRIGGER_ON_RELEASE +#include + +#define NK_GLFW_GL2_IMPLEMENTATION +#include + +#include +#include +#include + +#ifdef _MSC_VER +#define strdup(x) _strdup(x) +#endif + +static int joysticks[GLFW_JOYSTICK_LAST + 1]; +static int joystick_count = 0; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void joystick_callback(int jid, int event) +{ + if (event == GLFW_CONNECTED) + joysticks[joystick_count++] = jid; + else if (event == GLFW_DISCONNECTED) + { + int i; + + for (i = 0; i < joystick_count; i++) + { + if (joysticks[i] == jid) + break; + } + + for (i = i + 1; i < joystick_count; i++) + joysticks[i - 1] = joysticks[i]; + + joystick_count--; + } +} + +static const char* joystick_label(int jid) +{ + static char label[1024]; + snprintf(label, sizeof(label), "%i: %s", jid + 1, glfwGetJoystickName(jid)); + return label; +} + +int main(void) +{ + int jid; + GLFWwindow* window; + struct nk_context* nk; + struct nk_font_atlas* atlas; + + memset(joysticks, 0, sizeof(joysticks)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + for (jid = GLFW_JOYSTICK_1; jid <= GLFW_JOYSTICK_LAST; jid++) + { + if (glfwJoystickPresent(jid)) + joystick_callback(jid, GLFW_CONNECTED); + } + + glfwSetJoystickCallback(joystick_callback); + + window = glfwCreateWindow(640, 480, "Joystick Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + nk = nk_glfw3_init(window, NK_GLFW3_INSTALL_CALLBACKS); + nk_glfw3_font_stash_begin(&atlas); + nk_glfw3_font_stash_end(); + + while (!glfwWindowShouldClose(window)) + { + int i, width, height; + struct nk_panel layout; + + glfwGetWindowSize(window, &width, &height); + + glClear(GL_COLOR_BUFFER_BIT); + nk_glfw3_new_frame(); + + if (nk_begin(nk, &layout, + "Joysticks", + nk_rect(0.f, 0.f, 0.f, 0.f), + NK_WINDOW_MINIMIZABLE | + NK_WINDOW_TITLE)) + { + nk_window_set_bounds(nk, nk_rect(width - 200.f, 0.f, + 200.f, (float) height)); + + nk_layout_row_dynamic(nk, 30, 1); + + if (joystick_count) + { + for (i = 0; i < joystick_count; i++) + { + if (nk_button_label(nk, joystick_label(joysticks[i]))) + nk_window_set_focus(nk, joystick_label(joysticks[i])); + } + } + else + nk_label(nk, "No joysticks connected", NK_TEXT_LEFT); + } + + nk_end(nk); + + for (i = 0; i < joystick_count; i++) + { + if (nk_begin(nk, &layout, + joystick_label(joysticks[i]), + nk_rect(i * 20.f, i * 20.f, 400.f, 400.f), + NK_WINDOW_BORDER | + NK_WINDOW_MOVABLE | + NK_WINDOW_SCALABLE | + NK_WINDOW_MINIMIZABLE | + NK_WINDOW_TITLE)) + { + int j, axis_count, button_count; + const float* axes; + const unsigned char* buttons; + + nk_layout_row_dynamic(nk, 30, 1); + + axes = glfwGetJoystickAxes(joysticks[i], &axis_count); + if (axis_count) + { + for (j = 0; j < axis_count; j++) + nk_slide_float(nk, -1.f, axes[j], 1.f, 0.1f); + } + + nk_layout_row_dynamic(nk, 30, 8); + + buttons = glfwGetJoystickButtons(joysticks[i], &button_count); + if (button_count) + { + for (j = 0; j < button_count; j++) + { + char name[16]; + snprintf(name, sizeof(name), "%i", j + 1); + nk_select_label(nk, name, NK_TEXT_CENTERED, buttons[j]); + } + } + + nk_layout_row_end(nk); + } + + nk_end(nk); + } + + nk_glfw3_render(NK_ANTI_ALIASING_ON, 10000, 1000); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/monitors.c b/apps/exampleViewer/common/glfw/tests/monitors.c new file mode 100644 index 0000000000..77a2a51ff9 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/monitors.c @@ -0,0 +1,251 @@ +//======================================================================== +// Monitor information tool +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test prints monitor and video mode information or verifies video +// modes +// +//======================================================================== + +#include +#include + +#include +#include +#include + +#include "getopt.h" + +enum Mode +{ + LIST_MODE, + TEST_MODE +}; + +static void usage(void) +{ + printf("Usage: monitors [-t]\n"); + printf(" monitors -h\n"); +} + +static int euclid(int a, int b) +{ + return b ? euclid(b, a % b) : a; +} + +static const char* format_mode(const GLFWvidmode* mode) +{ + static char buffer[512]; + const int gcd = euclid(mode->width, mode->height); + + snprintf(buffer, + sizeof(buffer), + "%i x %i x %i (%i:%i) (%i %i %i) %i Hz", + mode->width, mode->height, + mode->redBits + mode->greenBits + mode->blueBits, + mode->width / gcd, mode->height / gcd, + mode->redBits, mode->greenBits, mode->blueBits, + mode->refreshRate); + + buffer[sizeof(buffer) - 1] = '\0'; + return buffer; +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + printf("Framebuffer resized to %ix%i\n", width, height); + + glViewport(0, 0, width, height); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static void list_modes(GLFWmonitor* monitor) +{ + int count, x, y, widthMM, heightMM, i; + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); + + glfwGetMonitorPos(monitor, &x, &y); + glfwGetMonitorPhysicalSize(monitor, &widthMM, &heightMM); + + printf("Name: %s (%s)\n", + glfwGetMonitorName(monitor), + glfwGetPrimaryMonitor() == monitor ? "primary" : "secondary"); + printf("Current mode: %s\n", format_mode(mode)); + printf("Virtual position: %i %i\n", x, y); + + printf("Physical size: %i x %i mm (%0.2f dpi)\n", + widthMM, heightMM, mode->width * 25.4f / widthMM); + + printf("Modes:\n"); + + for (i = 0; i < count; i++) + { + printf("%3u: %s", (unsigned int) i, format_mode(modes + i)); + + if (memcmp(mode, modes + i, sizeof(GLFWvidmode)) == 0) + printf(" (current mode)"); + + putchar('\n'); + } +} + +static void test_modes(GLFWmonitor* monitor) +{ + int i, count; + GLFWwindow* window; + const GLFWvidmode* modes = glfwGetVideoModes(monitor, &count); + + for (i = 0; i < count; i++) + { + const GLFWvidmode* mode = modes + i; + GLFWvidmode current; + + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + + printf("Testing mode %u on monitor %s: %s\n", + (unsigned int) i, + glfwGetMonitorName(monitor), + format_mode(mode)); + + window = glfwCreateWindow(mode->width, mode->height, + "Video Mode Test", + glfwGetPrimaryMonitor(), + NULL); + if (!window) + { + printf("Failed to enter mode %u: %s\n", + (unsigned int) i, + format_mode(mode)); + continue; + } + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetKeyCallback(window, key_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glfwSetTime(0.0); + + while (glfwGetTime() < 5.0) + { + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + glfwPollEvents(); + + if (glfwWindowShouldClose(window)) + { + printf("User terminated program\n"); + + glfwTerminate(); + exit(EXIT_SUCCESS); + } + } + + glGetIntegerv(GL_RED_BITS, ¤t.redBits); + glGetIntegerv(GL_GREEN_BITS, ¤t.greenBits); + glGetIntegerv(GL_BLUE_BITS, ¤t.blueBits); + + glfwGetWindowSize(window, ¤t.width, ¤t.height); + + if (current.redBits != mode->redBits || + current.greenBits != mode->greenBits || + current.blueBits != mode->blueBits) + { + printf("*** Color bit mismatch: (%i %i %i) instead of (%i %i %i)\n", + current.redBits, current.greenBits, current.blueBits, + mode->redBits, mode->greenBits, mode->blueBits); + } + + if (current.width != mode->width || current.height != mode->height) + { + printf("*** Size mismatch: %ix%i instead of %ix%i\n", + current.width, current.height, + mode->width, mode->height); + } + + printf("Closing window\n"); + + glfwDestroyWindow(window); + window = NULL; + + glfwPollEvents(); + } +} + +int main(int argc, char** argv) +{ + int ch, i, count, mode = LIST_MODE; + GLFWmonitor** monitors; + + while ((ch = getopt(argc, argv, "th")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 't': + mode = TEST_MODE; + break; + default: + usage(); + exit(EXIT_FAILURE); + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + monitors = glfwGetMonitors(&count); + + for (i = 0; i < count; i++) + { + if (mode == LIST_MODE) + list_modes(monitors[i]); + else if (mode == TEST_MODE) + test_modes(monitors[i]); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/msaa.c b/apps/exampleViewer/common/glfw/tests/msaa.c new file mode 100644 index 0000000000..b8aae34b3d --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/msaa.c @@ -0,0 +1,217 @@ +//======================================================================== +// Multisample anti-aliasing test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test renders two high contrast, slowly rotating quads, one aliased +// and one (hopefully) anti-aliased, thus allowing for visual verification +// of whether MSAA is indeed enabled +// +//======================================================================== + +#include +#include + +#if defined(_MSC_VER) + // Make MS math.h define M_PI + #define _USE_MATH_DEFINES +#endif + +#include "linmath.h" + +#include +#include + +#include "getopt.h" + +static const vec2 vertices[4] = +{ + { -0.6f, -0.6f }, + { 0.6f, -0.6f }, + { 0.6f, 0.6f }, + { -0.6f, 0.6f } +}; + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec2 vPos;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +"}\n"; + +static const char* fragment_shader_text = +"void main()\n" +"{\n" +" gl_FragColor = vec4(1.0);\n" +"}\n"; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_SPACE: + glfwSetTime(0.0); + break; + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + } +} + +static void usage(void) +{ + printf("Usage: msaa [-h] [-s SAMPLES]\n"); +} + +int main(int argc, char** argv) +{ + int ch, samples = 4; + GLFWwindow* window; + GLuint vertex_buffer, vertex_shader, fragment_shader, program; + GLint mvp_location, vpos_location; + + while ((ch = getopt(argc, argv, "hs:")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 's': + samples = atoi(optarg); + break; + default: + usage(); + exit(EXIT_FAILURE); + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + if (samples) + printf("Requesting MSAA with %i samples\n", samples); + else + printf("Requesting that MSAA not be available\n"); + + glfwWindowHint(GLFW_SAMPLES, samples); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(800, 400, "Aliasing Detector", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(window, key_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glGetIntegerv(GL_SAMPLES, &samples); + if (samples) + printf("Context reports MSAA is available with %i samples\n", samples); + else + printf("Context reports MSAA is unavailable\n"); + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + + while (!glfwWindowShouldClose(window)) + { + float ratio; + int width, height; + mat4x4 m, p, mvp; + const double angle = glfwGetTime() * M_PI / 180.0; + + glfwGetFramebufferSize(window, &width, &height); + ratio = width / (float) height; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + glUseProgram(program); + + mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 0.f, 1.f); + + mat4x4_translate(m, -1.f, 0.f, 0.f); + mat4x4_rotate_Z(m, m, (float) angle); + mat4x4_mul(mvp, p, m); + + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDisable(GL_MULTISAMPLE); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + mat4x4_translate(m, 1.f, 0.f, 0.f); + mat4x4_rotate_Z(m, m, (float) angle); + mat4x4_mul(mvp, p, m); + + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glEnable(GL_MULTISAMPLE); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glfwSwapBuffers(window); + glfwPollEvents(); + } + + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/reopen.c b/apps/exampleViewer/common/glfw/tests/reopen.c new file mode 100644 index 0000000000..4ea0c1e2ab --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/reopen.c @@ -0,0 +1,237 @@ +//======================================================================== +// Window re-opener (open/close stress test) +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test came about as the result of bug #1262773 +// +// It closes and re-opens the GLFW window every five seconds, alternating +// between windowed and full screen mode +// +// It also times and logs opening and closing actions and attempts to separate +// user initiated window closing from its own +// +//======================================================================== + +#include +#include + +#include +#include +#include + +#include "linmath.h" + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec2 vPos;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +"}\n"; + +static const char* fragment_shader_text = +"void main()\n" +"{\n" +" gl_FragColor = vec4(1.0);\n" +"}\n"; + +static const vec2 vertices[4] = +{ + { -0.5f, -0.5f }, + { 0.5f, -0.5f }, + { 0.5f, 0.5f }, + { -0.5f, 0.5f } +}; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void window_close_callback(GLFWwindow* window) +{ + printf("Close callback triggered\n"); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_Q: + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + } +} + +static void close_window(GLFWwindow* window) +{ + double base = glfwGetTime(); + glfwDestroyWindow(window); + printf("Closing window took %0.3f seconds\n", glfwGetTime() - base); +} + +int main(int argc, char** argv) +{ + int count = 0; + double base; + GLFWwindow* window; + + srand((unsigned int) time(NULL)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + for (;;) + { + int width, height; + GLFWmonitor* monitor = NULL; + GLuint vertex_shader, fragment_shader, program, vertex_buffer; + GLint mvp_location, vpos_location; + + if (count & 1) + { + int monitorCount; + GLFWmonitor** monitors = glfwGetMonitors(&monitorCount); + monitor = monitors[rand() % monitorCount]; + } + + if (monitor) + { + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + width = mode->width; + height = mode->height; + } + else + { + width = 640; + height = 480; + } + + base = glfwGetTime(); + + window = glfwCreateWindow(width, height, "Window Re-opener", monitor, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + if (monitor) + { + printf("Opening full screen window on monitor %s took %0.3f seconds\n", + glfwGetMonitorName(monitor), + glfwGetTime() - base); + } + else + { + printf("Opening regular window took %0.3f seconds\n", + glfwGetTime() - base); + } + + glfwSetWindowCloseCallback(window, window_close_callback); + glfwSetKeyCallback(window, key_callback); + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + + glfwSetTime(0.0); + + while (glfwGetTime() < 5.0) + { + float ratio; + int width, height; + mat4x4 m, p, mvp; + + glfwGetFramebufferSize(window, &width, &height); + ratio = width / (float) height; + + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT); + + mat4x4_ortho(p, -ratio, ratio, -1.f, 1.f, 0.f, 1.f); + + mat4x4_identity(m); + mat4x4_rotate_Z(m, m, (float) glfwGetTime()); + mat4x4_mul(mvp, p, m); + + glUseProgram(program); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glfwSwapBuffers(window); + glfwPollEvents(); + + if (glfwWindowShouldClose(window)) + { + close_window(window); + printf("User closed window\n"); + + glfwTerminate(); + exit(EXIT_SUCCESS); + } + } + + printf("Closing window\n"); + close_window(window); + + count++; + } + + glfwTerminate(); +} + diff --git a/apps/exampleViewer/common/glfw/tests/sharing.c b/apps/exampleViewer/common/glfw/tests/sharing.c new file mode 100644 index 0000000000..32f99ce146 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/sharing.c @@ -0,0 +1,264 @@ +//======================================================================== +// Context sharing test program +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This program is used to test sharing of objects between contexts +// +//======================================================================== + +#include +#include + +#include +#include +#include + +#include "getopt.h" +#include "linmath.h" + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec2 vPos;\n" +"varying vec2 texcoord;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +" texcoord = vPos;\n" +"}\n"; + +static const char* fragment_shader_text = +"uniform sampler2D texture;\n" +"uniform vec3 color;\n" +"varying vec2 texcoord;\n" +"void main()\n" +"{\n" +" gl_FragColor = vec4(color * texture2D(texture, texcoord).rgb, 1.0);\n" +"}\n"; + +static const vec2 vertices[4] = +{ + { 0.f, 0.f }, + { 1.f, 0.f }, + { 1.f, 1.f }, + { 0.f, 1.f } +}; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +void APIENTRY debug_callback(GLenum source, + GLenum type, + GLuint id, + GLenum severity, + GLsizei length, + const GLchar* message, + const void* user) +{ + fprintf(stderr, "Error: %s\n", message); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action == GLFW_PRESS && key == GLFW_KEY_ESCAPE) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +int main(int argc, char** argv) +{ + int ch; + GLFWwindow* windows[2]; + GLuint texture, program, vertex_buffer; + GLint mvp_location, vpos_location, color_location, texture_location; + + srand((unsigned int) time(NULL)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + while ((ch = getopt(argc, argv, "d")) != -1) + { + switch (ch) + { + case 'd': + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GLFW_TRUE); + break; + } + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + windows[0] = glfwCreateWindow(400, 400, "First", NULL, NULL); + if (!windows[0]) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(windows[0], key_callback); + + glfwMakeContextCurrent(windows[0]); + + // Only enable vsync for the first of the windows to be swapped to + // avoid waiting out the interval for each window + glfwSwapInterval(1); + + // The contexts are created with the same APIs so the function + // pointers should be re-usable between them + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + + if (GLAD_GL_KHR_debug) + { + glDebugMessageCallback(debug_callback, NULL); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, NULL, GL_TRUE); + } + + // Create the OpenGL objects inside the first context, created above + // All objects will be shared with the second context, created below + { + int x, y; + char pixels[16 * 16]; + GLuint vertex_shader, fragment_shader; + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + + for (y = 0; y < 16; y++) + { + for (x = 0; x < 16; x++) + pixels[y * 16 + x] = rand() % 256; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 16, 16, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + color_location = glGetUniformLocation(program, "color"); + texture_location = glGetUniformLocation(program, "texture"); + vpos_location = glGetAttribLocation(program, "vPos"); + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + } + + glUseProgram(program); + glUniform1i(texture_location, 0); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + + windows[1] = glfwCreateWindow(400, 400, "Second", NULL, windows[0]); + if (!windows[1]) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + // Place the second window to the right of the first + { + int xpos, ypos, left, right, width; + + glfwGetWindowSize(windows[0], &width, NULL); + glfwGetWindowFrameSize(windows[0], &left, NULL, &right, NULL); + glfwGetWindowPos(windows[0], &xpos, &ypos); + + glfwSetWindowPos(windows[1], xpos + width + left + right, ypos); + } + + glfwSetKeyCallback(windows[1], key_callback); + + glfwMakeContextCurrent(windows[1]); + + // While objects are shared, the global context state is not and will + // need to be set up for each context + + glUseProgram(program); + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, texture); + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + + while (!glfwWindowShouldClose(windows[0]) && + !glfwWindowShouldClose(windows[1])) + { + int i; + const vec3 colors[2] = + { + { 0.3f, 0.4f, 1.f }, + { 0.8f, 0.4f, 1.f } + }; + + for (i = 0; i < 2; i++) + { + int width, height; + mat4x4 mvp; + + glfwGetFramebufferSize(windows[i], &width, &height); + glfwMakeContextCurrent(windows[i]); + + glViewport(0, 0, width, height); + + mat4x4_ortho(mvp, 0.f, 1.f, 0.f, 1.f, 0.f, 1.f); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glUniform3fv(color_location, 1, colors[i]); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glfwSwapBuffers(windows[i]); + } + + glfwWaitEvents(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/tearing.c b/apps/exampleViewer/common/glfw/tests/tearing.c new file mode 100644 index 0000000000..46c51e40f9 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/tearing.c @@ -0,0 +1,272 @@ +//======================================================================== +// Vsync enabling test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test renders a high contrast, horizontally moving bar, allowing for +// visual verification of whether the set swap interval is indeed obeyed +// +//======================================================================== + +#include +#include + +#include +#include +#include + +#include "linmath.h" +#include "getopt.h" + +static const struct +{ + float x, y; +} vertices[4] = +{ + { -0.25f, -1.f }, + { 0.25f, -1.f }, + { 0.25f, 1.f }, + { -0.25f, 1.f } +}; + +static const char* vertex_shader_text = +"uniform mat4 MVP;\n" +"attribute vec2 vPos;\n" +"void main()\n" +"{\n" +" gl_Position = MVP * vec4(vPos, 0.0, 1.0);\n" +"}\n"; + +static const char* fragment_shader_text = +"void main()\n" +"{\n" +" gl_FragColor = vec4(1.0);\n" +"}\n"; + +static int swap_tear; +static int swap_interval; +static double frame_rate; + +static void usage(void) +{ + printf("Usage: tearing [-h] [-f]\n"); + printf("Options:\n"); + printf(" -f create full screen window\n"); + printf(" -h show this help\n"); +} + +static void update_window_title(GLFWwindow* window) +{ + char title[256]; + + snprintf(title, sizeof(title), "Tearing detector (interval %i%s, %0.1f Hz)", + swap_interval, + (swap_tear && swap_interval < 0) ? " (swap tear)" : "", + frame_rate); + + glfwSetWindowTitle(window, title); +} + +static void set_swap_interval(GLFWwindow* window, int interval) +{ + swap_interval = interval; + glfwSwapInterval(swap_interval); + update_window_title(window); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_UP: + { + if (swap_interval + 1 > swap_interval) + set_swap_interval(window, swap_interval + 1); + break; + } + + case GLFW_KEY_DOWN: + { + if (swap_tear) + { + if (swap_interval - 1 < swap_interval) + set_swap_interval(window, swap_interval - 1); + } + else + { + if (swap_interval - 1 >= 0) + set_swap_interval(window, swap_interval - 1); + } + break; + } + + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, 1); + break; + } +} + +int main(int argc, char** argv) +{ + int ch, width, height; + unsigned long frame_count = 0; + double last_time, current_time; + int fullscreen = GLFW_FALSE; + GLFWmonitor* monitor = NULL; + GLFWwindow* window; + GLuint vertex_buffer, vertex_shader, fragment_shader, program; + GLint mvp_location, vpos_location; + + while ((ch = getopt(argc, argv, "fh")) != -1) + { + switch (ch) + { + case 'h': + usage(); + exit(EXIT_SUCCESS); + + case 'f': + fullscreen = GLFW_TRUE; + break; + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + if (fullscreen) + { + const GLFWvidmode* mode; + + monitor = glfwGetPrimaryMonitor(); + mode = glfwGetVideoMode(monitor); + + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + glfwWindowHint(GLFW_REFRESH_RATE, mode->refreshRate); + + width = mode->width; + height = mode->height; + } + else + { + width = 640; + height = 480; + } + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(width, height, "", monitor, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + set_swap_interval(window, 0); + + last_time = glfwGetTime(); + frame_rate = 0.0; + swap_tear = (glfwExtensionSupported("WGL_EXT_swap_control_tear") || + glfwExtensionSupported("GLX_EXT_swap_control_tear")); + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetKeyCallback(window, key_callback); + + glGenBuffers(1, &vertex_buffer); + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); + + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(vertex_shader, 1, &vertex_shader_text, NULL); + glCompileShader(vertex_shader); + + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(fragment_shader, 1, &fragment_shader_text, NULL); + glCompileShader(fragment_shader); + + program = glCreateProgram(); + glAttachShader(program, vertex_shader); + glAttachShader(program, fragment_shader); + glLinkProgram(program); + + mvp_location = glGetUniformLocation(program, "MVP"); + vpos_location = glGetAttribLocation(program, "vPos"); + + glEnableVertexAttribArray(vpos_location); + glVertexAttribPointer(vpos_location, 2, GL_FLOAT, GL_FALSE, + sizeof(vertices[0]), (void*) 0); + + while (!glfwWindowShouldClose(window)) + { + mat4x4 m, p, mvp; + float position = cosf((float) glfwGetTime() * 4.f) * 0.75f; + + glClear(GL_COLOR_BUFFER_BIT); + + mat4x4_ortho(p, -1.f, 1.f, -1.f, 1.f, 0.f, 1.f); + mat4x4_translate(m, position, 0.f, 0.f); + mat4x4_mul(mvp, p, m); + + glUseProgram(program); + glUniformMatrix4fv(mvp_location, 1, GL_FALSE, (const GLfloat*) mvp); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glfwSwapBuffers(window); + glfwPollEvents(); + + frame_count++; + + current_time = glfwGetTime(); + if (current_time - last_time > 1.0) + { + frame_rate = frame_count / (current_time - last_time); + frame_count = 0; + last_time = current_time; + update_window_title(window); + } + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/threads.c b/apps/exampleViewer/common/glfw/tests/threads.c new file mode 100644 index 0000000000..d5e3483849 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/threads.c @@ -0,0 +1,151 @@ +//======================================================================== +// Multi-threading test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test is intended to verify whether the OpenGL context part of +// the GLFW API is able to be used from multiple threads +// +//======================================================================== + +#include "tinycthread.h" + +#include +#include + +#include +#include +#include + +typedef struct +{ + GLFWwindow* window; + const char* title; + float r, g, b; + thrd_t id; +} Thread; + +static volatile int running = GLFW_TRUE; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static int thread_main(void* data) +{ + const Thread* thread = data; + + glfwMakeContextCurrent(thread->window); + glfwSwapInterval(1); + + while (running) + { + const float v = (float) fabs(sin(glfwGetTime() * 2.f)); + glClearColor(thread->r * v, thread->g * v, thread->b * v, 0.f); + + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(thread->window); + } + + glfwMakeContextCurrent(NULL); + return 0; +} + +int main(void) +{ + int i, result; + Thread threads[] = + { + { NULL, "Red", 1.f, 0.f, 0.f, 0 }, + { NULL, "Green", 0.f, 1.f, 0.f, 0 }, + { NULL, "Blue", 0.f, 0.f, 1.f, 0 } + }; + const int count = sizeof(threads) / sizeof(Thread); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + for (i = 0; i < count; i++) + { + threads[i].window = glfwCreateWindow(200, 200, + threads[i].title, + NULL, NULL); + if (!threads[i].window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(threads[i].window, key_callback); + + glfwSetWindowPos(threads[i].window, 200 + 250 * i, 200); + glfwShowWindow(threads[i].window); + } + + glfwMakeContextCurrent(threads[0].window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwMakeContextCurrent(NULL); + + for (i = 0; i < count; i++) + { + if (thrd_create(&threads[i].id, thread_main, threads + i) != + thrd_success) + { + fprintf(stderr, "Failed to create secondary thread\n"); + + glfwTerminate(); + exit(EXIT_FAILURE); + } + } + + while (running) + { + glfwWaitEvents(); + + for (i = 0; i < count; i++) + { + if (glfwWindowShouldClose(threads[i].window)) + running = GLFW_FALSE; + } + } + + for (i = 0; i < count; i++) + glfwHideWindow(threads[i].window); + + for (i = 0; i < count; i++) + thrd_join(threads[i].id, &result); + + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/timeout.c b/apps/exampleViewer/common/glfw/tests/timeout.c new file mode 100644 index 0000000000..4bf05514b5 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/timeout.c @@ -0,0 +1,97 @@ +//======================================================================== +// Event wait timeout test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test is intended to verify that waiting for events with timeout works +// +//======================================================================== + +#include +#include + +#include +#include +#include +#include + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static float nrand(void) +{ + return (float) rand() / (float) RAND_MAX; +} + +int main(void) +{ + GLFWwindow* window; + + srand((unsigned int) time(NULL)); + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = glfwCreateWindow(640, 480, "Event Wait Timeout Test", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSetKeyCallback(window, key_callback); + + while (!glfwWindowShouldClose(window)) + { + int width, height; + float r = nrand(), g = nrand(), b = nrand(); + float l = (float) sqrt(r * r + g * g + b * b); + + glfwGetFramebufferSize(window, &width, &height); + + glViewport(0, 0, width, height); + glClearColor(r / l, g / l, b / l, 1.f); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + + glfwWaitEventsTimeout(1.0); + } + + glfwDestroyWindow(window); + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/title.c b/apps/exampleViewer/common/glfw/tests/title.c new file mode 100644 index 0000000000..9fe67f4e7f --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/title.c @@ -0,0 +1,78 @@ +//======================================================================== +// UTF-8 window title test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test sets a UTF-8 window title +// +//======================================================================== + +#include +#include + +#include +#include + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +int main(void) +{ + GLFWwindow* window; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = glfwCreateWindow(400, 400, "English 日本語 русский язык 官話", NULL, NULL); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwMakeContextCurrent(window); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glfwSwapInterval(1); + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(window); + glfwWaitEvents(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/exampleViewer/common/glfw/tests/vulkan.c b/apps/exampleViewer/common/glfw/tests/vulkan.c new file mode 100644 index 0000000000..1b62d68145 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/vulkan.c @@ -0,0 +1,2245 @@ +/* + * Copyright (c) 2015-2016 The Khronos Group Inc. + * Copyright (c) 2015-2016 Valve Corporation + * Copyright (c) 2015-2016 LunarG, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and/or associated documentation files (the "Materials"), to + * deal in the Materials without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Materials, and to permit persons to whom the Materials are + * furnished to do so, subject to the following conditions: + * + * The above copyright notice(s) and this permission notice shall be included in + * all copies or substantial portions of the Materials. + * + * THE MATERIALS ARE 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 MATERIALS OR THE + * USE OR OTHER DEALINGS IN THE MATERIALS. + * + * Author: Chia-I Wu + * Author: Cody Northrop + * Author: Courtney Goeltzenleuchter + * Author: Ian Elliott + * Author: Jon Ashburn + * Author: Piers Daniell + */ +/* + * Draw a textured triangle with depth testing. This is written against Intel + * ICD. It does not do state transition nor object memory binding like it + * should. It also does no error checking. + */ + +#ifndef _MSC_VER +#define _ISOC11_SOURCE /* for aligned_alloc() */ +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#define DEMO_TEXTURE_COUNT 1 +#define VERTEX_BUFFER_BIND_ID 0 +#define APP_SHORT_NAME "vulkan" +#define APP_LONG_NAME "The Vulkan Triangle Demo Program" + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) + +#if defined(NDEBUG) && defined(__GNUC__) +#define U_ASSERT_ONLY __attribute__((unused)) +#else +#define U_ASSERT_ONLY +#endif + +#define ERR_EXIT(err_msg, err_class) \ + do { \ + printf(err_msg); \ + fflush(stdout); \ + exit(1); \ + } while (0) + +#define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ + { \ + demo->fp##entrypoint = \ + (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ + if (demo->fp##entrypoint == NULL) { \ + ERR_EXIT("vkGetInstanceProcAddr failed to find vk" #entrypoint, \ + "vkGetInstanceProcAddr Failure"); \ + } \ + } + +#define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ + { \ + demo->fp##entrypoint = \ + (PFN_vk##entrypoint)vkGetDeviceProcAddr(dev, "vk" #entrypoint); \ + if (demo->fp##entrypoint == NULL) { \ + ERR_EXIT("vkGetDeviceProcAddr failed to find vk" #entrypoint, \ + "vkGetDeviceProcAddr Failure"); \ + } \ + } + +static const char fragShaderCode[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x03, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x03, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, 0x65, 0x70, 0x61, 0x72, + 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x65, 0x72, 0x5f, 0x6f, + 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, 0x68, 0x61, 0x64, 0x69, + 0x6e, 0x67, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, + 0x34, 0x32, 0x30, 0x70, 0x61, 0x63, 0x6b, 0x00, 0x05, 0x00, 0x04, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, 0x75, 0x46, 0x72, 0x61, + 0x67, 0x43, 0x6f, 0x6c, 0x6f, 0x72, 0x00, 0x00, 0x05, 0x00, 0x03, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, 0x05, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x19, 0x00, 0x09, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x03, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x57, 0x00, 0x05, 0x00, 0x07, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; + +static const char vertShaderCode[] = { + 0x03, 0x02, 0x23, 0x07, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x08, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x02, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x47, 0x4c, 0x53, 0x4c, 0x2e, 0x73, 0x74, 0x64, 0x2e, 0x34, 0x35, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x03, 0x00, 0x02, 0x00, 0x00, 0x00, 0x90, 0x01, 0x00, 0x00, + 0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, + 0x65, 0x70, 0x61, 0x72, 0x61, 0x74, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x64, + 0x65, 0x72, 0x5f, 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x00, 0x00, + 0x04, 0x00, 0x09, 0x00, 0x47, 0x4c, 0x5f, 0x41, 0x52, 0x42, 0x5f, 0x73, + 0x68, 0x61, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x6c, 0x61, 0x6e, 0x67, 0x75, + 0x61, 0x67, 0x65, 0x5f, 0x34, 0x32, 0x30, 0x70, 0x61, 0x63, 0x6b, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x04, 0x00, 0x00, 0x00, 0x6d, 0x61, 0x69, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x05, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x74, 0x65, 0x78, 0x63, 0x6f, 0x6f, 0x72, 0x64, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x61, 0x74, 0x74, 0x72, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x67, 0x6c, 0x5f, 0x50, 0x65, 0x72, 0x56, 0x65, 0x72, 0x74, 0x65, 0x78, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x73, 0x69, 0x74, + 0x69, 0x6f, 0x6e, 0x00, 0x06, 0x00, 0x07, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x50, 0x6f, 0x69, 0x6e, 0x74, + 0x53, 0x69, 0x7a, 0x65, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x07, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x43, + 0x6c, 0x69, 0x70, 0x44, 0x69, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x03, 0x00, 0x17, 0x00, 0x00, 0x00, 0x70, 0x6f, 0x73, 0x00, + 0x05, 0x00, 0x05, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x56, + 0x65, 0x72, 0x74, 0x65, 0x78, 0x49, 0x44, 0x00, 0x05, 0x00, 0x06, 0x00, + 0x1d, 0x00, 0x00, 0x00, 0x67, 0x6c, 0x5f, 0x49, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x44, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x48, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x05, 0x00, + 0x11, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x47, 0x00, 0x03, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x04, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, + 0x47, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x13, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x21, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x16, 0x00, 0x03, 0x00, 0x06, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x17, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x15, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x04, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x05, 0x00, 0x11, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x12, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, + 0x3b, 0x00, 0x04, 0x00, 0x12, 0x00, 0x00, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x15, 0x00, 0x04, 0x00, 0x14, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x04, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x17, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x04, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x20, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x3b, 0x00, 0x04, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x36, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x04, 0x00, + 0x0d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, + 0x41, 0x00, 0x05, 0x00, 0x19, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, + 0x1a, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x01, 0x00, + 0x38, 0x00, 0x01, 0x00 +}; + +struct texture_object { + VkSampler sampler; + + VkImage image; + VkImageLayout imageLayout; + + VkDeviceMemory mem; + VkImageView view; + int32_t tex_width, tex_height; +}; + +VKAPI_ATTR VkBool32 VKAPI_CALL +dbgFunc(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, + uint64_t srcObject, size_t location, int32_t msgCode, + const char *pLayerPrefix, const char *pMsg, void *pUserData) { + char *message = (char *)malloc(strlen(pMsg) + 100); + + assert(message); + + if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) { + sprintf(message, "ERROR: [%s] Code %d : %s", pLayerPrefix, msgCode, + pMsg); + } else if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) { + sprintf(message, "WARNING: [%s] Code %d : %s", pLayerPrefix, msgCode, + pMsg); + } else { + return false; + } + + printf("%s\n", message); + fflush(stdout); + free(message); + + /* + * false indicates that layer should not bail-out of an + * API call that had validation failures. This may mean that the + * app dies inside the driver due to invalid parameter(s). + * That's what would happen without validation layers, so we'll + * keep that behavior here. + */ + return false; +} + +typedef struct _SwapchainBuffers { + VkImage image; + VkCommandBuffer cmd; + VkImageView view; +} SwapchainBuffers; + +struct demo { + GLFWwindow* window; + VkSurfaceKHR surface; + bool use_staging_buffer; + + VkAllocationCallbacks allocator; + + VkInstance inst; + VkPhysicalDevice gpu; + VkDevice device; + VkQueue queue; + VkPhysicalDeviceProperties gpu_props; + VkQueueFamilyProperties *queue_props; + uint32_t graphics_queue_node_index; + + uint32_t enabled_extension_count; + uint32_t enabled_layer_count; + const char *extension_names[64]; + char *device_validation_layers[64]; + + int width, height; + VkFormat format; + VkColorSpaceKHR color_space; + + PFN_vkGetPhysicalDeviceSurfaceSupportKHR + fpGetPhysicalDeviceSurfaceSupportKHR; + PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR + fpGetPhysicalDeviceSurfaceCapabilitiesKHR; + PFN_vkGetPhysicalDeviceSurfaceFormatsKHR + fpGetPhysicalDeviceSurfaceFormatsKHR; + PFN_vkGetPhysicalDeviceSurfacePresentModesKHR + fpGetPhysicalDeviceSurfacePresentModesKHR; + PFN_vkCreateSwapchainKHR fpCreateSwapchainKHR; + PFN_vkDestroySwapchainKHR fpDestroySwapchainKHR; + PFN_vkGetSwapchainImagesKHR fpGetSwapchainImagesKHR; + PFN_vkAcquireNextImageKHR fpAcquireNextImageKHR; + PFN_vkQueuePresentKHR fpQueuePresentKHR; + uint32_t swapchainImageCount; + VkSwapchainKHR swapchain; + SwapchainBuffers *buffers; + + VkCommandPool cmd_pool; + + struct { + VkFormat format; + + VkImage image; + VkDeviceMemory mem; + VkImageView view; + } depth; + + struct texture_object textures[DEMO_TEXTURE_COUNT]; + + struct { + VkBuffer buf; + VkDeviceMemory mem; + + VkPipelineVertexInputStateCreateInfo vi; + VkVertexInputBindingDescription vi_bindings[1]; + VkVertexInputAttributeDescription vi_attrs[2]; + } vertices; + + VkCommandBuffer setup_cmd; // Command Buffer for initialization commands + VkCommandBuffer draw_cmd; // Command Buffer for drawing commands + VkPipelineLayout pipeline_layout; + VkDescriptorSetLayout desc_layout; + VkPipelineCache pipelineCache; + VkRenderPass render_pass; + VkPipeline pipeline; + + VkShaderModule vert_shader_module; + VkShaderModule frag_shader_module; + + VkDescriptorPool desc_pool; + VkDescriptorSet desc_set; + + VkFramebuffer *framebuffers; + + VkPhysicalDeviceMemoryProperties memory_properties; + + bool validate; + PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; + PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; + VkDebugReportCallbackEXT msg_callback; + + float depthStencil; + float depthIncrement; + + uint32_t current_buffer; + uint32_t queue_count; +}; + +// Forward declaration: +static void demo_resize(struct demo *demo); + +static bool memory_type_from_properties(struct demo *demo, uint32_t typeBits, + VkFlags requirements_mask, + uint32_t *typeIndex) { + uint32_t i; + + // Search memtypes to find first index with those properties + for (i = 0; i < 32; i++) { + if ((typeBits & 1) == 1) { + // Type is available, does it match user properties? + if ((demo->memory_properties.memoryTypes[i].propertyFlags & + requirements_mask) == requirements_mask) { + *typeIndex = i; + return true; + } + } + typeBits >>= 1; + } + // No memory types matched, return failure + return false; +} + +static void demo_flush_init_cmd(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + + if (demo->setup_cmd == VK_NULL_HANDLE) + return; + + err = vkEndCommandBuffer(demo->setup_cmd); + assert(!err); + + const VkCommandBuffer cmd_bufs[] = {demo->setup_cmd}; + VkFence nullFence = {VK_NULL_HANDLE}; + VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .waitSemaphoreCount = 0, + .pWaitSemaphores = NULL, + .pWaitDstStageMask = NULL, + .commandBufferCount = 1, + .pCommandBuffers = cmd_bufs, + .signalSemaphoreCount = 0, + .pSignalSemaphores = NULL}; + + err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); + assert(!err); + + err = vkQueueWaitIdle(demo->queue); + assert(!err); + + vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, cmd_bufs); + demo->setup_cmd = VK_NULL_HANDLE; +} + +static void demo_set_image_layout(struct demo *demo, VkImage image, + VkImageAspectFlags aspectMask, + VkImageLayout old_image_layout, + VkImageLayout new_image_layout) { + VkResult U_ASSERT_ONLY err; + + if (demo->setup_cmd == VK_NULL_HANDLE) { + const VkCommandBufferAllocateInfo cmd = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = NULL, + .commandPool = demo->cmd_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + + err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->setup_cmd); + assert(!err); + + VkCommandBufferInheritanceInfo cmd_buf_hinfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + .pNext = NULL, + .renderPass = VK_NULL_HANDLE, + .subpass = 0, + .framebuffer = VK_NULL_HANDLE, + .occlusionQueryEnable = VK_FALSE, + .queryFlags = 0, + .pipelineStatistics = 0, + }; + VkCommandBufferBeginInfo cmd_buf_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = NULL, + .flags = 0, + .pInheritanceInfo = &cmd_buf_hinfo, + }; + err = vkBeginCommandBuffer(demo->setup_cmd, &cmd_buf_info); + assert(!err); + } + + VkImageMemoryBarrier image_memory_barrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = NULL, + .srcAccessMask = 0, + .dstAccessMask = 0, + .oldLayout = old_image_layout, + .newLayout = new_image_layout, + .image = image, + .subresourceRange = {aspectMask, 0, 1, 0, 1}}; + + if (new_image_layout == VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL) { + /* Make sure anything that was copying from this image has completed */ + image_memory_barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = + VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL) { + image_memory_barrier.dstAccessMask = + VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; + } + + if (new_image_layout == VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { + /* Make sure any Copy or CPU writes to image are flushed */ + image_memory_barrier.dstAccessMask = + VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT; + } + + VkImageMemoryBarrier *pmemory_barrier = &image_memory_barrier; + + VkPipelineStageFlags src_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + VkPipelineStageFlags dest_stages = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; + + vkCmdPipelineBarrier(demo->setup_cmd, src_stages, dest_stages, 0, 0, NULL, + 0, NULL, 1, pmemory_barrier); +} + +static void demo_draw_build_cmd(struct demo *demo) { + const VkCommandBufferInheritanceInfo cmd_buf_hinfo = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, + .pNext = NULL, + .renderPass = VK_NULL_HANDLE, + .subpass = 0, + .framebuffer = VK_NULL_HANDLE, + .occlusionQueryEnable = VK_FALSE, + .queryFlags = 0, + .pipelineStatistics = 0, + }; + const VkCommandBufferBeginInfo cmd_buf_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, + .pNext = NULL, + .flags = 0, + .pInheritanceInfo = &cmd_buf_hinfo, + }; + const VkClearValue clear_values[2] = { + [0] = {.color.float32 = {0.2f, 0.2f, 0.2f, 0.2f}}, + [1] = {.depthStencil = {demo->depthStencil, 0}}, + }; + const VkRenderPassBeginInfo rp_begin = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, + .pNext = NULL, + .renderPass = demo->render_pass, + .framebuffer = demo->framebuffers[demo->current_buffer], + .renderArea.offset.x = 0, + .renderArea.offset.y = 0, + .renderArea.extent.width = demo->width, + .renderArea.extent.height = demo->height, + .clearValueCount = 2, + .pClearValues = clear_values, + }; + VkResult U_ASSERT_ONLY err; + + err = vkBeginCommandBuffer(demo->draw_cmd, &cmd_buf_info); + assert(!err); + + vkCmdBeginRenderPass(demo->draw_cmd, &rp_begin, VK_SUBPASS_CONTENTS_INLINE); + vkCmdBindPipeline(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + demo->pipeline); + vkCmdBindDescriptorSets(demo->draw_cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, + demo->pipeline_layout, 0, 1, &demo->desc_set, 0, + NULL); + + VkViewport viewport; + memset(&viewport, 0, sizeof(viewport)); + viewport.height = (float)demo->height; + viewport.width = (float)demo->width; + viewport.minDepth = (float)0.0f; + viewport.maxDepth = (float)1.0f; + vkCmdSetViewport(demo->draw_cmd, 0, 1, &viewport); + + VkRect2D scissor; + memset(&scissor, 0, sizeof(scissor)); + scissor.extent.width = demo->width; + scissor.extent.height = demo->height; + scissor.offset.x = 0; + scissor.offset.y = 0; + vkCmdSetScissor(demo->draw_cmd, 0, 1, &scissor); + + VkDeviceSize offsets[1] = {0}; + vkCmdBindVertexBuffers(demo->draw_cmd, VERTEX_BUFFER_BIND_ID, 1, + &demo->vertices.buf, offsets); + + vkCmdDraw(demo->draw_cmd, 3, 1, 0, 0); + vkCmdEndRenderPass(demo->draw_cmd); + + VkImageMemoryBarrier prePresentBarrier = { + .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, + .pNext = NULL, + .srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + .dstAccessMask = VK_ACCESS_MEMORY_READ_BIT, + .oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}}; + + prePresentBarrier.image = demo->buffers[demo->current_buffer].image; + VkImageMemoryBarrier *pmemory_barrier = &prePresentBarrier; + vkCmdPipelineBarrier(demo->draw_cmd, VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, NULL, 0, + NULL, 1, pmemory_barrier); + + err = vkEndCommandBuffer(demo->draw_cmd); + assert(!err); +} + +static void demo_draw(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + VkSemaphore presentCompleteSemaphore; + VkSemaphoreCreateInfo presentCompleteSemaphoreCreateInfo = { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = NULL, + .flags = 0, + }; + + err = vkCreateSemaphore(demo->device, &presentCompleteSemaphoreCreateInfo, + NULL, &presentCompleteSemaphore); + assert(!err); + + // Get the index of the next available swapchain image: + err = demo->fpAcquireNextImageKHR(demo->device, demo->swapchain, UINT64_MAX, + presentCompleteSemaphore, + (VkFence)0, // TODO: Show use of fence + &demo->current_buffer); + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + // demo->swapchain is out of date (e.g. the window was resized) and + // must be recreated: + demo_resize(demo); + demo_draw(demo); + vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); + return; + } else if (err == VK_SUBOPTIMAL_KHR) { + // demo->swapchain is not as optimal as it could be, but the platform's + // presentation engine will still present the image correctly. + } else { + assert(!err); + } + + // Assume the command buffer has been run on current_buffer before so + // we need to set the image layout back to COLOR_ATTACHMENT_OPTIMAL + demo_set_image_layout(demo, demo->buffers[demo->current_buffer].image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, + VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); + demo_flush_init_cmd(demo); + + // Wait for the present complete semaphore to be signaled to ensure + // that the image won't be rendered to until the presentation + // engine has fully released ownership to the application, and it is + // okay to render to the image. + + // FIXME/TODO: DEAL WITH VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + demo_draw_build_cmd(demo); + VkFence nullFence = VK_NULL_HANDLE; + VkPipelineStageFlags pipe_stage_flags = + VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = NULL, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &presentCompleteSemaphore, + .pWaitDstStageMask = &pipe_stage_flags, + .commandBufferCount = 1, + .pCommandBuffers = &demo->draw_cmd, + .signalSemaphoreCount = 0, + .pSignalSemaphores = NULL}; + + err = vkQueueSubmit(demo->queue, 1, &submit_info, nullFence); + assert(!err); + + VkPresentInfoKHR present = { + .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, + .pNext = NULL, + .swapchainCount = 1, + .pSwapchains = &demo->swapchain, + .pImageIndices = &demo->current_buffer, + }; + + // TBD/TODO: SHOULD THE "present" PARAMETER BE "const" IN THE HEADER? + err = demo->fpQueuePresentKHR(demo->queue, &present); + if (err == VK_ERROR_OUT_OF_DATE_KHR) { + // demo->swapchain is out of date (e.g. the window was resized) and + // must be recreated: + demo_resize(demo); + } else if (err == VK_SUBOPTIMAL_KHR) { + // demo->swapchain is not as optimal as it could be, but the platform's + // presentation engine will still present the image correctly. + } else { + assert(!err); + } + + err = vkQueueWaitIdle(demo->queue); + assert(err == VK_SUCCESS); + + vkDestroySemaphore(demo->device, presentCompleteSemaphore, NULL); +} + +static void demo_prepare_buffers(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + VkSwapchainKHR oldSwapchain = demo->swapchain; + + // Check the surface capabilities and formats + VkSurfaceCapabilitiesKHR surfCapabilities; + err = demo->fpGetPhysicalDeviceSurfaceCapabilitiesKHR( + demo->gpu, demo->surface, &surfCapabilities); + assert(!err); + + uint32_t presentModeCount; + err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( + demo->gpu, demo->surface, &presentModeCount, NULL); + assert(!err); + VkPresentModeKHR *presentModes = + (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); + assert(presentModes); + err = demo->fpGetPhysicalDeviceSurfacePresentModesKHR( + demo->gpu, demo->surface, &presentModeCount, presentModes); + assert(!err); + + VkExtent2D swapchainExtent; + // width and height are either both -1, or both not -1. + if (surfCapabilities.currentExtent.width == (uint32_t)-1) { + // If the surface size is undefined, the size is set to + // the size of the images requested. + swapchainExtent.width = demo->width; + swapchainExtent.height = demo->height; + } else { + // If the surface size is defined, the swap chain size must match + swapchainExtent = surfCapabilities.currentExtent; + demo->width = surfCapabilities.currentExtent.width; + demo->height = surfCapabilities.currentExtent.height; + } + + VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR; + + // Determine the number of VkImage's to use in the swap chain (we desire to + // own only 1 image at a time, besides the images being displayed and + // queued for display): + uint32_t desiredNumberOfSwapchainImages = + surfCapabilities.minImageCount + 1; + if ((surfCapabilities.maxImageCount > 0) && + (desiredNumberOfSwapchainImages > surfCapabilities.maxImageCount)) { + // Application must settle for fewer images than desired: + desiredNumberOfSwapchainImages = surfCapabilities.maxImageCount; + } + + VkSurfaceTransformFlagsKHR preTransform; + if (surfCapabilities.supportedTransforms & + VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { + preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + } else { + preTransform = surfCapabilities.currentTransform; + } + + const VkSwapchainCreateInfoKHR swapchain = { + .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, + .pNext = NULL, + .surface = demo->surface, + .minImageCount = desiredNumberOfSwapchainImages, + .imageFormat = demo->format, + .imageColorSpace = demo->color_space, + .imageExtent = + { + .width = swapchainExtent.width, .height = swapchainExtent.height, + }, + .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, + .preTransform = preTransform, + .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, + .imageArrayLayers = 1, + .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE, + .queueFamilyIndexCount = 0, + .pQueueFamilyIndices = NULL, + .presentMode = swapchainPresentMode, + .oldSwapchain = oldSwapchain, + .clipped = true, + }; + uint32_t i; + + err = demo->fpCreateSwapchainKHR(demo->device, &swapchain, NULL, + &demo->swapchain); + assert(!err); + + // If we just re-created an existing swapchain, we should destroy the old + // swapchain at this point. + // Note: destroying the swapchain also cleans up all its associated + // presentable images once the platform is done with them. + if (oldSwapchain != VK_NULL_HANDLE) { + demo->fpDestroySwapchainKHR(demo->device, oldSwapchain, NULL); + } + + err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, + &demo->swapchainImageCount, NULL); + assert(!err); + + VkImage *swapchainImages = + (VkImage *)malloc(demo->swapchainImageCount * sizeof(VkImage)); + assert(swapchainImages); + err = demo->fpGetSwapchainImagesKHR(demo->device, demo->swapchain, + &demo->swapchainImageCount, + swapchainImages); + assert(!err); + + demo->buffers = (SwapchainBuffers *)malloc(sizeof(SwapchainBuffers) * + demo->swapchainImageCount); + assert(demo->buffers); + + for (i = 0; i < demo->swapchainImageCount; i++) { + VkImageViewCreateInfo color_attachment_view = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = NULL, + .format = demo->format, + .components = + { + .r = VK_COMPONENT_SWIZZLE_R, + .g = VK_COMPONENT_SWIZZLE_G, + .b = VK_COMPONENT_SWIZZLE_B, + .a = VK_COMPONENT_SWIZZLE_A, + }, + .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .flags = 0, + }; + + demo->buffers[i].image = swapchainImages[i]; + + // Render loop will expect image to have been used before and in + // VK_IMAGE_LAYOUT_PRESENT_SRC_KHR + // layout and will change to COLOR_ATTACHMENT_OPTIMAL, so init the image + // to that state + demo_set_image_layout( + demo, demo->buffers[i].image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR); + + color_attachment_view.image = demo->buffers[i].image; + + err = vkCreateImageView(demo->device, &color_attachment_view, NULL, + &demo->buffers[i].view); + assert(!err); + } + + demo->current_buffer = 0; + + if (NULL != presentModes) { + free(presentModes); + } +} + +static void demo_prepare_depth(struct demo *demo) { + const VkFormat depth_format = VK_FORMAT_D16_UNORM; + const VkImageCreateInfo image = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = NULL, + .imageType = VK_IMAGE_TYPE_2D, + .format = depth_format, + .extent = {demo->width, demo->height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = VK_IMAGE_TILING_OPTIMAL, + .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, + .flags = 0, + }; + VkMemoryAllocateInfo mem_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = NULL, + .allocationSize = 0, + .memoryTypeIndex = 0, + }; + VkImageViewCreateInfo view = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = NULL, + .image = VK_NULL_HANDLE, + .format = depth_format, + .subresourceRange = {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1}, + .flags = 0, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + }; + + VkMemoryRequirements mem_reqs; + VkResult U_ASSERT_ONLY err; + bool U_ASSERT_ONLY pass; + + demo->depth.format = depth_format; + + /* create image */ + err = vkCreateImage(demo->device, &image, NULL, &demo->depth.image); + assert(!err); + + /* get memory requirements for this object */ + vkGetImageMemoryRequirements(demo->device, demo->depth.image, &mem_reqs); + + /* select memory size and type */ + mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, + 0, /* No requirements */ + &mem_alloc.memoryTypeIndex); + assert(pass); + + /* allocate memory */ + err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->depth.mem); + assert(!err); + + /* bind memory */ + err = + vkBindImageMemory(demo->device, demo->depth.image, demo->depth.mem, 0); + assert(!err); + + demo_set_image_layout(demo, demo->depth.image, VK_IMAGE_ASPECT_DEPTH_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); + + /* create image view */ + view.image = demo->depth.image; + err = vkCreateImageView(demo->device, &view, NULL, &demo->depth.view); + assert(!err); +} + +static void +demo_prepare_texture_image(struct demo *demo, const uint32_t *tex_colors, + struct texture_object *tex_obj, VkImageTiling tiling, + VkImageUsageFlags usage, VkFlags required_props) { + const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM; + const int32_t tex_width = 2; + const int32_t tex_height = 2; + VkResult U_ASSERT_ONLY err; + bool U_ASSERT_ONLY pass; + + tex_obj->tex_width = tex_width; + tex_obj->tex_height = tex_height; + + const VkImageCreateInfo image_create_info = { + .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, + .pNext = NULL, + .imageType = VK_IMAGE_TYPE_2D, + .format = tex_format, + .extent = {tex_width, tex_height, 1}, + .mipLevels = 1, + .arrayLayers = 1, + .samples = VK_SAMPLE_COUNT_1_BIT, + .tiling = tiling, + .usage = usage, + .flags = 0, + }; + VkMemoryAllocateInfo mem_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = NULL, + .allocationSize = 0, + .memoryTypeIndex = 0, + }; + + VkMemoryRequirements mem_reqs; + + err = + vkCreateImage(demo->device, &image_create_info, NULL, &tex_obj->image); + assert(!err); + + vkGetImageMemoryRequirements(demo->device, tex_obj->image, &mem_reqs); + + mem_alloc.allocationSize = mem_reqs.size; + pass = + memory_type_from_properties(demo, mem_reqs.memoryTypeBits, + required_props, &mem_alloc.memoryTypeIndex); + assert(pass); + + /* allocate memory */ + err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &tex_obj->mem); + assert(!err); + + /* bind memory */ + err = vkBindImageMemory(demo->device, tex_obj->image, tex_obj->mem, 0); + assert(!err); + + if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) { + const VkImageSubresource subres = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .mipLevel = 0, + .arrayLayer = 0, + }; + VkSubresourceLayout layout; + void *data; + int32_t x, y; + + vkGetImageSubresourceLayout(demo->device, tex_obj->image, &subres, + &layout); + + err = vkMapMemory(demo->device, tex_obj->mem, 0, + mem_alloc.allocationSize, 0, &data); + assert(!err); + + for (y = 0; y < tex_height; y++) { + uint32_t *row = (uint32_t *)((char *)data + layout.rowPitch * y); + for (x = 0; x < tex_width; x++) + row[x] = tex_colors[(x & 1) ^ (y & 1)]; + } + + vkUnmapMemory(demo->device, tex_obj->mem); + } + + tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + demo_set_image_layout(demo, tex_obj->image, VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_UNDEFINED, tex_obj->imageLayout); + /* setting the image layout does not reference the actual memory so no need + * to add a mem ref */ +} + +static void demo_destroy_texture_image(struct demo *demo, + struct texture_object *tex_obj) { + /* clean up staging resources */ + vkDestroyImage(demo->device, tex_obj->image, NULL); + vkFreeMemory(demo->device, tex_obj->mem, NULL); +} + +static void demo_prepare_textures(struct demo *demo) { + const VkFormat tex_format = VK_FORMAT_B8G8R8A8_UNORM; + VkFormatProperties props; + const uint32_t tex_colors[DEMO_TEXTURE_COUNT][2] = { + {0xffff0000, 0xff00ff00}, + }; + uint32_t i; + VkResult U_ASSERT_ONLY err; + + vkGetPhysicalDeviceFormatProperties(demo->gpu, tex_format, &props); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + if ((props.linearTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) && + !demo->use_staging_buffer) { + /* Device can texture using linear textures */ + demo_prepare_texture_image(demo, tex_colors[i], &demo->textures[i], + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_SAMPLED_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + } else if (props.optimalTilingFeatures & + VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) { + /* Must use staging buffer to copy linear texture to optimized */ + struct texture_object staging_texture; + + memset(&staging_texture, 0, sizeof(staging_texture)); + demo_prepare_texture_image(demo, tex_colors[i], &staging_texture, + VK_IMAGE_TILING_LINEAR, + VK_IMAGE_USAGE_TRANSFER_SRC_BIT, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT); + + demo_prepare_texture_image( + demo, tex_colors[i], &demo->textures[i], + VK_IMAGE_TILING_OPTIMAL, + (VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT), + VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + + demo_set_image_layout(demo, staging_texture.image, + VK_IMAGE_ASPECT_COLOR_BIT, + staging_texture.imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL); + + demo_set_image_layout(demo, demo->textures[i].image, + VK_IMAGE_ASPECT_COLOR_BIT, + demo->textures[i].imageLayout, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL); + + VkImageCopy copy_region = { + .srcSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + .srcOffset = {0, 0, 0}, + .dstSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}, + .dstOffset = {0, 0, 0}, + .extent = {staging_texture.tex_width, + staging_texture.tex_height, 1}, + }; + vkCmdCopyImage( + demo->setup_cmd, staging_texture.image, + VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, demo->textures[i].image, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ©_region); + + demo_set_image_layout(demo, demo->textures[i].image, + VK_IMAGE_ASPECT_COLOR_BIT, + VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, + demo->textures[i].imageLayout); + + demo_flush_init_cmd(demo); + + demo_destroy_texture_image(demo, &staging_texture); + } else { + /* Can't support VK_FORMAT_B8G8R8A8_UNORM !? */ + assert(!"No support for B8G8R8A8_UNORM as texture image format"); + } + + const VkSamplerCreateInfo sampler = { + .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO, + .pNext = NULL, + .magFilter = VK_FILTER_NEAREST, + .minFilter = VK_FILTER_NEAREST, + .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST, + .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT, + .mipLodBias = 0.0f, + .anisotropyEnable = VK_FALSE, + .maxAnisotropy = 1, + .compareOp = VK_COMPARE_OP_NEVER, + .minLod = 0.0f, + .maxLod = 0.0f, + .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE, + .unnormalizedCoordinates = VK_FALSE, + }; + VkImageViewCreateInfo view = { + .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, + .pNext = NULL, + .image = VK_NULL_HANDLE, + .viewType = VK_IMAGE_VIEW_TYPE_2D, + .format = tex_format, + .components = + { + VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, + VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A, + }, + .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}, + .flags = 0, + }; + + /* create sampler */ + err = vkCreateSampler(demo->device, &sampler, NULL, + &demo->textures[i].sampler); + assert(!err); + + /* create image view */ + view.image = demo->textures[i].image; + err = vkCreateImageView(demo->device, &view, NULL, + &demo->textures[i].view); + assert(!err); + } +} + +static void demo_prepare_vertices(struct demo *demo) { + // clang-format off + const float vb[3][5] = { + /* position texcoord */ + { -1.0f, -1.0f, 0.25f, 0.0f, 0.0f }, + { 1.0f, -1.0f, 0.25f, 1.0f, 0.0f }, + { 0.0f, 1.0f, 1.0f, 0.5f, 1.0f }, + }; + // clang-format on + const VkBufferCreateInfo buf_info = { + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .pNext = NULL, + .size = sizeof(vb), + .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + .flags = 0, + }; + VkMemoryAllocateInfo mem_alloc = { + .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, + .pNext = NULL, + .allocationSize = 0, + .memoryTypeIndex = 0, + }; + VkMemoryRequirements mem_reqs; + VkResult U_ASSERT_ONLY err; + bool U_ASSERT_ONLY pass; + void *data; + + memset(&demo->vertices, 0, sizeof(demo->vertices)); + + err = vkCreateBuffer(demo->device, &buf_info, NULL, &demo->vertices.buf); + assert(!err); + + vkGetBufferMemoryRequirements(demo->device, demo->vertices.buf, &mem_reqs); + assert(!err); + + mem_alloc.allocationSize = mem_reqs.size; + pass = memory_type_from_properties(demo, mem_reqs.memoryTypeBits, + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, + &mem_alloc.memoryTypeIndex); + assert(pass); + + err = vkAllocateMemory(demo->device, &mem_alloc, NULL, &demo->vertices.mem); + assert(!err); + + err = vkMapMemory(demo->device, demo->vertices.mem, 0, + mem_alloc.allocationSize, 0, &data); + assert(!err); + + memcpy(data, vb, sizeof(vb)); + + vkUnmapMemory(demo->device, demo->vertices.mem); + + err = vkBindBufferMemory(demo->device, demo->vertices.buf, + demo->vertices.mem, 0); + assert(!err); + + demo->vertices.vi.sType = + VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + demo->vertices.vi.pNext = NULL; + demo->vertices.vi.vertexBindingDescriptionCount = 1; + demo->vertices.vi.pVertexBindingDescriptions = demo->vertices.vi_bindings; + demo->vertices.vi.vertexAttributeDescriptionCount = 2; + demo->vertices.vi.pVertexAttributeDescriptions = demo->vertices.vi_attrs; + + demo->vertices.vi_bindings[0].binding = VERTEX_BUFFER_BIND_ID; + demo->vertices.vi_bindings[0].stride = sizeof(vb[0]); + demo->vertices.vi_bindings[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + demo->vertices.vi_attrs[0].binding = VERTEX_BUFFER_BIND_ID; + demo->vertices.vi_attrs[0].location = 0; + demo->vertices.vi_attrs[0].format = VK_FORMAT_R32G32B32_SFLOAT; + demo->vertices.vi_attrs[0].offset = 0; + + demo->vertices.vi_attrs[1].binding = VERTEX_BUFFER_BIND_ID; + demo->vertices.vi_attrs[1].location = 1; + demo->vertices.vi_attrs[1].format = VK_FORMAT_R32G32_SFLOAT; + demo->vertices.vi_attrs[1].offset = sizeof(float) * 3; +} + +static void demo_prepare_descriptor_layout(struct demo *demo) { + const VkDescriptorSetLayoutBinding layout_binding = { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = DEMO_TEXTURE_COUNT, + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, + .pImmutableSamplers = NULL, + }; + const VkDescriptorSetLayoutCreateInfo descriptor_layout = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .pNext = NULL, + .bindingCount = 1, + .pBindings = &layout_binding, + }; + VkResult U_ASSERT_ONLY err; + + err = vkCreateDescriptorSetLayout(demo->device, &descriptor_layout, NULL, + &demo->desc_layout); + assert(!err); + + const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = { + .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, + .pNext = NULL, + .setLayoutCount = 1, + .pSetLayouts = &demo->desc_layout, + }; + + err = vkCreatePipelineLayout(demo->device, &pPipelineLayoutCreateInfo, NULL, + &demo->pipeline_layout); + assert(!err); +} + +static void demo_prepare_render_pass(struct demo *demo) { + const VkAttachmentDescription attachments[2] = { + [0] = + { + .format = demo->format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_STORE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }, + [1] = + { + .format = demo->depth.format, + .samples = VK_SAMPLE_COUNT_1_BIT, + .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR, + .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE, + .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE, + .initialLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + .finalLayout = + VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + }, + }; + const VkAttachmentReference color_reference = { + .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, + }; + const VkAttachmentReference depth_reference = { + .attachment = 1, + .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + }; + const VkSubpassDescription subpass = { + .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, + .flags = 0, + .inputAttachmentCount = 0, + .pInputAttachments = NULL, + .colorAttachmentCount = 1, + .pColorAttachments = &color_reference, + .pResolveAttachments = NULL, + .pDepthStencilAttachment = &depth_reference, + .preserveAttachmentCount = 0, + .pPreserveAttachments = NULL, + }; + const VkRenderPassCreateInfo rp_info = { + .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, + .pNext = NULL, + .attachmentCount = 2, + .pAttachments = attachments, + .subpassCount = 1, + .pSubpasses = &subpass, + .dependencyCount = 0, + .pDependencies = NULL, + }; + VkResult U_ASSERT_ONLY err; + + err = vkCreateRenderPass(demo->device, &rp_info, NULL, &demo->render_pass); + assert(!err); +} + +static VkShaderModule +demo_prepare_shader_module(struct demo *demo, const void *code, size_t size) { + VkShaderModuleCreateInfo moduleCreateInfo; + VkShaderModule module; + VkResult U_ASSERT_ONLY err; + + moduleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + moduleCreateInfo.pNext = NULL; + + moduleCreateInfo.codeSize = size; + moduleCreateInfo.pCode = code; + moduleCreateInfo.flags = 0; + err = vkCreateShaderModule(demo->device, &moduleCreateInfo, NULL, &module); + assert(!err); + + return module; +} + +static VkShaderModule demo_prepare_vs(struct demo *demo) { + size_t size = sizeof(vertShaderCode); + + demo->vert_shader_module = + demo_prepare_shader_module(demo, vertShaderCode, size); + + return demo->vert_shader_module; +} + +static VkShaderModule demo_prepare_fs(struct demo *demo) { + size_t size = sizeof(fragShaderCode); + + demo->frag_shader_module = + demo_prepare_shader_module(demo, fragShaderCode, size); + + return demo->frag_shader_module; +} + +static void demo_prepare_pipeline(struct demo *demo) { + VkGraphicsPipelineCreateInfo pipeline; + VkPipelineCacheCreateInfo pipelineCache; + + VkPipelineVertexInputStateCreateInfo vi; + VkPipelineInputAssemblyStateCreateInfo ia; + VkPipelineRasterizationStateCreateInfo rs; + VkPipelineColorBlendStateCreateInfo cb; + VkPipelineDepthStencilStateCreateInfo ds; + VkPipelineViewportStateCreateInfo vp; + VkPipelineMultisampleStateCreateInfo ms; + VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE]; + VkPipelineDynamicStateCreateInfo dynamicState; + + VkResult U_ASSERT_ONLY err; + + memset(dynamicStateEnables, 0, sizeof dynamicStateEnables); + memset(&dynamicState, 0, sizeof dynamicState); + dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamicState.pDynamicStates = dynamicStateEnables; + + memset(&pipeline, 0, sizeof(pipeline)); + pipeline.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline.layout = demo->pipeline_layout; + + vi = demo->vertices.vi; + + memset(&ia, 0, sizeof(ia)); + ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + memset(&rs, 0, sizeof(rs)); + rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rs.polygonMode = VK_POLYGON_MODE_FILL; + rs.cullMode = VK_CULL_MODE_BACK_BIT; + rs.frontFace = VK_FRONT_FACE_CLOCKWISE; + rs.depthClampEnable = VK_FALSE; + rs.rasterizerDiscardEnable = VK_FALSE; + rs.depthBiasEnable = VK_FALSE; + + memset(&cb, 0, sizeof(cb)); + cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + VkPipelineColorBlendAttachmentState att_state[1]; + memset(att_state, 0, sizeof(att_state)); + att_state[0].colorWriteMask = 0xf; + att_state[0].blendEnable = VK_FALSE; + cb.attachmentCount = 1; + cb.pAttachments = att_state; + + memset(&vp, 0, sizeof(vp)); + vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + vp.viewportCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = + VK_DYNAMIC_STATE_VIEWPORT; + vp.scissorCount = 1; + dynamicStateEnables[dynamicState.dynamicStateCount++] = + VK_DYNAMIC_STATE_SCISSOR; + + memset(&ds, 0, sizeof(ds)); + ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + ds.depthTestEnable = VK_TRUE; + ds.depthWriteEnable = VK_TRUE; + ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; + ds.depthBoundsTestEnable = VK_FALSE; + ds.back.failOp = VK_STENCIL_OP_KEEP; + ds.back.passOp = VK_STENCIL_OP_KEEP; + ds.back.compareOp = VK_COMPARE_OP_ALWAYS; + ds.stencilTestEnable = VK_FALSE; + ds.front = ds.back; + + memset(&ms, 0, sizeof(ms)); + ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms.pSampleMask = NULL; + ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + // Two stages: vs and fs + pipeline.stageCount = 2; + VkPipelineShaderStageCreateInfo shaderStages[2]; + memset(&shaderStages, 0, 2 * sizeof(VkPipelineShaderStageCreateInfo)); + + shaderStages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + shaderStages[0].module = demo_prepare_vs(demo); + shaderStages[0].pName = "main"; + + shaderStages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shaderStages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shaderStages[1].module = demo_prepare_fs(demo); + shaderStages[1].pName = "main"; + + pipeline.pVertexInputState = &vi; + pipeline.pInputAssemblyState = &ia; + pipeline.pRasterizationState = &rs; + pipeline.pColorBlendState = &cb; + pipeline.pMultisampleState = &ms; + pipeline.pViewportState = &vp; + pipeline.pDepthStencilState = &ds; + pipeline.pStages = shaderStages; + pipeline.renderPass = demo->render_pass; + pipeline.pDynamicState = &dynamicState; + + memset(&pipelineCache, 0, sizeof(pipelineCache)); + pipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + + err = vkCreatePipelineCache(demo->device, &pipelineCache, NULL, + &demo->pipelineCache); + assert(!err); + err = vkCreateGraphicsPipelines(demo->device, demo->pipelineCache, 1, + &pipeline, NULL, &demo->pipeline); + assert(!err); + + vkDestroyPipelineCache(demo->device, demo->pipelineCache, NULL); + + vkDestroyShaderModule(demo->device, demo->frag_shader_module, NULL); + vkDestroyShaderModule(demo->device, demo->vert_shader_module, NULL); +} + +static void demo_prepare_descriptor_pool(struct demo *demo) { + const VkDescriptorPoolSize type_count = { + .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, + .descriptorCount = DEMO_TEXTURE_COUNT, + }; + const VkDescriptorPoolCreateInfo descriptor_pool = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .pNext = NULL, + .maxSets = 1, + .poolSizeCount = 1, + .pPoolSizes = &type_count, + }; + VkResult U_ASSERT_ONLY err; + + err = vkCreateDescriptorPool(demo->device, &descriptor_pool, NULL, + &demo->desc_pool); + assert(!err); +} + +static void demo_prepare_descriptor_set(struct demo *demo) { + VkDescriptorImageInfo tex_descs[DEMO_TEXTURE_COUNT]; + VkWriteDescriptorSet write; + VkResult U_ASSERT_ONLY err; + uint32_t i; + + VkDescriptorSetAllocateInfo alloc_info = { + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .pNext = NULL, + .descriptorPool = demo->desc_pool, + .descriptorSetCount = 1, + .pSetLayouts = &demo->desc_layout}; + err = vkAllocateDescriptorSets(demo->device, &alloc_info, &demo->desc_set); + assert(!err); + + memset(&tex_descs, 0, sizeof(tex_descs)); + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + tex_descs[i].sampler = demo->textures[i].sampler; + tex_descs[i].imageView = demo->textures[i].view; + tex_descs[i].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + memset(&write, 0, sizeof(write)); + write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write.dstSet = demo->desc_set; + write.descriptorCount = DEMO_TEXTURE_COUNT; + write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write.pImageInfo = tex_descs; + + vkUpdateDescriptorSets(demo->device, 1, &write, 0, NULL); +} + +static void demo_prepare_framebuffers(struct demo *demo) { + VkImageView attachments[2]; + attachments[1] = demo->depth.view; + + const VkFramebufferCreateInfo fb_info = { + .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, + .pNext = NULL, + .renderPass = demo->render_pass, + .attachmentCount = 2, + .pAttachments = attachments, + .width = demo->width, + .height = demo->height, + .layers = 1, + }; + VkResult U_ASSERT_ONLY err; + uint32_t i; + + demo->framebuffers = (VkFramebuffer *)malloc(demo->swapchainImageCount * + sizeof(VkFramebuffer)); + assert(demo->framebuffers); + + for (i = 0; i < demo->swapchainImageCount; i++) { + attachments[0] = demo->buffers[i].view; + err = vkCreateFramebuffer(demo->device, &fb_info, NULL, + &demo->framebuffers[i]); + assert(!err); + } +} + +static void demo_prepare(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + + const VkCommandPoolCreateInfo cmd_pool_info = { + .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, + .pNext = NULL, + .queueFamilyIndex = demo->graphics_queue_node_index, + .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, + }; + err = vkCreateCommandPool(demo->device, &cmd_pool_info, NULL, + &demo->cmd_pool); + assert(!err); + + const VkCommandBufferAllocateInfo cmd = { + .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, + .pNext = NULL, + .commandPool = demo->cmd_pool, + .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY, + .commandBufferCount = 1, + }; + err = vkAllocateCommandBuffers(demo->device, &cmd, &demo->draw_cmd); + assert(!err); + + demo_prepare_buffers(demo); + demo_prepare_depth(demo); + demo_prepare_textures(demo); + demo_prepare_vertices(demo); + demo_prepare_descriptor_layout(demo); + demo_prepare_render_pass(demo); + demo_prepare_pipeline(demo); + + demo_prepare_descriptor_pool(demo); + demo_prepare_descriptor_set(demo); + + demo_prepare_framebuffers(demo); +} + +static void demo_error_callback(int error, const char* description) { + printf("GLFW error: %s\n", description); + fflush(stdout); +} + +static void demo_key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { + if (key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE) + glfwSetWindowShouldClose(window, GLFW_TRUE); +} + +static void demo_refresh_callback(GLFWwindow* window) { + struct demo* demo = glfwGetWindowUserPointer(window); + demo_draw(demo); +} + +static void demo_resize_callback(GLFWwindow* window, int width, int height) { + struct demo* demo = glfwGetWindowUserPointer(window); + demo->width = width; + demo->height = height; + demo_resize(demo); +} + +static void demo_run(struct demo *demo) { + while (!glfwWindowShouldClose(demo->window)) { + glfwPollEvents(); + + demo_draw(demo); + + if (demo->depthStencil > 0.99f) + demo->depthIncrement = -0.001f; + if (demo->depthStencil < 0.8f) + demo->depthIncrement = 0.001f; + + demo->depthStencil += demo->depthIncrement; + + // Wait for work to finish before updating MVP. + vkDeviceWaitIdle(demo->device); + } +} + +static void demo_create_window(struct demo *demo) { + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + + demo->window = glfwCreateWindow(demo->width, + demo->height, + APP_LONG_NAME, + NULL, + NULL); + if (!demo->window) { + // It didn't work, so try to give a useful error: + printf("Cannot create a window in which to draw!\n"); + fflush(stdout); + exit(1); + } + + glfwSetWindowUserPointer(demo->window, demo); + glfwSetWindowRefreshCallback(demo->window, demo_refresh_callback); + glfwSetFramebufferSizeCallback(demo->window, demo_resize_callback); + glfwSetKeyCallback(demo->window, demo_key_callback); +} + +/* + * Return 1 (true) if all layer names specified in check_names + * can be found in given layer properties. + */ +static VkBool32 demo_check_layers(uint32_t check_count, char **check_names, + uint32_t layer_count, + VkLayerProperties *layers) { + uint32_t i, j; + for (i = 0; i < check_count; i++) { + VkBool32 found = 0; + for (j = 0; j < layer_count; j++) { + if (!strcmp(check_names[i], layers[j].layerName)) { + found = 1; + break; + } + } + if (!found) { + fprintf(stderr, "Cannot find layer: %s\n", check_names[i]); + return 0; + } + } + return 1; +} + +VKAPI_ATTR void *VKAPI_CALL myrealloc(void *pUserData, void *pOriginal, + size_t size, size_t alignment, + VkSystemAllocationScope allocationScope) { + return realloc(pOriginal, size); +} + +VKAPI_ATTR void *VKAPI_CALL myalloc(void *pUserData, size_t size, + size_t alignment, + VkSystemAllocationScope allocationScope) { +#ifdef _MSC_VER + return _aligned_malloc(size, alignment); +#else + return aligned_alloc(alignment, size); +#endif +} + +VKAPI_ATTR void VKAPI_CALL myfree(void *pUserData, void *pMemory) { +#ifdef _MSC_VER + _aligned_free(pMemory); +#else + free(pMemory); +#endif +} + +static void demo_init_vk(struct demo *demo) { + VkResult err; + uint32_t required_extension_count; + const char** required_extensions; + uint32_t i; + uint32_t instance_extension_count = 0; + uint32_t instance_layer_count = 0; + uint32_t device_validation_layer_count = 0; + demo->enabled_extension_count = 0; + demo->enabled_layer_count = 0; + + char *instance_validation_layers[] = { + "VK_LAYER_LUNARG_mem_tracker", + "VK_LAYER_GOOGLE_unique_objects", + }; + + demo->device_validation_layers[0] = "VK_LAYER_LUNARG_mem_tracker"; + demo->device_validation_layers[1] = "VK_LAYER_GOOGLE_unique_objects"; + device_validation_layer_count = 2; + + /* Look for validation layers */ + VkBool32 validation_found = 0; + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, NULL); + assert(!err); + + if (instance_layer_count > 0) { + VkLayerProperties *instance_layers = + malloc(sizeof(VkLayerProperties) * instance_layer_count); + err = vkEnumerateInstanceLayerProperties(&instance_layer_count, + instance_layers); + assert(!err); + + if (demo->validate) { + validation_found = demo_check_layers( + ARRAY_SIZE(instance_validation_layers), + instance_validation_layers, instance_layer_count, + instance_layers); + demo->enabled_layer_count = ARRAY_SIZE(instance_validation_layers); + } + + free(instance_layers); + } + + if (demo->validate && !validation_found) { + ERR_EXIT("vkEnumerateInstanceLayerProperties failed to find" + "required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + + /* Look for instance extensions */ + required_extensions = glfwGetRequiredInstanceExtensions(&required_extension_count); + if (!required_extensions) { + ERR_EXIT("glfwGetRequiredInstanceExtensions failed to find the " + "platform surface extensions.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + + for (i = 0; i < required_extension_count; i++) { + demo->extension_names[demo->enabled_extension_count++] = required_extensions[i]; + assert(demo->enabled_extension_count < 64); + } + + err = vkEnumerateInstanceExtensionProperties( + NULL, &instance_extension_count, NULL); + assert(!err); + + if (instance_extension_count > 0) { + VkExtensionProperties *instance_extensions = + malloc(sizeof(VkExtensionProperties) * instance_extension_count); + err = vkEnumerateInstanceExtensionProperties( + NULL, &instance_extension_count, instance_extensions); + assert(!err); + for (i = 0; i < instance_extension_count; i++) { + if (!strcmp(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, + instance_extensions[i].extensionName)) { + if (demo->validate) { + demo->extension_names[demo->enabled_extension_count++] = + VK_EXT_DEBUG_REPORT_EXTENSION_NAME; + } + } + assert(demo->enabled_extension_count < 64); + } + + free(instance_extensions); + } + + const VkApplicationInfo app = { + .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO, + .pNext = NULL, + .pApplicationName = APP_SHORT_NAME, + .applicationVersion = 0, + .pEngineName = APP_SHORT_NAME, + .engineVersion = 0, + .apiVersion = VK_API_VERSION_1_0, + }; + VkInstanceCreateInfo inst_info = { + .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, + .pNext = NULL, + .pApplicationInfo = &app, + .enabledLayerCount = demo->enabled_layer_count, + .ppEnabledLayerNames = (const char *const *)instance_validation_layers, + .enabledExtensionCount = demo->enabled_extension_count, + .ppEnabledExtensionNames = (const char *const *)demo->extension_names, + }; + + uint32_t gpu_count; + + demo->allocator.pfnAllocation = myalloc; + demo->allocator.pfnFree = myfree; + demo->allocator.pfnReallocation = myrealloc; + + err = vkCreateInstance(&inst_info, &demo->allocator, &demo->inst); + if (err == VK_ERROR_INCOMPATIBLE_DRIVER) { + ERR_EXIT("Cannot find a compatible Vulkan installable client driver " + "(ICD).\n\nPlease look at the Getting Started guide for " + "additional information.\n", + "vkCreateInstance Failure"); + } else if (err == VK_ERROR_EXTENSION_NOT_PRESENT) { + ERR_EXIT("Cannot find a specified extension library" + ".\nMake sure your layers path is set appropriately\n", + "vkCreateInstance Failure"); + } else if (err) { + ERR_EXIT("vkCreateInstance failed.\n\nDo you have a compatible Vulkan " + "installable client driver (ICD) installed?\nPlease look at " + "the Getting Started guide for additional information.\n", + "vkCreateInstance Failure"); + } + + /* Make initial call to query gpu_count, then second call for gpu info*/ + err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, NULL); + assert(!err && gpu_count > 0); + + if (gpu_count > 0) { + VkPhysicalDevice *physical_devices = + malloc(sizeof(VkPhysicalDevice) * gpu_count); + err = vkEnumeratePhysicalDevices(demo->inst, &gpu_count, + physical_devices); + assert(!err); + /* For tri demo we just grab the first physical device */ + demo->gpu = physical_devices[0]; + free(physical_devices); + } else { + ERR_EXIT("vkEnumeratePhysicalDevices reported zero accessible devices." + "\n\nDo you have a compatible Vulkan installable client" + " driver (ICD) installed?\nPlease look at the Getting Started" + " guide for additional information.\n", + "vkEnumeratePhysicalDevices Failure"); + } + + /* Look for validation layers */ + validation_found = 0; + demo->enabled_layer_count = 0; + uint32_t device_layer_count = 0; + err = + vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, NULL); + assert(!err); + + if (device_layer_count > 0) { + VkLayerProperties *device_layers = + malloc(sizeof(VkLayerProperties) * device_layer_count); + err = vkEnumerateDeviceLayerProperties(demo->gpu, &device_layer_count, + device_layers); + assert(!err); + + if (demo->validate) { + validation_found = demo_check_layers(device_validation_layer_count, + demo->device_validation_layers, + device_layer_count, + device_layers); + demo->enabled_layer_count = device_validation_layer_count; + } + + free(device_layers); + } + + if (demo->validate && !validation_found) { + ERR_EXIT("vkEnumerateDeviceLayerProperties failed to find " + "a required validation layer.\n\n" + "Please look at the Getting Started guide for additional " + "information.\n", + "vkCreateDevice Failure"); + } + + /* Look for device extensions */ + uint32_t device_extension_count = 0; + VkBool32 swapchainExtFound = 0; + demo->enabled_extension_count = 0; + + err = vkEnumerateDeviceExtensionProperties(demo->gpu, NULL, + &device_extension_count, NULL); + assert(!err); + + if (device_extension_count > 0) { + VkExtensionProperties *device_extensions = + malloc(sizeof(VkExtensionProperties) * device_extension_count); + err = vkEnumerateDeviceExtensionProperties( + demo->gpu, NULL, &device_extension_count, device_extensions); + assert(!err); + + for (i = 0; i < device_extension_count; i++) { + if (!strcmp(VK_KHR_SWAPCHAIN_EXTENSION_NAME, + device_extensions[i].extensionName)) { + swapchainExtFound = 1; + demo->extension_names[demo->enabled_extension_count++] = + VK_KHR_SWAPCHAIN_EXTENSION_NAME; + } + assert(demo->enabled_extension_count < 64); + } + + free(device_extensions); + } + + if (!swapchainExtFound) { + ERR_EXIT("vkEnumerateDeviceExtensionProperties failed to find " + "the " VK_KHR_SWAPCHAIN_EXTENSION_NAME + " extension.\n\nDo you have a compatible " + "Vulkan installable client driver (ICD) installed?\nPlease " + "look at the Getting Started guide for additional " + "information.\n", + "vkCreateInstance Failure"); + } + + if (demo->validate) { + demo->CreateDebugReportCallback = + (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( + demo->inst, "vkCreateDebugReportCallbackEXT"); + if (!demo->CreateDebugReportCallback) { + ERR_EXIT( + "GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT\n", + "vkGetProcAddr Failure"); + } + VkDebugReportCallbackCreateInfoEXT dbgCreateInfo; + dbgCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; + dbgCreateInfo.flags = + VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; + dbgCreateInfo.pfnCallback = dbgFunc; + dbgCreateInfo.pUserData = NULL; + dbgCreateInfo.pNext = NULL; + err = demo->CreateDebugReportCallback(demo->inst, &dbgCreateInfo, NULL, + &demo->msg_callback); + switch (err) { + case VK_SUCCESS: + break; + case VK_ERROR_OUT_OF_HOST_MEMORY: + ERR_EXIT("CreateDebugReportCallback: out of host memory\n", + "CreateDebugReportCallback Failure"); + break; + default: + ERR_EXIT("CreateDebugReportCallback: unknown failure\n", + "CreateDebugReportCallback Failure"); + break; + } + } + + // Having these GIPA queries of device extension entry points both + // BEFORE and AFTER vkCreateDevice is a good test for the loader + GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceFormatsKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfacePresentModesKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, GetPhysicalDeviceSurfaceSupportKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, CreateSwapchainKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, DestroySwapchainKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, GetSwapchainImagesKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, AcquireNextImageKHR); + GET_INSTANCE_PROC_ADDR(demo->inst, QueuePresentKHR); + + vkGetPhysicalDeviceProperties(demo->gpu, &demo->gpu_props); + + // Query with NULL data to get count + vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, + NULL); + + demo->queue_props = (VkQueueFamilyProperties *)malloc( + demo->queue_count * sizeof(VkQueueFamilyProperties)); + vkGetPhysicalDeviceQueueFamilyProperties(demo->gpu, &demo->queue_count, + demo->queue_props); + assert(demo->queue_count >= 1); + + // Graphics queue and MemMgr queue can be separate. + // TODO: Add support for separate queues, including synchronization, + // and appropriate tracking for QueueSubmit +} + +static void demo_init_device(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + + float queue_priorities[1] = {0.0}; + const VkDeviceQueueCreateInfo queue = { + .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO, + .pNext = NULL, + .queueFamilyIndex = demo->graphics_queue_node_index, + .queueCount = 1, + .pQueuePriorities = queue_priorities}; + + VkDeviceCreateInfo device = { + .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, + .pNext = NULL, + .queueCreateInfoCount = 1, + .pQueueCreateInfos = &queue, + .enabledLayerCount = demo->enabled_layer_count, + .ppEnabledLayerNames = + (const char *const *)((demo->validate) + ? demo->device_validation_layers + : NULL), + .enabledExtensionCount = demo->enabled_extension_count, + .ppEnabledExtensionNames = (const char *const *)demo->extension_names, + }; + + err = vkCreateDevice(demo->gpu, &device, NULL, &demo->device); + assert(!err); + + GET_DEVICE_PROC_ADDR(demo->device, CreateSwapchainKHR); + GET_DEVICE_PROC_ADDR(demo->device, DestroySwapchainKHR); + GET_DEVICE_PROC_ADDR(demo->device, GetSwapchainImagesKHR); + GET_DEVICE_PROC_ADDR(demo->device, AcquireNextImageKHR); + GET_DEVICE_PROC_ADDR(demo->device, QueuePresentKHR); +} + +static void demo_init_vk_swapchain(struct demo *demo) { + VkResult U_ASSERT_ONLY err; + uint32_t i; + + // Create a WSI surface for the window: + glfwCreateWindowSurface(demo->inst, demo->window, NULL, &demo->surface); + + // Iterate over each queue to learn whether it supports presenting: + VkBool32 *supportsPresent = + (VkBool32 *)malloc(demo->queue_count * sizeof(VkBool32)); + for (i = 0; i < demo->queue_count; i++) { + demo->fpGetPhysicalDeviceSurfaceSupportKHR(demo->gpu, i, demo->surface, + &supportsPresent[i]); + } + + // Search for a graphics and a present queue in the array of queue + // families, try to find one that supports both + uint32_t graphicsQueueNodeIndex = UINT32_MAX; + uint32_t presentQueueNodeIndex = UINT32_MAX; + for (i = 0; i < demo->queue_count; i++) { + if ((demo->queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { + if (graphicsQueueNodeIndex == UINT32_MAX) { + graphicsQueueNodeIndex = i; + } + + if (supportsPresent[i] == VK_TRUE) { + graphicsQueueNodeIndex = i; + presentQueueNodeIndex = i; + break; + } + } + } + if (presentQueueNodeIndex == UINT32_MAX) { + // If didn't find a queue that supports both graphics and present, then + // find a separate present queue. + for (i = 0; i < demo->queue_count; ++i) { + if (supportsPresent[i] == VK_TRUE) { + presentQueueNodeIndex = i; + break; + } + } + } + free(supportsPresent); + + // Generate error if could not find both a graphics and a present queue + if (graphicsQueueNodeIndex == UINT32_MAX || + presentQueueNodeIndex == UINT32_MAX) { + ERR_EXIT("Could not find a graphics and a present queue\n", + "Swapchain Initialization Failure"); + } + + // TODO: Add support for separate queues, including presentation, + // synchronization, and appropriate tracking for QueueSubmit. + // NOTE: While it is possible for an application to use a separate graphics + // and a present queues, this demo program assumes it is only using + // one: + if (graphicsQueueNodeIndex != presentQueueNodeIndex) { + ERR_EXIT("Could not find a common graphics and a present queue\n", + "Swapchain Initialization Failure"); + } + + demo->graphics_queue_node_index = graphicsQueueNodeIndex; + + demo_init_device(demo); + + vkGetDeviceQueue(demo->device, demo->graphics_queue_node_index, 0, + &demo->queue); + + // Get the list of VkFormat's that are supported: + uint32_t formatCount; + err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, + &formatCount, NULL); + assert(!err); + VkSurfaceFormatKHR *surfFormats = + (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); + err = demo->fpGetPhysicalDeviceSurfaceFormatsKHR(demo->gpu, demo->surface, + &formatCount, surfFormats); + assert(!err); + // If the format list includes just one entry of VK_FORMAT_UNDEFINED, + // the surface has no preferred format. Otherwise, at least one + // supported format will be returned. + if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { + demo->format = VK_FORMAT_B8G8R8A8_UNORM; + } else { + assert(formatCount >= 1); + demo->format = surfFormats[0].format; + } + demo->color_space = surfFormats[0].colorSpace; + + // Get Memory information and properties + vkGetPhysicalDeviceMemoryProperties(demo->gpu, &demo->memory_properties); +} + +static void demo_init_connection(struct demo *demo) { + glfwSetErrorCallback(demo_error_callback); + + if (!glfwInit()) { + printf("Cannot initialize GLFW.\nExiting ...\n"); + fflush(stdout); + exit(1); + } + + if (!glfwVulkanSupported()) { + printf("GLFW failed to find the Vulkan loader.\nExiting ...\n"); + fflush(stdout); + exit(1); + } +} + +static void demo_init(struct demo *demo, const int argc, const char *argv[]) +{ + int i; + + memset(demo, 0, sizeof(*demo)); + + for (i = 0; i < argc; i++) { + if (strncmp(argv[i], "--use_staging", strlen("--use_staging")) == 0) + demo->use_staging_buffer = true; + } + + demo_init_connection(demo); + demo_init_vk(demo); + + demo->width = 300; + demo->height = 300; + demo->depthStencil = 1.0; + demo->depthIncrement = -0.01f; +} + +static void demo_cleanup(struct demo *demo) { + uint32_t i; + + for (i = 0; i < demo->swapchainImageCount; i++) { + vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); + } + free(demo->framebuffers); + vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); + + if (demo->setup_cmd) { + vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd); + } + vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd); + vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); + + vkDestroyPipeline(demo->device, demo->pipeline, NULL); + vkDestroyRenderPass(demo->device, demo->render_pass, NULL); + vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); + + vkDestroyBuffer(demo->device, demo->vertices.buf, NULL); + vkFreeMemory(demo->device, demo->vertices.mem, NULL); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + vkDestroyImageView(demo->device, demo->textures[i].view, NULL); + vkDestroyImage(demo->device, demo->textures[i].image, NULL); + vkFreeMemory(demo->device, demo->textures[i].mem, NULL); + vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); + } + + for (i = 0; i < demo->swapchainImageCount; i++) { + vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); + } + + vkDestroyImageView(demo->device, demo->depth.view, NULL); + vkDestroyImage(demo->device, demo->depth.image, NULL); + vkFreeMemory(demo->device, demo->depth.mem, NULL); + + demo->fpDestroySwapchainKHR(demo->device, demo->swapchain, NULL); + free(demo->buffers); + + vkDestroyDevice(demo->device, NULL); + vkDestroySurfaceKHR(demo->inst, demo->surface, NULL); + vkDestroyInstance(demo->inst, &demo->allocator); + + free(demo->queue_props); + + glfwDestroyWindow(demo->window); + glfwTerminate(); +} + +static void demo_resize(struct demo *demo) { + uint32_t i; + + for (i = 0; i < demo->swapchainImageCount; i++) { + vkDestroyFramebuffer(demo->device, demo->framebuffers[i], NULL); + } + free(demo->framebuffers); + vkDestroyDescriptorPool(demo->device, demo->desc_pool, NULL); + + if (demo->setup_cmd) { + vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->setup_cmd); + } + vkFreeCommandBuffers(demo->device, demo->cmd_pool, 1, &demo->draw_cmd); + vkDestroyCommandPool(demo->device, demo->cmd_pool, NULL); + + vkDestroyPipeline(demo->device, demo->pipeline, NULL); + vkDestroyRenderPass(demo->device, demo->render_pass, NULL); + vkDestroyPipelineLayout(demo->device, demo->pipeline_layout, NULL); + vkDestroyDescriptorSetLayout(demo->device, demo->desc_layout, NULL); + + vkDestroyBuffer(demo->device, demo->vertices.buf, NULL); + vkFreeMemory(demo->device, demo->vertices.mem, NULL); + + for (i = 0; i < DEMO_TEXTURE_COUNT; i++) { + vkDestroyImageView(demo->device, demo->textures[i].view, NULL); + vkDestroyImage(demo->device, demo->textures[i].image, NULL); + vkFreeMemory(demo->device, demo->textures[i].mem, NULL); + vkDestroySampler(demo->device, demo->textures[i].sampler, NULL); + } + + for (i = 0; i < demo->swapchainImageCount; i++) { + vkDestroyImageView(demo->device, demo->buffers[i].view, NULL); + } + + vkDestroyImageView(demo->device, demo->depth.view, NULL); + vkDestroyImage(demo->device, demo->depth.image, NULL); + vkFreeMemory(demo->device, demo->depth.mem, NULL); + + free(demo->buffers); + + // Second, re-perform the demo_prepare() function, which will re-create the + // swapchain: + demo_prepare(demo); +} + +int main(const int argc, const char *argv[]) { + struct demo demo; + + demo_init(&demo, argc, argv); + demo_create_window(&demo); + demo_init_vk_swapchain(&demo); + + demo_prepare(&demo); + demo_run(&demo); + + demo_cleanup(&demo); + + return 0; +} + diff --git a/apps/exampleViewer/common/glfw/tests/windows.c b/apps/exampleViewer/common/glfw/tests/windows.c new file mode 100644 index 0000000000..025cce2946 --- /dev/null +++ b/apps/exampleViewer/common/glfw/tests/windows.c @@ -0,0 +1,166 @@ +//======================================================================== +// Simple multi-window test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test creates four windows and clears each in a different color +// +//======================================================================== + +#include +#include + +#include +#include + +#include "getopt.h" + +static const char* titles[] = +{ + "Red", + "Green", + "Blue", + "Yellow" +}; + +static const struct +{ + float r, g, b; +} colors[] = +{ + { 0.95f, 0.32f, 0.11f }, + { 0.50f, 0.80f, 0.16f }, + { 0.f, 0.68f, 0.94f }, + { 0.98f, 0.74f, 0.04f } +}; + +static void usage(void) +{ + printf("Usage: windows [-h] [-b]\n"); + printf("Options:\n"); + printf(" -b create decorated windows\n"); + printf(" -h show this help\n"); +} + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_SPACE: + { + int xpos, ypos; + glfwGetWindowPos(window, &xpos, &ypos); + glfwSetWindowPos(window, xpos, ypos); + break; + } + + case GLFW_KEY_ESCAPE: + glfwSetWindowShouldClose(window, GLFW_TRUE); + break; + } +} + +int main(int argc, char** argv) +{ + int i, ch; + int decorated = GLFW_FALSE; + int running = GLFW_TRUE; + GLFWwindow* windows[4]; + + while ((ch = getopt(argc, argv, "bh")) != -1) + { + switch (ch) + { + case 'b': + decorated = GLFW_TRUE; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + default: + usage(); + exit(EXIT_FAILURE); + } + } + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + glfwWindowHint(GLFW_DECORATED, decorated); + glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); + + for (i = 0; i < 4; i++) + { + int left, top, right, bottom; + + windows[i] = glfwCreateWindow(200, 200, titles[i], NULL, NULL); + if (!windows[i]) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glfwSetKeyCallback(windows[i], key_callback); + + glfwMakeContextCurrent(windows[i]); + gladLoadGLLoader((GLADloadproc) glfwGetProcAddress); + glClearColor(colors[i].r, colors[i].g, colors[i].b, 1.f); + + glfwGetWindowFrameSize(windows[i], &left, &top, &right, &bottom); + glfwSetWindowPos(windows[i], + 100 + (i & 1) * (200 + left + right), + 100 + (i >> 1) * (200 + top + bottom)); + } + + for (i = 0; i < 4; i++) + glfwShowWindow(windows[i]); + + while (running) + { + for (i = 0; i < 4; i++) + { + glfwMakeContextCurrent(windows[i]); + glClear(GL_COLOR_BUFFER_BIT); + glfwSwapBuffers(windows[i]); + + if (glfwWindowShouldClose(windows[i])) + running = GLFW_FALSE; + } + + glfwWaitEvents(); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} + diff --git a/apps/common/widgets/CMakeLists.txt b/apps/exampleViewer/common/imgui/CMakeLists.txt similarity index 77% rename from apps/common/widgets/CMakeLists.txt rename to apps/exampleViewer/common/imgui/CMakeLists.txt index 0b33f4e607..ddc046dd1a 100644 --- a/apps/common/widgets/CMakeLists.txt +++ b/apps/exampleViewer/common/imgui/CMakeLists.txt @@ -1,5 +1,5 @@ ## ======================================================================== ## -## Copyright 2009-2017 Intel Corporation ## +## Copyright 2009-2015 Intel Corporation ## ## ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## ## you may not use this file except in compliance with the License. ## @@ -14,18 +14,19 @@ ## limitations under the License. ## ## ======================================================================== ## -IF (OSPRAY_MODULE_DISPLAY_WALD) - INCLUDE_DIRECTORIES(${DISPLAY_WALD_INCLUDE_DIR}) - ADD_DEFINITIONS(-DOSPRAY_DISPLAY_WALD=1) -ENDIF() -OSPRAY_CREATE_LIBRARY(ospray_glut3d - Glut3dExport.h - glut3D.cpp - OSPGlutViewer.cpp -LINK - ospray - ospray_common +############################################################## +# Build imgui +############################################################## + +set(LIB_NAME imgui) + +add_library(${LIB_NAME} STATIC + imgui.cpp + imgui_draw.cpp + imgui_demo.cpp +) + +target_link_libraries(${LIB_NAME} ${OPENGL_LIBRARIES} - ${GLUT_LIBRARIES} ) diff --git a/apps/exampleViewer/common/imgui/LibreBaskerville-Regular.ttf b/apps/exampleViewer/common/imgui/LibreBaskerville-Regular.ttf new file mode 100755 index 0000000000..57e3c23b52 Binary files /dev/null and b/apps/exampleViewer/common/imgui/LibreBaskerville-Regular.ttf differ diff --git a/apps/exampleViewer/common/imgui/imconfig.h b/apps/exampleViewer/common/imgui/imconfig.h new file mode 100644 index 0000000000..af479dceca --- /dev/null +++ b/apps/exampleViewer/common/imgui/imconfig.h @@ -0,0 +1,65 @@ +//----------------------------------------------------------------------------- +// USER IMPLEMENTATION +// This file contains compile-time options for ImGui. +// Other options (memory allocation overrides, callbacks, etc.) can be set at runtime via the ImGuiIO structure - ImGui::GetIO(). +//----------------------------------------------------------------------------- + +#pragma once + +#include + +//---- Define assertion handler. Defaults to calling assert(). +//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) + +//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. +#if 0 +# ifdef _WIN32 +# ifdef imgui_EXPORTS +# define IMGUI_API __declspec(dllexport) +# else +# define IMGUI_API __declspec(dllimport) +# endif +# else +# define IMGUI_API +# endif +#else +# define IMGUI_API +#endif + +//---- Include imgui_user.h at the end of imgui.h +//#define IMGUI_INCLUDE_IMGUI_USER_H + +//---- Don't implement default handlers for Windows (so as not to link with OpenClipboard() and others Win32 functions) +//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS +//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS + +//---- Don't implement help and test window functionality (ShowUserGuide()/ShowStyleEditor()/ShowTestWindow() methods will be empty) +//#define IMGUI_DISABLE_TEST_WINDOWS + +//---- Don't define obsolete functions names +//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS + +//---- Pack colors to BGRA instead of RGBA (remove need to post process vertex buffer in back ends) +//#define IMGUI_USE_BGRA_PACKED_COLOR + +//---- Implement STB libraries in a namespace to avoid conflicts +//#define IMGUI_STB_NAMESPACE ImGuiStb + +//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. +#define IM_VEC2_CLASS_EXTRA \ + ImVec2(const ospcommon::vec2f& f) { x = f.x; y = f.y; } \ + operator ospcommon::vec2f() const { return ospcommon::vec2f(x,y); } + +#define IM_VEC4_CLASS_EXTRA \ + ImVec4(const ospcommon::vec4f& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ + operator ospcommon::vec4f() const { return ospcommon::vec4f(x,y,z,w); } + +//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. +//---- e.g. create variants of the ImGui::Value() helper for your low-level math types, or your own widgets/helpers. +/* +namespace ImGui +{ + void Value(const char* prefix, const MyMatrix44& v, const char* float_format = NULL); +} +*/ + diff --git a/apps/exampleViewer/common/imgui/imgui.cpp b/apps/exampleViewer/common/imgui/imgui.cpp new file mode 100644 index 0000000000..cc9440fccf --- /dev/null +++ b/apps/exampleViewer/common/imgui/imgui.cpp @@ -0,0 +1,9820 @@ +// dear imgui, v1.50 WIP +// (main code and documentation) + +// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code. +// Newcomers, read 'Programmer guide' below for notes on how to setup ImGui in your codebase. +// Get latest version at https://github.com/ocornut/imgui +// Releases change-log at https://github.com/ocornut/imgui/releases +// Developed by Omar Cornut and every direct or indirect contributors to the GitHub. +// This library is free but I need your support to sustain development and maintenance. +// If you work for a company, please consider financial support, e.g: https://www.patreon.com/imgui + +/* + + Index + - MISSION STATEMENT + - END-USER GUIDE + - PROGRAMMER GUIDE (read me!) + - API BREAKING CHANGES (read me when you update!) + - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + - How can I help? + - How do I update to a newer version of ImGui? + - What is ImTextureID and how do I display an image? + - I integrated ImGui in my engine and the text or lines are blurry.. + - I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around.. + - How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs. + - How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application? + - How can I load a different font than the default? + - How can I load multiple fonts? + - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? + - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) + - ISSUES & TODO-LIST + - CODE + + + MISSION STATEMENT + ================= + + - easy to use to create code-driven and data-driven tools + - easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools + - easy to hack and improve + - minimize screen real-estate usage + - minimize setup and maintenance + - minimize state storage on user side + - portable, minimize dependencies, run on target (consoles, phones, etc.) + - efficient runtime (NB- we do allocate when "growing" content - creating a window / opening a tree node for the first time, etc. - but a typical frame won't allocate anything) + - read about immediate-mode gui principles @ http://mollyrocket.com/861, http://mollyrocket.com/forums/index.html + + Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: + - doesn't look fancy, doesn't animate + - limited layout features, intricate layouts are typically crafted in code + - occasionally uses statically sized buffers for string manipulations - won't crash, but some very long pieces of text may be clipped. functions like ImGui::TextUnformatted() don't have such restriction. + + + END-USER GUIDE + ============== + + - double-click title bar to collapse window + - click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin() + - click and drag on lower right corner to resize window + - click and drag on any empty space to move window + - double-click/double-tap on lower right corner grip to auto-fit to content + - TAB/SHIFT+TAB to cycle through keyboard editable fields + - use mouse wheel to scroll + - use CTRL+mouse wheel to zoom window contents (if IO.FontAllowScaling is true) + - CTRL+Click on a slider or drag box to input value as text + - text editor: + - Hold SHIFT or use mouse to select text. + - CTRL+Left/Right to word jump + - CTRL+Shift+Left/Right to select words + - CTRL+A our Double-Click to select all + - CTRL+X,CTRL+C,CTRL+V to use OS clipboard + - CTRL+Z,CTRL+Y to undo/redo + - ESCAPE to revert text to its original value + - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) + + + PROGRAMMER GUIDE + ================ + + - read the FAQ below this section! + - your code creates the UI, if your code doesn't run the UI is gone! == very dynamic UI, no construction/destructions steps, less data retention on your side, no state duplication, less sync, less bugs. + - call and read ImGui::ShowTestWindow() for demo code demonstrating most features. + - see examples/ folder for standalone sample applications. Prefer reading examples/opengl2_example/ first as it is the simplest. + you may be able to grab and copy a ready made imgui_impl_*** file from the examples/. + - customization: PushStyleColor()/PushStyleVar() or the style editor to tweak the look of the interface (e.g. if you want a more compact UI or a different color scheme). + + - getting started: + - init: call ImGui::GetIO() to retrieve the ImGuiIO structure and fill the fields marked 'Settings'. + - init: call io.Fonts->GetTexDataAsRGBA32(...) and load the font texture pixels into graphics memory. + - every frame: + 1/ in your mainloop or right after you got your keyboard/mouse info, call ImGui::GetIO() and fill the fields marked 'Input' + 2/ call ImGui::NewFrame() as early as you can! + 3/ use any ImGui function you want between NewFrame() and Render() + 4/ call ImGui::Render() as late as you can to end the frame and finalize render data. it will call your RenderDrawListFn handler that you set in the IO structure. + (if you don't need to render, you still need to call Render() and ignore the callback, or call EndFrame() instead. if you call neither some aspects of windows focusing/moving will appear broken.) + - all rendering information are stored into command-lists until ImGui::Render() is called. + - ImGui never touches or know about your GPU state. the only function that knows about GPU is the RenderDrawListFn handler that you provide. + - effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" phases of your own application. + - refer to the examples applications in the examples/ folder for instruction on how to setup your code. + - a typical application skeleton may be: + + // Application init + ImGuiIO& io = ImGui::GetIO(); + io.DisplaySize.x = 1920.0f; + io.DisplaySize.y = 1280.0f; + io.IniFilename = "imgui.ini"; + io.RenderDrawListsFn = my_render_function; // Setup a render function, or set to NULL and call GetDrawData() after Render() to access the render data. + // TODO: Fill others settings of the io structure + + // Load texture atlas + // There is a default font so you don't need to care about choosing a font yet + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(pixels, &width, &height); + // TODO: At this points you've got a texture pointed to by 'pixels' and you need to upload that your your graphic system + // TODO: Store your texture pointer/identifier (whatever your engine uses) in 'io.Fonts->TexID' + + // Application main loop + while (true) + { + // 1) get low-level inputs (e.g. on Win32, GetKeyboardState(), or poll your events, etc.) + // TODO: fill all fields of IO structure and call NewFrame + ImGuiIO& io = ImGui::GetIO(); + io.DeltaTime = 1.0f/60.0f; + io.MousePos = mouse_pos; + io.MouseDown[0] = mouse_button_0; + io.MouseDown[1] = mouse_button_1; + io.KeysDown[i] = ... + + // 2) call NewFrame(), after this point you can use ImGui::* functions anytime + ImGui::NewFrame(); + + // 3) most of your application code here + MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); + MyGameRender(); // may use any ImGui functions + + // 4) render & swap video buffers + ImGui::Render(); + SwapBuffers(); + } + + - You can read back 'io.WantCaptureMouse', 'io.WantCaptureKeybord' etc. flags from the IO structure to tell how ImGui intends to use your + inputs and to know if you should share them or hide them from the rest of your application. Read the FAQ below for more information. + + + API BREAKING CHANGES + ==================== + + Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix. + Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code. + Also read releases logs https://github.com/ocornut/imgui/releases for more details. + + - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. + - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. + - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. + - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. + If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. + However if your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. + This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color. + ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) + { + float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; + return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); + } + If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. + - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). + - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. + - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). + - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. + - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). + - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) + - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). + - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. + - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. + - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. + - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. + - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. + GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. + GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! + - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize + - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. + - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason + - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. + you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. + - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. + this necessary change will break your rendering function! the fix should be very easy. sorry for that :( + - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. + - the signature of the io.RenderDrawListsFn handler has changed! + ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) + became: + ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). + argument 'cmd_lists' -> 'draw_data->CmdLists' + argument 'cmd_lists_count' -> 'draw_data->CmdListsCount' + ImDrawList 'commands' -> 'CmdBuffer' + ImDrawList 'vtx_buffer' -> 'VtxBuffer' + ImDrawList n/a -> 'IdxBuffer' (new) + ImDrawCmd 'vtx_count' -> 'ElemCount' + ImDrawCmd 'clip_rect' -> 'ClipRect' + ImDrawCmd 'user_callback' -> 'UserCallback' + ImDrawCmd 'texture_id' -> 'TextureId' + - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. + - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! + - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! + - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. + - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). + - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. + - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence + - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! + - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). + - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). + - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. + - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened. + - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). + - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. + - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API + - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. + - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. + - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. + - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing + - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. + - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) + - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. + - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. + - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. + - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior + - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() + - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) + - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. + - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. + (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. + this sequence: + const void* png_data; + unsigned int png_size; + ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); + // + became: + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + // + io.Fonts->TexID = (your_texture_identifier); + you now have much more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. + it is now recommended that you sample the font texture with bilinear interpolation. + (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. + (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) + (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets + - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) + - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) + - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility + - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() + - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) + - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) + - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() + - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn + - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) + - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite + - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes + + + FREQUENTLY ASKED QUESTIONS (FAQ), TIPS + ====================================== + + Q: How can I help? + A: - If you are experienced enough with ImGui and with C/C++, look at the todo list and see how you want/can help! + - Become a Patron/donate. Convince your company to become a Patron or provide serious funding for development time. + + Q: How do I update to a newer version of ImGui? + A: Overwrite the following files: + imgui.cpp + imgui.h + imgui_demo.cpp + imgui_draw.cpp + imgui_internal.h + stb_rect_pack.h + stb_textedit.h + stb_truetype.h + Don't overwrite imconfig.h if you have made modification to your copy. + Check the "API BREAKING CHANGES" sections for a list of occasional API breaking changes. If you have a problem with a function, search for its name + in the code, there will likely be a comment about it. Please report any issue to the GitHub page! + + + Q: What is ImTextureID and how do I display an image? + A: ImTextureID is a void* used to pass renderer-agnostic texture references around until it hits your render function. + ImGui knows nothing about what those bits represent, it just passes them around. It is up to you to decide what you want the void* to carry! + It could be an identifier to your OpenGL texture (cast GLuint to void*), a pointer to your custom engine material (cast MyMaterial* to void*), etc. + At the end of the chain, your renderer takes this void* to cast it back into whatever it needs to select a current texture to render. + Refer to examples applications, where each renderer (in a imgui_impl_xxxx.cpp file) is treating ImTextureID as a different thing. + (c++ tip: OpenGL uses integers to identify textures. You can safely store an integer into a void*, just cast it to void*, don't take it's address!) + To display a custom image/texture within an ImGui window, you may use ImGui::Image(), ImGui::ImageButton(), ImDrawList::AddImage() functions. + ImGui will generate the geometry and draw calls using the ImTextureID that you passed and which your renderer can use. + It is your responsibility to get textures uploaded to your GPU. + + Q: I integrated ImGui in my engine and the text or lines are blurry.. + A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). + Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension. + + Q: I integrated ImGui in my engine and some elements are clipping or disappearing when I move windows around.. + A: Most likely you are mishandling the clipping rectangles in your render function. Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). + + Q: Can I have multiple widgets with the same label? Can I have widget without a label? (Yes) + A: Yes. A primer on the use of labels/IDs in ImGui.. + + - Elements that are not clickable, such as Text() items don't need an ID. + + - Interactive widgets require state to be carried over multiple frames (most typically ImGui often needs to remember what is the "active" widget). + to do so they need a unique ID. unique ID are typically derived from a string label, an integer index or a pointer. + + Button("OK"); // Label = "OK", ID = hash of "OK" + Button("Cancel"); // Label = "Cancel", ID = hash of "Cancel" + + - ID are uniquely scoped within windows, tree nodes, etc. so no conflict can happen if you have two buttons called "OK" in two different windows + or in two different locations of a tree. + + - If you have a same ID twice in the same location, you'll have a conflict: + + Button("OK"); + Button("OK"); // ID collision! Both buttons will be treated as the same. + + Fear not! this is easy to solve and there are many ways to solve it! + + - When passing a label you can optionally specify extra unique ID information within string itself. This helps solving the simpler collision cases. + use "##" to pass a complement to the ID that won't be visible to the end-user: + + Button("Play"); // Label = "Play", ID = hash of "Play" + Button("Play##foo1"); // Label = "Play", ID = hash of "Play##foo1" (different from above) + Button("Play##foo2"); // Label = "Play", ID = hash of "Play##foo2" (different from above) + + - If you want to completely hide the label, but still need an ID: + + Checkbox("##On", &b); // Label = "", ID = hash of "##On" (no label!) + + - Occasionally/rarely you might want change a label while preserving a constant ID. This allows you to animate labels. + For example you may want to include varying information in a window title bar (and windows are uniquely identified by their ID.. obviously) + Use "###" to pass a label that isn't part of ID: + + Button("Hello###ID"; // Label = "Hello", ID = hash of "ID" + Button("World###ID"; // Label = "World", ID = hash of "ID" (same as above) + + sprintf(buf, "My game (%f FPS)###MyGame"); + Begin(buf); // Variable label, ID = hash of "MyGame" + + - Use PushID() / PopID() to create scopes and avoid ID conflicts within the same Window. + This is the most convenient way of distinguishing ID if you are iterating and creating many UI elements. + You can push a pointer, a string or an integer value. Remember that ID are formed from the concatenation of everything in the ID stack! + + for (int i = 0; i < 100; i++) + { + PushID(i); + Button("Click"); // Label = "Click", ID = hash of integer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj); + Button("Click"); // Label = "Click", ID = hash of pointer + "label" (unique) + PopID(); + } + + for (int i = 0; i < 100; i++) + { + MyObject* obj = Objects[i]; + PushID(obj->Name); + Button("Click"); // Label = "Click", ID = hash of string + "label" (unique) + PopID(); + } + + - More example showing that you can stack multiple prefixes into the ID stack: + + Button("Click"); // Label = "Click", ID = hash of "Click" + PushID("node"); + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + PushID(my_ptr); + Button("Click"); // Label = "Click", ID = hash of "node" + ptr + "Click" + PopID(); + PopID(); + + - Tree nodes implicitly creates a scope for you by calling PushID(). + + Button("Click"); // Label = "Click", ID = hash of "Click" + if (TreeNode("node")) + { + Button("Click"); // Label = "Click", ID = hash of "node" + "Click" + TreePop(); + } + + - When working with trees, ID are used to preserve the open/close state of each tree node. + Depending on your use cases you may want to use strings, indices or pointers as ID. + e.g. when displaying a single object that may change over time (1-1 relationship), using a static string as ID will preserve your node open/closed state when the targeted object change. + e.g. when displaying a list of objects, using indices or pointers as ID will preserve the node open/closed state differently. experiment and see what makes more sense! + + Q: How can I tell when ImGui wants my mouse/keyboard inputs and when I can pass them to my application? + A: You can read the 'io.WantCaptureXXX' flags in the ImGuiIO structure. Preferably read them after calling ImGui::NewFrame() to avoid those flags lagging by one frame, but either should be fine. + When 'io.WantCaptureMouse' or 'io.WantCaptureKeyboard' flags are set you may want to discard/hide the inputs from the rest of your application. + When 'io.WantInputsCharacters' is set to may want to notify your OS to popup an on-screen keyboard, if available. + ImGui is tracking dragging and widget activity that may occur outside the boundary of a window, so 'io.WantCaptureMouse' is a more accurate and complete than testing for ImGui::IsMouseHoveringAnyWindow(). + (Advanced note: text input releases focus on Return 'KeyDown', so the following Return 'KeyUp' event that your application receive will typically have 'io.WantcaptureKeyboard=false'. + Depending on your application logic it may or not be inconvenient. You might want to track which key-downs were for ImGui (e.g. with an array of bool) and filter out the corresponding key-ups.) + + Q: How can I load a different font than the default? (default is an embedded version of ProggyClean.ttf, rendered at size 13) + A: Use the font atlas to load the TTF file you want: + + ImGuiIO& io = ImGui::GetIO(); + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + + Q: How can I load multiple fonts? + A: Use the font atlas to pack them into a single texture: + (Read extra_fonts/README.txt and the code in ImFontAtlas for more details.) + + ImGuiIO& io = ImGui::GetIO(); + ImFont* font0 = io.Fonts->AddFontDefault(); + ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); + ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + // the first loaded font gets used by default + // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime + + // Options + ImFontConfig config; + config.OversampleH = 3; + config.OversampleV = 1; + config.GlyphExtraSpacing.x = 1.0f; + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); + + // Combine multiple fonts into one + ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; + ImFontConfig config; + config.MergeMode = true; + io.Fonts->AddFontDefault(); + io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); + io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); + + Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? + A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. + All your strings needs to use UTF-8 encoding. Specifying literal in your source code using a local code page (such as CP-923 for Japanese or CP-1251 for Cyrillic) will not work. + In C++11 you can encode a string literal in UTF-8 by using the u8"hello" syntax. Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. + You can also try to remap your local codepage characters to their Unicode codepoint using font->AddRemapChar(), but international users may have problems reading/editing your source code. + + io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters + io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() + io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application + + As for text input, depends on you passing the right character code to io.AddInputCharacter(). The example applications do that. + + Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) + A: The easiest way is to create a dummy window. Call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flag, zero background alpha, + then retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. + + - tip: the construct 'IMGUI_ONCE_UPON_A_FRAME { ... }' will run the block of code only once a frame. You can use it to quickly add custom UI in the middle of a deep nested inner loop in your code. + - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug" + - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. this is also useful to set yourself in the context of another window (to get/set other settings) + - tip: you can call Render() multiple times (e.g for VR renders). + - tip: call and read the ShowTestWindow() code in imgui_demo.cpp for more example of how to use ImGui! + + + ISSUES & TODO-LIST + ================== + Issue numbers (#) refer to github issues listed at https://github.com/ocornut/imgui/issues + The list below consist mostly of notes of things to do before they are requested/discussed by users (at that point it usually happens on the github) + + - doc: add a proper documentation+regression testing system (#435) + - window: add a way for very transient windows (non-saved, temporary overlay over hundreds of objects) to "clean" up from the global window list. perhaps a lightweight explicit cleanup pass. + - window: calling SetNextWindowSize() every frame with <= 0 doesn't do anything, may be useful to allow (particularly when used for a single axis) (#690) + - window: auto-fit feedback loop when user relies on any dynamic layout (window width multiplier, column) appears weird to end-user. clarify. + - window: allow resizing of child windows (possibly given min/max for each axis?) + - window: background options for child windows, border option (disable rounding) + - window: add a way to clear an existing window instead of appending (e.g. for tooltip override using a consistent api rather than the deferred tooltip) + - window: resizing from any sides? + mouse cursor directives for app. +!- window: begin with *p_open == false should return false. + - window: get size/pos helpers given names (see discussion in #249) + - window: a collapsed window can be stuck behind the main menu bar? + - window: when window is small, prioritize resize button over close button. + - window: detect extra End() call that pop the "Debug" window out and assert at call site instead of later. + - window/tooltip: allow to set the width of a tooltip to allow TextWrapped() etc. while keeping the height automatic. + - window: increase minimum size of a window with menus or fix the menu rendering so that it doesn't look odd. + - draw-list: maintaining bounding box per command would allow to merge draw command when clipping isn't relied on (typical non-scrolling window or non-overflowing column would merge with previous command). +!- scrolling: allow immediately effective change of scroll if we haven't appended items yet + - splitter/separator: formalize the splitter idiom into an official api (we want to handle n-way split) (#319) + - widgets: display mode: widget-label, label-widget (aligned on column or using fixed size), label-newline-tab-widget etc. + - widgets: clean up widgets internal toward exposing everything. + - widgets: add disabled and read-only modes (#211) + - main: considering adding an Init() function? some constructs are awkward in the implementation because of the lack of them. +!- main: make it so that a frame with no window registered won't refocus every window on subsequent frames (~bump LastFrameActive of all windows). + - main: IsItemHovered() make it more consistent for various type of widgets, widgets with multiple components, etc. also effectively IsHovered() region sometimes differs from hot region, e.g tree nodes + - main: IsItemHovered() info stored in a stack? so that 'if TreeNode() { Text; TreePop; } if IsHovered' return the hover state of the TreeNode? + - input text: clean up the mess caused by converting UTF-8 <> wchar. the code is rather inefficient right now and super fragile. + - input text: reorganize event handling, allow CharFilter to modify buffers, allow multiple events? (#541) + - input text: expose CursorPos in char filter event (#816) + - input text: flag to disable live update of the user buffer (also applies to float/int text input) + - input text: resize behavior - field could stretch when being edited? hover tooltip shows more text? + - input text: add ImGuiInputTextFlags_EnterToApply? (off #218) + - input text: add discard flag (e.g. ImGuiInputTextFlags_DiscardActiveBuffer) or make it easier to clear active focus for text replacement during edition (#725) + - input text multi-line: don't directly call AddText() which does an unnecessary vertex reserve for character count prior to clipping. and/or more line-based clipping to AddText(). and/or reorganize TextUnformatted/RenderText for more efficiency for large text (e.g TextUnformatted could clip and log separately, etc). + - input text multi-line: way to dynamically grow the buffer without forcing the user to initially allocate for worse case (follow up on #200) + - input text multi-line: line numbers? status bar? (follow up on #200) + - input text multi-line: behave better when user changes input buffer while editing is active (even though it is illegal behavior). namely, the change of buffer can create a scrollbar glitch (#725) + - input text: allow centering/positioning text so that ctrl+clicking Drag or Slider keeps the textual value at the same pixel position. + - input number: optional range min/max for Input*() functions + - input number: holding [-]/[+] buttons could increase the step speed non-linearly (or user-controlled) + - input number: use mouse wheel to step up/down + - input number: applying arithmetics ops (+,-,*,/) messes up with text edit undo stack. + - button: provide a button that looks framed. + - text: proper alignment options + - image/image button: misalignment on padded/bordered button? + - image/image button: parameters are confusing, image() has tint_col,border_col whereas imagebutton() has bg_col/tint_col. Even thou they are different parameters ordering could be more consistent. can we fix that? + - layout: horizontal layout helper (#97) + - layout: horizontal flow until no space left (#404) + - layout: more generic alignment state (left/right/centered) for single items? + - layout: clean up the InputFloatN/SliderFloatN/ColorEdit4 layout code. item width should include frame padding. + - layout: BeginGroup() needs a border option. + - columns: declare column set (each column: fixed size, %, fill, distribute default size among fills) (#513, #125) + - columns: add a conditional parameter to SetColumnOffset() (#513, #125) + - columns: separator function or parameter that works within the column (currently Separator() bypass all columns) (#125) + - columns: columns header to act as button (~sort op) and allow resize/reorder (#513, #125) + - columns: user specify columns size (#513, #125) + - columns: flag to add horizontal separator above/below? + - columns/layout: setup minimum line height (equivalent of automatically calling AlignFirstTextHeightToWidgets) + - combo: sparse combo boxes (via function call?) / iterators + - combo: contents should extends to fit label if combo widget is small + - combo/listbox: keyboard control. need InputText-like non-active focus + key handling. considering keyboard for custom listbox (pr #203) + - listbox: multiple selection + - listbox: user may want to initial scroll to focus on the one selected value? + - listbox: keyboard navigation. + - listbox: scrolling should track modified selection. +!- popups/menus: clarify usage of popups id, how MenuItem/Selectable closing parent popups affects the ID, etc. this is quite fishy needs improvement! (#331, #402) + - popups: add variant using global identifier similar to Begin/End (#402) + - popups: border options. richer api like BeginChild() perhaps? (#197) + - tooltip: tooltip that doesn't fit in entire screen seems to lose their "last preferred button" and may teleport when moving mouse + - menus: local shortcuts, global shortcuts (#456, #126) + - menus: icons + - menus: menubars: some sort of priority / effect of main menu-bar on desktop size? + - menus: calling BeginMenu() twice with a same name doesn't seem to append nicely + - statusbar: add a per-window status bar helper similar to what menubar does. + - tabs (#261, #351) + - separator: separator on the initial position of a window is not visible (cursorpos.y <= clippos.y) +!- color: the color helpers/typing is a mess and needs sorting out. + - color: add a better color picker (#346) + - node/graph editor (#306) + - pie menus patterns (#434) + - drag'n drop, dragging helpers (carry dragging info, visualize drag source before clicking, drop target, etc.) (#143, #479) + - plot: PlotLines() should use the polygon-stroke facilities (currently issues with averaging normals) + - plot: make it easier for user to draw extra stuff into the graph (e.g: draw basis, highlight certain points, 2d plots, multiple plots) + - plot: "smooth" automatic scale over time, user give an input 0.0(full user scale) 1.0(full derived from value) + - plot: add a helper e.g. Plot(char* label, float value, float time_span=2.0f) that stores values and Plot them for you - probably another function name. and/or automatically allow to plot ANY displayed value (more reliance on stable ID) + - slider: allow using the [-]/[+] buttons used by InputFloat()/InputInt() + - slider: initial absolute click is imprecise. change to relative movement slider (same as scrollbar). + - slider: add dragging-based widgets to edit values with mouse (on 2 axises), saving screen real-estate. + - slider: tint background based on value (e.g. v_min -> v_max, or use 0.0f either side of the sign) + - slider & drag: int data passing through a float + - drag float: up/down axis + - drag float: added leeway on edge (e.g. a few invisible steps past the clamp limits) + - tree node / optimization: avoid formatting when clipped. + - tree node: tree-node/header right-most side doesn't take account of horizontal scrolling. + - tree node: add treenode/treepush int variants? not there because (void*) cast from int warns on some platforms/settings? + - tree node: try to apply scrolling at time of TreePop() if node was just opened and end of node is past scrolling limits? + - tree node / selectable render mismatch which is visible if you use them both next to each other (e.g. cf. property viewer) + - tree node: tweak color scheme to distinguish headers from selected tree node (#581) + - textwrapped: figure out better way to use TextWrapped() in an always auto-resize context (tooltip, etc.) (#249) + - settings: write more decent code to allow saving/loading new fields + - settings: api for per-tool simple persistent data (bool,int,float,columns sizes,etc.) in .ini file + - style: add window shadows. + - style/optimization: store rounded corners in texture to use 1 quad per corner (filled and wireframe) to lower the cost of rounding. + - style: color-box not always square? + - style: a concept of "compact style" that the end-user can easily rely on (e.g. PushStyleCompact()?) that maps to other settings? avoid implementing duplicate helpers such as SmallCheckbox(), etc. + - style: try to make PushStyleVar() more robust to incorrect parameters (to be more friendly to edit & continues situation). + - style: global scale setting. + - style: WindowPadding needs to be EVEN needs the 0.5 multiplier probably have a subtle effect on clip rectangle + - text: simple markup language for color change? + - font: dynamic font atlas to avoid baking huge ranges into bitmap and make scaling easier. + - font: small opt: for monospace font (like the defalt one) we can trim IndexXAdvance as long as trailing value is == FallbackXAdvance + - font: add support for kerning, probably optional. perhaps default to (32..128)^2 matrix ~ 36KB then hash fallback. + - font: add a simpler CalcTextSizeA() api? current one ok but not welcome if user needs to call it directly (without going through ImGui::CalcTextSize) + - font: fix AddRemapChar() to work before font has been built. + - log: LogButtons() options for specifying depth and/or hiding depth slider + - log: have more control over the log scope (e.g. stop logging when leaving current tree node scope) + - log: be able to log anything (e.g. right-click on a window/tree-node, shows context menu? log into tty/file/clipboard) + - log: let user copy any window content to clipboard easily (CTRL+C on windows? while moving it? context menu?). code is commented because it fails with multiple Begin/End pairs. + - filters: set a current filter that tree node can automatically query to hide themselves + - filters: handle wildcards (with implicit leading/trailing *), regexps + - shortcuts: add a shortcut api, e.g. parse "&Save" and/or "Save (CTRL+S)", pass in to widgets or provide simple ways to use (button=activate, input=focus) +!- keyboard: tooltip & combo boxes are messing up / not honoring keyboard tabbing + - keyboard: full keyboard navigation and focus. (#323) + - focus: preserve ActiveId/focus stack state, e.g. when opening a menu and close it, previously selected InputText() focus gets restored (#622) + - focus: SetKeyboardFocusHere() on with >= 0 offset could be done on same frame (else latch and modulate on beginning of next frame) + - input: rework IO system to be able to pass actual ordered/timestamped events. (~#335, #71) + - input: allow to decide and pass explicit double-clicks (e.g. for windows by the CS_DBLCLKS style). + - input: support track pad style scrolling & slider edit. + - misc: provide a way to compile out the entire implementation while providing a dummy API (e.g. #define IMGUI_DUMMY_IMPL) + - misc: double-clicking on title bar to minimize isn't consistent, perhaps move to single-click on left-most collapse icon? + - misc: provide HoveredTime and ActivatedTime to ease the creation of animations. + - style editor: have a more global HSV setter (e.g. alter hue on all elements). consider replacing active/hovered by offset in HSV space? (#438) + - style editor: color child window height expressed in multiple of line height. + - remote: make a system like RemoteImGui first-class citizen/project (#75) + - drawlist: move Font, FontSize, FontTexUvWhitePixel inside ImDrawList and make it self-contained (apart from drawing settings?) + - drawlist: end-user probably can't call Clear() directly because we expect a texture to be pushed in the stack. + - examples: directx9: save/restore device state more thoroughly. + - examples: window minimize, maximize (#583) + - optimization: add a flag to disable most of rendering, for the case where the user expect to skip it (#335) + - optimization: use another hash function than crc32, e.g. FNV1a + - optimization/render: merge command-lists with same clip-rect into one even if they aren't sequential? (as long as in-between clip rectangle don't overlap)? + - optimization: turn some the various stack vectors into statically-sized arrays + - optimization: better clipping for multi-component widgets +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#define IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_PLACEMENT_NEW +#include "imgui_internal.h" + +#include // toupper, isprint +#include // NULL, malloc, free, qsort, atoi +#include // vsnprintf, sscanf, printf +#include // INT_MIN, INT_MAX +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#endif + +// Clang warnings with -Weverything +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. +#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. +#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' // +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size +#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' +#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value +#endif + +//------------------------------------------------------------------------- +// Forward Declarations +//------------------------------------------------------------------------- + +static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end = NULL); + +static void PushMultiItemsWidths(int components, float w_full = 0.0f); +static float GetDraggedColumnOffset(int column_index); + +static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); + +static void SetCurrentFont(ImFont* font); +static void SetCurrentWindow(ImGuiWindow* window); +static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond); +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond); +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond); +static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs); +static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); +static inline bool IsWindowContentHoverable(ImGuiWindow* window); +static void ClearSetNextWindowData(); +static void CheckStacksSize(ImGuiWindow* window, bool write); +static void Scrollbar(ImGuiWindow* window, bool horizontal); + +static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list); +static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window); +static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window); + +static ImGuiIniData* FindWindowSettings(const char* name); +static ImGuiIniData* AddWindowSettings(const char* name); +static void LoadSettings(); +static void SaveSettings(); +static void MarkSettingsDirty(); + +static void PushColumnClipRect(int column_index = -1); +static ImRect GetVisibleRect(); + +static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags); +static void CloseInactivePopups(); +static void ClosePopupToLevel(int remaining); +static void ClosePopup(ImGuiID id); +static bool IsPopupOpen(ImGuiID id); +static ImGuiWindow* GetFrontMostModalRootWindow(); +static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& rect_to_avoid); + +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size); +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size); +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2); +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format); + +//----------------------------------------------------------------------------- +// Platform dependent default implementations +//----------------------------------------------------------------------------- + +static const char* GetClipboardTextFn_DefaultImpl(void* user_data); +static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); + +//----------------------------------------------------------------------------- +// Context +//----------------------------------------------------------------------------- + +// Default context, default font atlas. +// New contexts always point by default to this font atlas. It can be changed by reassigning the GetIO().Fonts variable. +static ImGuiContext GImDefaultContext; +static ImFontAtlas GImDefaultFontAtlas; + +// Current context pointer. Implicitely used by all ImGui functions. Always assumed to be != NULL. Change to a different context by calling ImGui::SetCurrentContext() +// ImGui is currently not thread-safe because of this variable. If you want thread-safety to allow N threads to access N different contexts, you might work around it by (A) having two instances of the ImGui code under different namespaces or (B) change this variable to be TLS. Further development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 +ImGuiContext* GImGui = &GImDefaultContext; + +//----------------------------------------------------------------------------- +// User facing structures +//----------------------------------------------------------------------------- + +ImGuiStyle::ImGuiStyle() +{ + Alpha = 1.0f; // Global alpha applies to everything in ImGui + WindowPadding = ImVec2(8,8); // Padding within a window + WindowMinSize = ImVec2(32,32); // Minimum window size + WindowRounding = 9.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text + ChildWindowRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows + FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) + FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). + ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines + ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). + ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns + ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar + ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar + GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar + GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. + DisplayWindowPadding = ImVec2(22,22); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + DisplaySafeAreaPadding = ImVec2(4,4); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. + AntiAliasedShapes = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + CurveTessellationTol = 1.25f; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + + Colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); + Colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); + Colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f); + Colors[ImGuiCol_ChildWindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + Colors[ImGuiCol_PopupBg] = ImVec4(0.05f, 0.05f, 0.10f, 0.90f); + Colors[ImGuiCol_Border] = ImVec4(0.70f, 0.70f, 0.70f, 0.65f); + Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + Colors[ImGuiCol_FrameBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.30f); // Background of checkbox, radio button, plot, slider, text input + Colors[ImGuiCol_FrameBgHovered] = ImVec4(0.90f, 0.80f, 0.80f, 0.40f); + Colors[ImGuiCol_FrameBgActive] = ImVec4(0.90f, 0.65f, 0.65f, 0.45f); + Colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); + Colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); + Colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); + Colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); + Colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); + Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); + Colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); + Colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 0.40f); + Colors[ImGuiCol_ComboBg] = ImVec4(0.20f, 0.20f, 0.20f, 0.99f); + Colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); + Colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); + Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_Button] = ImVec4(0.67f, 0.40f, 0.40f, 0.60f); + Colors[ImGuiCol_ButtonHovered] = ImVec4(0.67f, 0.40f, 0.40f, 1.00f); + Colors[ImGuiCol_ButtonActive] = ImVec4(0.80f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); + Colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); + Colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); + Colors[ImGuiCol_Column] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + Colors[ImGuiCol_ColumnHovered] = ImVec4(0.70f, 0.60f, 0.60f, 1.00f); + Colors[ImGuiCol_ColumnActive] = ImVec4(0.90f, 0.70f, 0.70f, 1.00f); + Colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); + Colors[ImGuiCol_ResizeGripHovered] = ImVec4(1.00f, 1.00f, 1.00f, 0.60f); + Colors[ImGuiCol_ResizeGripActive] = ImVec4(1.00f, 1.00f, 1.00f, 0.90f); + Colors[ImGuiCol_CloseButton] = ImVec4(0.50f, 0.50f, 0.90f, 0.50f); + Colors[ImGuiCol_CloseButtonHovered] = ImVec4(0.70f, 0.70f, 0.90f, 0.60f); + Colors[ImGuiCol_CloseButtonActive] = ImVec4(0.70f, 0.70f, 0.70f, 1.00f); + Colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + Colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + Colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); + Colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); + Colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); + Colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); +} + +ImGuiIO::ImGuiIO() +{ + // Most fields are initialized with zero + memset(this, 0, sizeof(*this)); + + DisplaySize = ImVec2(-1.0f, -1.0f); + DeltaTime = 1.0f/60.0f; + IniSavingRate = 5.0f; + IniFilename = "imgui.ini"; + LogFilename = "imgui_log.txt"; + Fonts = &GImDefaultFontAtlas; + FontGlobalScale = 1.0f; + DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + MousePos = ImVec2(-1,-1); + MousePosPrev = ImVec2(-1,-1); + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) + MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; + for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) + KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; + for (int i = 0; i < ImGuiKey_COUNT; i++) + KeyMap[i] = -1; + KeyRepeatDelay = 0.250f; + KeyRepeatRate = 0.050f; + UserData = NULL; + + // User functions + RenderDrawListsFn = NULL; + MemAllocFn = malloc; + MemFreeFn = free; + GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations + SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; + ClipboardUserData = NULL; + ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; + + // Set OS X style defaults based on __APPLE__ compile time flag +#ifdef __APPLE__ + OSXBehaviors = true; +#endif +} + +// Pass in translated ASCII characters for text input. +// - with glfw you can get those from the callback set in glfwSetCharCallback() +// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message +void ImGuiIO::AddInputCharacter(ImWchar c) +{ + const int n = ImStrlenW(InputCharacters); + if (n + 1 < IM_ARRAYSIZE(InputCharacters)) + { + InputCharacters[n] = c; + InputCharacters[n+1] = '\0'; + } +} + +void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) +{ + // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more + const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); + ImWchar wchars[wchars_buf_len]; + ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); + for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) + AddInputCharacter(wchars[i]); +} + +//----------------------------------------------------------------------------- +// HELPERS +//----------------------------------------------------------------------------- + +#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose +#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 + +// Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. +#ifdef _WIN32 +#define IM_NEWLINE "\r\n" +#else +#define IM_NEWLINE "\n" +#endif + +bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c) +{ + bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; + bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; + bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; + return ((b1 == b2) && (b2 == b3)); +} + +int ImStricmp(const char* str1, const char* str2) +{ + int d; + while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } + return d; +} + +int ImStrnicmp(const char* str1, const char* str2, int count) +{ + int d = 0; + while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } + return d; +} + +void ImStrncpy(char* dst, const char* src, int count) +{ + if (count < 1) return; + strncpy(dst, src, (size_t)count); + dst[count-1] = 0; +} + +char* ImStrdup(const char *str) +{ + size_t len = strlen(str) + 1; + void* buff = ImGui::MemAlloc(len); + return (char*)memcpy(buff, (const void*)str, len); +} + +int ImStrlenW(const ImWchar* str) +{ + int n = 0; + while (*str++) n++; + return n; +} + +const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line +{ + while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') + buf_mid_line--; + return buf_mid_line; +} + +const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) +{ + if (!needle_end) + needle_end = needle + strlen(needle); + + const char un0 = (char)toupper(*needle); + while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) + { + if (toupper(*haystack) == un0) + { + const char* b = needle + 1; + for (const char* a = haystack + 1; b < needle_end; a++, b++) + if (toupper(*a) != toupper(*b)) + break; + if (b == needle_end) + return haystack; + } + haystack++; + } + return NULL; +} + + +// MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). +// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. +int ImFormatString(char* buf, int buf_size, const char* fmt, ...) +{ + IM_ASSERT(buf_size > 0); + va_list args; + va_start(args, fmt); + int w = vsnprintf(buf, buf_size, fmt, args); + va_end(args); + if (w == -1 || w >= buf_size) + w = buf_size - 1; + buf[w] = 0; + return w; +} + +int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args) +{ + IM_ASSERT(buf_size > 0); + int w = vsnprintf(buf, buf_size, fmt, args); + if (w == -1 || w >= buf_size) + w = buf_size - 1; + buf[w] = 0; + return w; +} + +// Pass data_size==0 for zero-terminated strings +// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. +ImU32 ImHash(const void* data, int data_size, ImU32 seed) +{ + static ImU32 crc32_lut[256] = { 0 }; + if (!crc32_lut[1]) + { + const ImU32 polynomial = 0xEDB88320; + for (ImU32 i = 0; i < 256; i++) + { + ImU32 crc = i; + for (ImU32 j = 0; j < 8; j++) + crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); + crc32_lut[i] = crc; + } + } + + seed = ~seed; + ImU32 crc = seed; + const unsigned char* current = (const unsigned char*)data; + + if (data_size > 0) + { + // Known size + while (data_size--) + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; + } + else + { + // Zero-terminated string + while (unsigned char c = *current++) + { + // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. + // Because this syntax is rarely used we are optimizing for the common case. + // - If we reach ### in the string we discard the hash so far and reset to the seed. + // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. + if (c == '#' && current[0] == '#' && current[1] == '#') + crc = seed; + crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; + } + } + return ~crc; +} + +//----------------------------------------------------------------------------- +// ImText* helpers +//----------------------------------------------------------------------------- + +// Convert UTF-8 to 32-bits character, process single character input. +// Based on stb_from_utf8() from github.com/nothings/stb/ +// We handle UTF-8 decoding error by skipping forward. +int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) +{ + unsigned int c = (unsigned int)-1; + const unsigned char* str = (const unsigned char*)in_text; + if (!(*str & 0x80)) + { + c = (unsigned int)(*str++); + *out_char = c; + return 1; + } + if ((*str & 0xe0) == 0xc0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 2) return 1; + if (*str < 0xc2) return 2; + c = (unsigned int)((*str++ & 0x1f) << 6); + if ((*str & 0xc0) != 0x80) return 2; + c += (*str++ & 0x3f); + *out_char = c; + return 2; + } + if ((*str & 0xf0) == 0xe0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 3) return 1; + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; + if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x0f) << 12); + if ((*str & 0xc0) != 0x80) return 3; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 3; + c += (*str++ & 0x3f); + *out_char = c; + return 3; + } + if ((*str & 0xf8) == 0xf0) + { + *out_char = 0xFFFD; // will be invalid but not end of string + if (in_text_end && in_text_end - (const char*)str < 4) return 1; + if (*str > 0xf4) return 4; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; + if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below + c = (unsigned int)((*str++ & 0x07) << 18); + if ((*str & 0xc0) != 0x80) return 4; + c += (unsigned int)((*str++ & 0x3f) << 12); + if ((*str & 0xc0) != 0x80) return 4; + c += (unsigned int)((*str++ & 0x3f) << 6); + if ((*str & 0xc0) != 0x80) return 4; + c += (*str++ & 0x3f); + // utf-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xFFFFF800) == 0xD800) return 4; + *out_char = c; + return 4; + } + *out_char = 0; + return 0; +} + +int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) +{ + ImWchar* buf_out = buf; + ImWchar* buf_end = buf + buf_size; + while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c; + in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; + if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes + *buf_out++ = (ImWchar)c; + } + *buf_out = 0; + if (in_text_remaining) + *in_text_remaining = in_text; + return (int)(buf_out - buf); +} + +int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) +{ + int char_count = 0; + while ((!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c; + in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); + if (c == 0) + break; + if (c < 0x10000) + char_count++; + } + return char_count; +} + +// Based on stb_to_utf8() from github.com/nothings/stb/ +static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) +{ + if (c < 0x80) + { + buf[0] = (char)c; + return 1; + } + if (c < 0x800) + { + if (buf_size < 2) return 0; + buf[0] = (char)(0xc0 + (c >> 6)); + buf[1] = (char)(0x80 + (c & 0x3f)); + return 2; + } + if (c >= 0xdc00 && c < 0xe000) + { + return 0; + } + if (c >= 0xd800 && c < 0xdc00) + { + if (buf_size < 4) return 0; + buf[0] = (char)(0xf0 + (c >> 18)); + buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); + buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); + buf[3] = (char)(0x80 + ((c ) & 0x3f)); + return 4; + } + //else if (c < 0x10000) + { + if (buf_size < 3) return 0; + buf[0] = (char)(0xe0 + (c >> 12)); + buf[1] = (char)(0x80 + ((c>> 6) & 0x3f)); + buf[2] = (char)(0x80 + ((c ) & 0x3f)); + return 3; + } +} + +static inline int ImTextCountUtf8BytesFromChar(unsigned int c) +{ + if (c < 0x80) return 1; + if (c < 0x800) return 2; + if (c >= 0xdc00 && c < 0xe000) return 0; + if (c >= 0xd800 && c < 0xdc00) return 4; + return 3; +} + +int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) +{ + char* buf_out = buf; + const char* buf_end = buf + buf_size; + while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c = (unsigned int)(*in_text++); + if (c < 0x80) + *buf_out++ = (char)c; + else + buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c); + } + *buf_out = 0; + return (int)(buf_out - buf); +} + +int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) +{ + int bytes_count = 0; + while ((!in_text_end || in_text < in_text_end) && *in_text) + { + unsigned int c = (unsigned int)(*in_text++); + if (c < 0x80) + bytes_count++; + else + bytes_count += ImTextCountUtf8BytesFromChar(c); + } + return bytes_count; +} + +ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) +{ + float s = 1.0f/255.0f; + return ImVec4( + ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, + ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); +} + +ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) +{ + ImU32 out; + out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; + out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; + return out; +} + +ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) +{ + ImVec4 c = GImGui->Style.Colors[idx]; + c.w *= GImGui->Style.Alpha * alpha_mul; + return ColorConvertFloat4ToU32(c); +} + +ImU32 ImGui::GetColorU32(const ImVec4& col) +{ + ImVec4 c = col; + c.w *= GImGui->Style.Alpha; + return ColorConvertFloat4ToU32(c); +} + +// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 +// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv +void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) +{ + float K = 0.f; + if (g < b) + { + const float tmp = g; g = b; b = tmp; + K = -1.f; + } + if (r < g) + { + const float tmp = r; r = g; g = tmp; + K = -2.f / 6.f - K; + } + + const float chroma = r - (g < b ? g : b); + out_h = fabsf(K + (g - b) / (6.f * chroma + 1e-20f)); + out_s = chroma / (r + 1e-20f); + out_v = r; +} + +// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 +// also http://en.wikipedia.org/wiki/HSL_and_HSV +void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) +{ + if (s == 0.0f) + { + // gray + out_r = out_g = out_b = v; + return; + } + + h = fmodf(h, 1.0f) / (60.0f/360.0f); + int i = (int)h; + float f = h - (float)i; + float p = v * (1.0f - s); + float q = v * (1.0f - s * f); + float t = v * (1.0f - s * (1.0f - f)); + + switch (i) + { + case 0: out_r = v; out_g = t; out_b = p; break; + case 1: out_r = q; out_g = v; out_b = p; break; + case 2: out_r = p; out_g = v; out_b = t; break; + case 3: out_r = p; out_g = q; out_b = v; break; + case 4: out_r = t; out_g = p; out_b = v; break; + case 5: default: out_r = v; out_g = p; out_b = q; break; + } +} + +// Load file content into memory +// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() +void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size, int padding_bytes) +{ + IM_ASSERT(filename && file_open_mode); + if (out_file_size) + *out_file_size = 0; + + FILE* f; + if ((f = fopen(filename, file_open_mode)) == NULL) + return NULL; + + long file_size_signed; + if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) + { + fclose(f); + return NULL; + } + + int file_size = (int)file_size_signed; + void* file_data = ImGui::MemAlloc(file_size + padding_bytes); + if (file_data == NULL) + { + fclose(f); + return NULL; + } + if (fread(file_data, 1, (size_t)file_size, f) != (size_t)file_size) + { + fclose(f); + ImGui::MemFree(file_data); + return NULL; + } + if (padding_bytes > 0) + memset((void *)(((char*)file_data) + file_size), 0, padding_bytes); + + fclose(f); + if (out_file_size) + *out_file_size = file_size; + + return file_data; +} + +//----------------------------------------------------------------------------- +// ImGuiStorage +//----------------------------------------------------------------------------- + +// Helper: Key->value storage +void ImGuiStorage::Clear() +{ + Data.clear(); +} + +// std::lower_bound but without the bullshit +static ImVector::iterator LowerBound(ImVector& data, ImGuiID key) +{ + ImVector::iterator first = data.begin(); + ImVector::iterator last = data.end(); + int count = (int)(last - first); + while (count > 0) + { + int count2 = count / 2; + ImVector::iterator mid = first + count2; + if (mid->key < key) + { + first = ++mid; + count -= count2 + 1; + } + else + { + count = count2; + } + } + return first; +} + +int ImGuiStorage::GetInt(ImGuiID key, int default_val) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_i; +} + +bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const +{ + return GetInt(key, default_val ? 1 : 0) != 0; +} + +float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return default_val; + return it->val_f; +} + +void* ImGuiStorage::GetVoidPtr(ImGuiID key) const +{ + ImVector::iterator it = LowerBound(const_cast&>(Data), key); + if (it == Data.end() || it->key != key) + return NULL; + return it->val_p; +} + +// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. +int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_i; +} + +bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) +{ + return (bool*)GetIntRef(key, default_val ? 1 : 0); +} + +float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_f; +} + +void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + it = Data.insert(it, Pair(key, default_val)); + return &it->val_p; +} + +// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) +void ImGuiStorage::SetInt(ImGuiID key, int val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_i = val; +} + +void ImGuiStorage::SetBool(ImGuiID key, bool val) +{ + SetInt(key, val ? 1 : 0); +} + +void ImGuiStorage::SetFloat(ImGuiID key, float val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_f = val; +} + +void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) +{ + ImVector::iterator it = LowerBound(Data, key); + if (it == Data.end() || it->key != key) + { + Data.insert(it, Pair(key, val)); + return; + } + it->val_p = val; +} + +void ImGuiStorage::SetAllInt(int v) +{ + for (int i = 0; i < Data.Size; i++) + Data[i].val_i = v; +} + +//----------------------------------------------------------------------------- +// ImGuiTextFilter +//----------------------------------------------------------------------------- + +// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) +{ + if (default_filter) + { + ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); + Build(); + } + else + { + InputBuf[0] = 0; + CountGrep = 0; + } +} + +bool ImGuiTextFilter::Draw(const char* label, float width) +{ + if (width != 0.0f) + ImGui::PushItemWidth(width); + bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); + if (width != 0.0f) + ImGui::PopItemWidth(); + if (value_changed) + Build(); + return value_changed; +} + +void ImGuiTextFilter::TextRange::split(char separator, ImVector& out) +{ + out.resize(0); + const char* wb = b; + const char* we = wb; + while (we < e) + { + if (*we == separator) + { + out.push_back(TextRange(wb, we)); + wb = we + 1; + } + we++; + } + if (wb != we) + out.push_back(TextRange(wb, we)); +} + +void ImGuiTextFilter::Build() +{ + Filters.resize(0); + TextRange input_range(InputBuf, InputBuf+strlen(InputBuf)); + input_range.split(',', Filters); + + CountGrep = 0; + for (int i = 0; i != Filters.Size; i++) + { + Filters[i].trim_blanks(); + if (Filters[i].empty()) + continue; + if (Filters[i].front() != '-') + CountGrep += 1; + } +} + +bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const +{ + if (Filters.empty()) + return true; + + if (text == NULL) + text = ""; + + for (int i = 0; i != Filters.Size; i++) + { + const TextRange& f = Filters[i]; + if (f.empty()) + continue; + if (f.front() == '-') + { + // Subtract + if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL) + return false; + } + else + { + // Grep + if (ImStristr(text, text_end, f.begin(), f.end()) != NULL) + return true; + } + } + + // Implicit * grep + if (CountGrep == 0) + return true; + + return false; +} + +//----------------------------------------------------------------------------- +// ImGuiTextBuffer +//----------------------------------------------------------------------------- + +// On some platform vsnprintf() takes va_list by reference and modifies it. +// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. +#ifndef va_copy +#define va_copy(dest, src) (dest = src) +#endif + +// Helper: Text buffer for logging/accumulating text +void ImGuiTextBuffer::appendv(const char* fmt, va_list args) +{ + va_list args_copy; + va_copy(args_copy, args); + + int len = vsnprintf(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. + if (len <= 0) + return; + + const int write_off = Buf.Size; + const int needed_sz = write_off + len; + if (write_off + len >= Buf.Capacity) + { + int double_capacity = Buf.Capacity * 2; + Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); + } + + Buf.resize(needed_sz); + ImFormatStringV(&Buf[write_off] - 1, len+1, fmt, args_copy); +} + +void ImGuiTextBuffer::append(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + appendv(fmt, args); + va_end(args); +} + +//----------------------------------------------------------------------------- +// ImGuiSimpleColumns +//----------------------------------------------------------------------------- + +ImGuiSimpleColumns::ImGuiSimpleColumns() +{ + Count = 0; + Spacing = Width = NextWidth = 0.0f; + memset(Pos, 0, sizeof(Pos)); + memset(NextWidths, 0, sizeof(NextWidths)); +} + +void ImGuiSimpleColumns::Update(int count, float spacing, bool clear) +{ + IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); + Count = count; + Width = NextWidth = 0.0f; + Spacing = spacing; + if (clear) memset(NextWidths, 0, sizeof(NextWidths)); + for (int i = 0; i < Count; i++) + { + if (i > 0 && NextWidths[i] > 0.0f) + Width += Spacing; + Pos[i] = (float)(int)Width; + Width += NextWidths[i]; + NextWidths[i] = 0.0f; + } +} + +float ImGuiSimpleColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double +{ + NextWidth = 0.0f; + NextWidths[0] = ImMax(NextWidths[0], w0); + NextWidths[1] = ImMax(NextWidths[1], w1); + NextWidths[2] = ImMax(NextWidths[2], w2); + for (int i = 0; i < 3; i++) + NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); + return ImMax(Width, NextWidth); +} + +float ImGuiSimpleColumns::CalcExtraSpace(float avail_w) +{ + return ImMax(0.0f, avail_w - Width); +} + +//----------------------------------------------------------------------------- +// ImGuiListClipper +//----------------------------------------------------------------------------- + +static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) +{ + // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. + // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. Consider moving within SetCursorXXX functions? + ImGui::SetCursorPosY(pos_y); + ImGuiWindow* window = ImGui::GetCurrentWindow(); + window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. + window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. + if (window->DC.ColumnsCount > 1) + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly +} + +// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 +// Use case B: Begin() called from constructor with items_height>0 +// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. +void ImGuiListClipper::Begin(int count, float items_height) +{ + StartPosY = ImGui::GetCursorPosY(); + ItemsHeight = items_height; + ItemsCount = count; + StepNo = 0; + DisplayEnd = DisplayStart = -1; + if (ItemsHeight > 0.0f) + { + ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display + if (DisplayStart > 0) + SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor + StepNo = 2; + } +} + +void ImGuiListClipper::End() +{ + if (ItemsCount < 0) + return; + // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. + if (ItemsCount < INT_MAX) + SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor + ItemsCount = -1; + StepNo = 3; +} + +bool ImGuiListClipper::Step() +{ + if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems) + { + ItemsCount = -1; + return false; + } + if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. + { + DisplayStart = 0; + DisplayEnd = 1; + StartPosY = ImGui::GetCursorPosY(); + StepNo = 1; + return true; + } + if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. + { + if (ItemsCount == 1) { ItemsCount = -1; return false; } + float items_height = ImGui::GetCursorPosY() - StartPosY; + IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically + Begin(ItemsCount-1, items_height); + DisplayStart++; + DisplayEnd++; + StepNo = 3; + return true; + } + if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3. + { + IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0); + StepNo = 3; + return true; + } + if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. + End(); + return false; +} + +//----------------------------------------------------------------------------- +// ImGuiWindow +//----------------------------------------------------------------------------- + +ImGuiWindow::ImGuiWindow(const char* name) +{ + Name = ImStrdup(name); + ID = ImHash(name, 0); + IDStack.push_back(ID); + MoveId = GetID("#MOVE"); + + Flags = 0; + IndexWithinParent = 0; + PosFloat = Pos = ImVec2(0.0f, 0.0f); + Size = SizeFull = ImVec2(0.0f, 0.0f); + SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); + WindowPadding = ImVec2(0.0f, 0.0f); + Scroll = ImVec2(0.0f, 0.0f); + ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); + ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); + ScrollbarX = ScrollbarY = false; + ScrollbarSizes = ImVec2(0.0f, 0.0f); + BorderSize = 0.0f; + Active = WasActive = false; + Accessed = false; + Collapsed = false; + SkipItems = false; + BeginCount = 0; + PopupId = 0; + AutoFitFramesX = AutoFitFramesY = -1; + AutoFitOnlyGrows = false; + AutoPosLastDirection = -1; + HiddenFrames = 0; + SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiSetCond_Always | ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing; + SetWindowPosCenterWanted = false; + + LastFrameActive = -1; + ItemWidthDefault = 0.0f; + FontWindowScale = 1.0f; + + DrawList = (ImDrawList*)ImGui::MemAlloc(sizeof(ImDrawList)); + IM_PLACEMENT_NEW(DrawList) ImDrawList(); + DrawList->_OwnerName = Name; + RootWindow = NULL; + RootNonPopupWindow = NULL; + ParentWindow = NULL; + + FocusIdxAllCounter = FocusIdxTabCounter = -1; + FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; + FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; +} + +ImGuiWindow::~ImGuiWindow() +{ + DrawList->~ImDrawList(); + ImGui::MemFree(DrawList); + DrawList = NULL; + ImGui::MemFree(Name); + Name = NULL; +} + +ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetID(const void* ptr) +{ + ImGuiID seed = IDStack.back(); + ImGuiID id = ImHash(&ptr, sizeof(void*), seed); + ImGui::KeepAliveID(id); + return id; +} + +ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) +{ + ImGuiID seed = IDStack.back(); + return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); +} + +//----------------------------------------------------------------------------- +// Internal API exposed in imgui_internal.h +//----------------------------------------------------------------------------- + +static void SetCurrentWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow = window; + if (window) + g.FontSize = window->CalcFontSize(); +} + +ImGuiWindow* ImGui::GetParentWindow() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.CurrentWindowStack.Size >= 2); + return g.CurrentWindowStack[(unsigned int)g.CurrentWindowStack.Size - 2]; +} + +void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window = NULL) +{ + ImGuiContext& g = *GImGui; + g.ActiveId = id; + g.ActiveIdAllowOverlap = false; + g.ActiveIdIsJustActivated = true; + if (id) + g.ActiveIdIsAlive = true; + g.ActiveIdWindow = window; +} + +void ImGui::SetHoveredID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + g.HoveredId = id; + g.HoveredIdAllowOverlap = false; +} + +void ImGui::KeepAliveID(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId == id) + g.ActiveIdIsAlive = true; +} + +// Advance cursor given item size for layout. +void ImGui::ItemSize(const ImVec2& size, float text_offset_y) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + // Always align ourselves on pixel boundaries + ImGuiContext& g = *GImGui; + const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); + const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); + window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); + window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + + //window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // Debug + + window->DC.PrevLineHeight = line_height; + window->DC.PrevLineTextBaseOffset = text_base_offset; + window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; +} + +void ImGui::ItemSize(const ImRect& bb, float text_offset_y) +{ + ItemSize(bb.GetSize(), text_offset_y); +} + +// Declare item bounding box for clipping and interaction. +// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface +// declares their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). +bool ImGui::ItemAdd(const ImRect& bb, const ImGuiID* id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.LastItemId = id ? *id : 0; + window->DC.LastItemRect = bb; + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = false; + if (IsClippedEx(bb, id, false)) + return false; + + // This is a sensible default, but widgets are free to override it after calling ItemAdd() + ImGuiContext& g = *GImGui; + if (IsMouseHoveringRect(bb.Min, bb.Max)) + { + // Matching the behavior of IsHovered() but allow if ActiveId==window->MoveID (we clicked on the window background) + // So that clicking on items with no active id such as Text() still returns true with IsItemHovered() + window->DC.LastItemHoveredRect = true; + if (g.HoveredRootWindow == window->RootWindow) + if (g.ActiveId == 0 || (id && g.ActiveId == *id) || g.ActiveIdAllowOverlap || (g.ActiveId == window->MoveId)) + if (IsWindowContentHoverable(window)) + window->DC.LastItemHoveredAndUsable = true; + } + + return true; +} + +bool ImGui::IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + + if (!bb.Overlaps(window->ClipRect)) + if (!id || *id != GImGui->ActiveId) + if (clip_even_when_logged || !g.LogEnabled) + return true; + return false; +} + +// NB: This is an internal helper. The user-facing IsItemHovered() is using data emitted from ItemAdd(), with a slightly different logic. +bool ImGui::IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs) +{ + ImGuiContext& g = *GImGui; + if (g.HoveredId == 0 || g.HoveredId == id || g.HoveredIdAllowOverlap) + { + ImGuiWindow* window = GetCurrentWindowRead(); + if (g.HoveredWindow == window || (flatten_childs && g.HoveredRootWindow == window->RootWindow)) + if ((g.ActiveId == 0 || g.ActiveId == id || g.ActiveIdAllowOverlap) && IsMouseHoveringRect(bb.Min, bb.Max)) + if (IsWindowContentHoverable(g.HoveredRootWindow)) + return true; + } + return false; +} + +bool ImGui::FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop) +{ + ImGuiContext& g = *GImGui; + + const bool allow_keyboard_focus = window->DC.AllowKeyboardFocus; + window->FocusIdxAllCounter++; + if (allow_keyboard_focus) + window->FocusIdxTabCounter++; + + // Process keyboard input at this point: TAB, Shift-TAB switch focus + // We can always TAB out of a widget that doesn't allow tabbing in. + if (tab_stop && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && is_active && IsKeyPressedMap(ImGuiKey_Tab)) + { + // Modulo on index will be applied at the end of frame once we've got the total counter of items. + window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); + } + + if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) + return true; + + if (allow_keyboard_focus) + if (window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) + return true; + + return false; +} + +void ImGui::FocusableItemUnregister(ImGuiWindow* window) +{ + window->FocusIdxAllCounter--; + window->FocusIdxTabCounter--; +} + +ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) +{ + ImGuiContext& g = *GImGui; + ImVec2 content_max; + if (size.x < 0.0f || size.y < 0.0f) + content_max = g.CurrentWindow->Pos + GetContentRegionMax(); + if (size.x <= 0.0f) + size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x; + if (size.y <= 0.0f) + size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y; + return size; +} + +float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) +{ + if (wrap_pos_x < 0.0f) + return 0.0f; + + ImGuiWindow* window = GetCurrentWindowRead(); + if (wrap_pos_x == 0.0f) + wrap_pos_x = GetContentRegionMax().x + window->Pos.x; + else if (wrap_pos_x > 0.0f) + wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space + + return ImMax(wrap_pos_x - pos.x, 1.0f); +} + +//----------------------------------------------------------------------------- + +void* ImGui::MemAlloc(size_t sz) +{ + GImGui->IO.MetricsAllocs++; + return GImGui->IO.MemAllocFn(sz); +} + +void ImGui::MemFree(void* ptr) +{ + if (ptr) GImGui->IO.MetricsAllocs--; + return GImGui->IO.MemFreeFn(ptr); +} + +const char* ImGui::GetClipboardText() +{ + return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : ""; +} + +void ImGui::SetClipboardText(const char* text) +{ + if (GImGui->IO.SetClipboardTextFn) + GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text); +} + +const char* ImGui::GetVersion() +{ + return IMGUI_VERSION; +} + +// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself +// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module +ImGuiContext* ImGui::GetCurrentContext() +{ + return GImGui; +} + +void ImGui::SetCurrentContext(ImGuiContext* ctx) +{ + GImGui = ctx; +} + +ImGuiContext* ImGui::CreateContext(void* (*malloc_fn)(size_t), void (*free_fn)(void*)) +{ + if (!malloc_fn) malloc_fn = malloc; + ImGuiContext* ctx = (ImGuiContext*)malloc_fn(sizeof(ImGuiContext)); + IM_PLACEMENT_NEW(ctx) ImGuiContext(); + ctx->IO.MemAllocFn = malloc_fn; + ctx->IO.MemFreeFn = free_fn ? free_fn : free; + return ctx; +} + +void ImGui::DestroyContext(ImGuiContext* ctx) +{ + void (*free_fn)(void*) = ctx->IO.MemFreeFn; + ctx->~ImGuiContext(); + free_fn(ctx); + if (GImGui == ctx) + GImGui = NULL; +} + +ImGuiIO& ImGui::GetIO() +{ + return GImGui->IO; +} + +ImGuiStyle& ImGui::GetStyle() +{ + return GImGui->Style; +} + +// Same value as passed to your RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame() +ImDrawData* ImGui::GetDrawData() +{ + return GImGui->RenderDrawData.Valid ? &GImGui->RenderDrawData : NULL; +} + +float ImGui::GetTime() +{ + return GImGui->Time; +} + +int ImGui::GetFrameCount() +{ + return GImGui->FrameCount; +} + +void ImGui::NewFrame() +{ + ImGuiContext& g = *GImGui; + + // Check user data + IM_ASSERT(g.IO.DeltaTime >= 0.0f); // Need a positive DeltaTime (zero is tolerated but will cause some timing issues) + IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f); + IM_ASSERT(g.IO.Fonts->Fonts.Size > 0); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(g.Style.CurveTessellationTol > 0.0f); // Invalid style setting + + if (!g.Initialized) + { + // Initialize on first frame + g.LogClipboard = (ImGuiTextBuffer*)ImGui::MemAlloc(sizeof(ImGuiTextBuffer)); + IM_PLACEMENT_NEW(g.LogClipboard) ImGuiTextBuffer(); + + IM_ASSERT(g.Settings.empty()); + LoadSettings(); + g.Initialized = true; + } + + SetCurrentFont(g.IO.Fonts->Fonts[0]); + + g.Time += g.IO.DeltaTime; + g.FrameCount += 1; + g.Tooltip[0] = '\0'; + g.OverlayDrawList.Clear(); + g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); + g.OverlayDrawList.PushClipRectFullScreen(); + + // Mark rendering data as invalid to prevent user who may have a handle on it to use it + g.RenderDrawData.Valid = false; + g.RenderDrawData.CmdLists = NULL; + g.RenderDrawData.CmdListsCount = g.RenderDrawData.TotalVtxCount = g.RenderDrawData.TotalIdxCount = 0; + + // Update inputs state + if (g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) + g.IO.MousePos = ImVec2(-9999.0f, -9999.0f); + if ((g.IO.MousePos.x < 0 && g.IO.MousePos.y < 0) || (g.IO.MousePosPrev.x < 0 && g.IO.MousePosPrev.y < 0)) // if mouse just appeared or disappeared (negative coordinate) we cancel out movement in MouseDelta + g.IO.MouseDelta = ImVec2(0.0f, 0.0f); + else + g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; + g.IO.MousePosPrev = g.IO.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + { + g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; + g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; + g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; + g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; + g.IO.MouseDoubleClicked[i] = false; + if (g.IO.MouseClicked[i]) + { + if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) + { + if (ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i]) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) + g.IO.MouseDoubleClicked[i] = true; + g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click + } + else + { + g.IO.MouseClickedTime[i] = g.Time; + } + g.IO.MouseClickedPos[i] = g.IO.MousePos; + g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (g.IO.MouseDown[i]) + { + g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(g.IO.MousePos - g.IO.MouseClickedPos[i])); + } + } + memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); + for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) + g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; + + // Calculate frame-rate for the user, as a purely luxurious feature + g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; + g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; + g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); + g.IO.Framerate = 1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame)); + + // Clear reference to active widget if the widget isn't alive anymore + g.HoveredIdPreviousFrame = g.HoveredId; + g.HoveredId = 0; + g.HoveredIdAllowOverlap = false; + if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) + SetActiveID(0); + g.ActiveIdPreviousFrame = g.ActiveId; + g.ActiveIdIsAlive = false; + g.ActiveIdIsJustActivated = false; + + // Handle user moving window (at the beginning of the frame to avoid input lag or sheering). Only valid for root windows. + if (g.MovedWindowMoveId && g.MovedWindowMoveId == g.ActiveId) + { + KeepAliveID(g.MovedWindowMoveId); + IM_ASSERT(g.MovedWindow && g.MovedWindow->RootWindow); + IM_ASSERT(g.MovedWindow->RootWindow->MoveId == g.MovedWindowMoveId); + if (g.IO.MouseDown[0]) + { + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoMove)) + { + g.MovedWindow->PosFloat += g.IO.MouseDelta; + if (!(g.MovedWindow->Flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + } + FocusWindow(g.MovedWindow); + } + else + { + SetActiveID(0); + g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } + } + else + { + g.MovedWindow = NULL; + g.MovedWindowMoveId = 0; + } + + // Delay saving settings so we don't spam disk too much + if (g.SettingsDirtyTimer > 0.0f) + { + g.SettingsDirtyTimer -= g.IO.DeltaTime; + if (g.SettingsDirtyTimer <= 0.0f) + SaveSettings(); + } + + // Find the window we are hovering. Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow + g.HoveredWindow = g.MovedWindow ? g.MovedWindow : FindHoveredWindow(g.IO.MousePos, false); + if (g.HoveredWindow && (g.HoveredWindow->Flags & ImGuiWindowFlags_ChildWindow)) + g.HoveredRootWindow = g.HoveredWindow->RootWindow; + else + g.HoveredRootWindow = g.MovedWindow ? g.MovedWindow->RootWindow : FindHoveredWindow(g.IO.MousePos, true); + + if (ImGuiWindow* modal_window = GetFrontMostModalRootWindow()) + { + g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); + ImGuiWindow* window = g.HoveredRootWindow; + while (window && window != modal_window) + window = window->ParentWindow; + if (!window) + g.HoveredRootWindow = g.HoveredWindow = NULL; + } + else + { + g.ModalWindowDarkeningRatio = 0.0f; + } + + // Are we using inputs? Tell user so they can capture/discard the inputs away from the rest of their application. + // When clicking outside of a window we assume the click is owned by the application and won't request capture. We need to track click ownership. + int mouse_earliest_button_down = -1; + bool mouse_any_down = false; + for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) + { + if (g.IO.MouseClicked[i]) + g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); + mouse_any_down |= g.IO.MouseDown[i]; + if (g.IO.MouseDown[i]) + if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[mouse_earliest_button_down] > g.IO.MouseClickedTime[i]) + mouse_earliest_button_down = i; + } + bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; + if (g.CaptureMouseNextFrame != -1) + g.IO.WantCaptureMouse = (g.CaptureMouseNextFrame != 0); + else + g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.ActiveId != 0) || (!g.OpenPopupStack.empty()); + g.IO.WantCaptureKeyboard = (g.CaptureKeyboardNextFrame != -1) ? (g.CaptureKeyboardNextFrame != 0) : (g.ActiveId != 0); + g.IO.WantTextInput = (g.ActiveId != 0 && g.InputTextState.Id == g.ActiveId); + g.MouseCursor = ImGuiMouseCursor_Arrow; + g.CaptureMouseNextFrame = g.CaptureKeyboardNextFrame = -1; + g.OsImePosRequest = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default + + // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. + if (!mouse_avail_to_imgui) + g.HoveredWindow = g.HoveredRootWindow = NULL; + + // Scale & Scrolling + if (g.HoveredWindow && g.IO.MouseWheel != 0.0f && !g.HoveredWindow->Collapsed) + { + ImGuiWindow* window = g.HoveredWindow; + if (g.IO.KeyCtrl) + { + if (g.IO.FontAllowUserScaling) + { + // Zoom / Scale window + float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + window->Pos += offset; + window->PosFloat += offset; + window->Size *= scale; + window->SizeFull *= scale; + } + } + else if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) + { + // Scroll + const int scroll_lines = (window->Flags & ImGuiWindowFlags_ComboBox) ? 3 : 5; + SetWindowScrollY(window, window->Scroll.y - g.IO.MouseWheel * window->CalcFontSize() * scroll_lines); + } + } + + // Pressing TAB activate widget focus + // NB: Don't discard FocusedWindow if it isn't active, so that a window that go on/off programatically won't lose its keyboard focus. + if (g.ActiveId == 0 && g.FocusedWindow != NULL && g.FocusedWindow->Active && IsKeyPressedMap(ImGuiKey_Tab, false)) + g.FocusedWindow->FocusIdxTabRequestNext = 0; + + // Mark all windows as not visible + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + window->WasActive = window->Active; + window->Active = false; + window->Accessed = false; + } + + // Closing the focused window restore focus to the first active root window in descending z-order + if (g.FocusedWindow && !g.FocusedWindow->WasActive) + for (int i = g.Windows.Size-1; i >= 0; i--) + if (g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) + { + FocusWindow(g.Windows[i]); + break; + } + + // No window should be open at the beginning of the frame. + // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. + g.CurrentWindowStack.resize(0); + g.CurrentPopupStack.resize(0); + CloseInactivePopups(); + + // Create implicit window - we will only render it if the user has added something to it. + ImGui::SetNextWindowSize(ImVec2(400,400), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Debug"); +} + +// NB: behavior of ImGui after Shutdown() is not tested/guaranteed at the moment. This function is merely here to free heap allocations. +void ImGui::Shutdown() +{ + ImGuiContext& g = *GImGui; + + // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) + if (g.IO.Fonts) // Testing for NULL to allow user to NULLify in case of running Shutdown() on multiple contexts. Bit hacky. + g.IO.Fonts->Clear(); + + // Cleanup of other data are conditional on actually having used ImGui. + if (!g.Initialized) + return; + + SaveSettings(); + + for (int i = 0; i < g.Windows.Size; i++) + { + g.Windows[i]->~ImGuiWindow(); + ImGui::MemFree(g.Windows[i]); + } + g.Windows.clear(); + g.WindowsSortBuffer.clear(); + g.CurrentWindow = NULL; + g.CurrentWindowStack.clear(); + g.FocusedWindow = NULL; + g.HoveredWindow = NULL; + g.HoveredRootWindow = NULL; + g.ActiveIdWindow = NULL; + g.MovedWindow = NULL; + for (int i = 0; i < g.Settings.Size; i++) + ImGui::MemFree(g.Settings[i].Name); + g.Settings.clear(); + g.ColorModifiers.clear(); + g.StyleModifiers.clear(); + g.FontStack.clear(); + g.OpenPopupStack.clear(); + g.CurrentPopupStack.clear(); + g.SetNextWindowSizeConstraintCallback = NULL; + g.SetNextWindowSizeConstraintCallbackUserData = NULL; + for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].clear(); + g.OverlayDrawList.ClearFreeMemory(); + g.ColorEditModeStorage.Clear(); + if (g.PrivateClipboard) + { + ImGui::MemFree(g.PrivateClipboard); + g.PrivateClipboard = NULL; + } + g.InputTextState.Text.clear(); + g.InputTextState.InitialText.clear(); + g.InputTextState.TempTextBuffer.clear(); + + if (g.LogFile && g.LogFile != stdout) + { + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard) + { + g.LogClipboard->~ImGuiTextBuffer(); + ImGui::MemFree(g.LogClipboard); + } + + g.Initialized = false; +} + +static ImGuiIniData* FindWindowSettings(const char* name) +{ + ImGuiContext& g = *GImGui; + ImGuiID id = ImHash(name, 0); + for (int i = 0; i != g.Settings.Size; i++) + { + ImGuiIniData* ini = &g.Settings[i]; + if (ini->Id == id) + return ini; + } + return NULL; +} + +static ImGuiIniData* AddWindowSettings(const char* name) +{ + GImGui->Settings.resize(GImGui->Settings.Size + 1); + ImGuiIniData* ini = &GImGui->Settings.back(); + ini->Name = ImStrdup(name); + ini->Id = ImHash(name, 0); + ini->Collapsed = false; + ini->Pos = ImVec2(FLT_MAX,FLT_MAX); + ini->Size = ImVec2(0,0); + return ini; +} + +// Zero-tolerance, poor-man .ini parsing +// FIXME: Write something less rubbish +static void LoadSettings() +{ + ImGuiContext& g = *GImGui; + const char* filename = g.IO.IniFilename; + if (!filename) + return; + + int file_size; + char* file_data = (char*)ImLoadFileToMemory(filename, "rb", &file_size, 1); + if (!file_data) + return; + + ImGuiIniData* settings = NULL; + const char* buf_end = file_data + file_size; + for (const char* line_start = file_data; line_start < buf_end; ) + { + const char* line_end = line_start; + while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') + line_end++; + + if (line_start[0] == '[' && line_end > line_start && line_end[-1] == ']') + { + char name[64]; + ImFormatString(name, IM_ARRAYSIZE(name), "%.*s", (int)(line_end-line_start-2), line_start+1); + settings = FindWindowSettings(name); + if (!settings) + settings = AddWindowSettings(name); + } + else if (settings) + { + float x, y; + int i; + if (sscanf(line_start, "Pos=%f,%f", &x, &y) == 2) + settings->Pos = ImVec2(x, y); + else if (sscanf(line_start, "Size=%f,%f", &x, &y) == 2) + settings->Size = ImMax(ImVec2(x, y), g.Style.WindowMinSize); + else if (sscanf(line_start, "Collapsed=%d", &i) == 1) + settings->Collapsed = (i != 0); + } + + line_start = line_end+1; + } + + ImGui::MemFree(file_data); +} + +static void SaveSettings() +{ + ImGuiContext& g = *GImGui; + const char* filename = g.IO.IniFilename; + if (!filename) + return; + + // Gather data from windows that were active during this session + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Flags & ImGuiWindowFlags_NoSavedSettings) + continue; + ImGuiIniData* settings = FindWindowSettings(window->Name); + settings->Pos = window->Pos; + settings->Size = window->SizeFull; + settings->Collapsed = window->Collapsed; + } + + // Write .ini file + // If a window wasn't opened in this session we preserve its settings + FILE* f = fopen(filename, "wt"); + if (!f) + return; + for (int i = 0; i != g.Settings.Size; i++) + { + const ImGuiIniData* settings = &g.Settings[i]; + if (settings->Pos.x == FLT_MAX) + continue; + const char* name = settings->Name; + if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + name = p; + fprintf(f, "[%s]\n", name); + fprintf(f, "Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); + fprintf(f, "Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); + fprintf(f, "Collapsed=%d\n", settings->Collapsed); + fprintf(f, "\n"); + } + + fclose(f); +} + +static void MarkSettingsDirty() +{ + ImGuiContext& g = *GImGui; + if (g.SettingsDirtyTimer <= 0.0f) + g.SettingsDirtyTimer = g.IO.IniSavingRate; +} + +// FIXME: Add a more explicit sort order in the window structure. +static int ChildWindowComparer(const void* lhs, const void* rhs) +{ + const ImGuiWindow* a = *(const ImGuiWindow**)lhs; + const ImGuiWindow* b = *(const ImGuiWindow**)rhs; + if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) + return d; + if (int d = (a->Flags & ImGuiWindowFlags_ComboBox) - (b->Flags & ImGuiWindowFlags_ComboBox)) + return d; + return (a->IndexWithinParent - b->IndexWithinParent); +} + +static void AddWindowToSortedBuffer(ImVector& out_sorted_windows, ImGuiWindow* window) +{ + out_sorted_windows.push_back(window); + if (window->Active) + { + int count = window->DC.ChildWindows.Size; + if (count > 1) + qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); + for (int i = 0; i < count; i++) + { + ImGuiWindow* child = window->DC.ChildWindows[i]; + if (child->Active) + AddWindowToSortedBuffer(out_sorted_windows, child); + } + } +} + +static void AddDrawListToRenderList(ImVector& out_render_list, ImDrawList* draw_list) +{ + if (draw_list->CmdBuffer.empty()) + return; + + // Remove trailing command if unused + ImDrawCmd& last_cmd = draw_list->CmdBuffer.back(); + if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL) + { + draw_list->CmdBuffer.pop_back(); + if (draw_list->CmdBuffer.empty()) + return; + } + + // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. + IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); + IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); + IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); + + // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = 2 bytes = 64K vertices) + // If this assert triggers because you are drawing lots of stuff manually, A) workaround by calling BeginChild()/EndChild() to put your draw commands in multiple draw lists, B) #define ImDrawIdx to a 'unsigned int' in imconfig.h and render accordingly. + IM_ASSERT((int64_t)draw_list->_VtxCurrentIdx <= ((int64_t)1L << (sizeof(ImDrawIdx)*8))); // Too many vertices in same ImDrawList. See comment above. + + out_render_list.push_back(draw_list); + GImGui->IO.MetricsRenderVertices += draw_list->VtxBuffer.Size; + GImGui->IO.MetricsRenderIndices += draw_list->IdxBuffer.Size; +} + +static void AddWindowToRenderList(ImVector& out_render_list, ImGuiWindow* window) +{ + AddDrawListToRenderList(out_render_list, window->DrawList); + for (int i = 0; i < window->DC.ChildWindows.Size; i++) + { + ImGuiWindow* child = window->DC.ChildWindows[i]; + if (!child->Active) // clipped children may have been marked not active + continue; + if ((child->Flags & ImGuiWindowFlags_Popup) && child->HiddenFrames > 0) + continue; + AddWindowToRenderList(out_render_list, child); + } +} + +// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result. +void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); + window->ClipRect = window->DrawList->_ClipRectStack.back(); +} + +void ImGui::PopClipRect() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->PopClipRect(); + window->ClipRect = window->DrawList->_ClipRectStack.back(); +} + +// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. +void ImGui::EndFrame() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.FrameCountEnded != g.FrameCount); // ImGui::EndFrame() called multiple times, or forgot to call ImGui::NewFrame() again + + // Render tooltip + if (g.Tooltip[0]) + { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(g.Tooltip); + ImGui::EndTooltip(); + } + + // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) + if (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.OsImePosRequest - g.OsImePosSet) > 0.0001f) + { + g.IO.ImeSetInputScreenPosFn((int)g.OsImePosRequest.x, (int)g.OsImePosRequest.y); + g.OsImePosSet = g.OsImePosRequest; + } + + // Hide implicit "Debug" window if it hasn't been used + IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls + if (g.CurrentWindow && !g.CurrentWindow->Accessed) + g.CurrentWindow->Active = false; + ImGui::End(); + + // Click to focus window and start moving (after we're done with all our widgets) + if (g.ActiveId == 0 && g.HoveredId == 0 && g.IO.MouseClicked[0]) + { + if (!(g.FocusedWindow && !g.FocusedWindow->WasActive && g.FocusedWindow->Active)) // Unless we just made a popup appear + { + if (g.HoveredRootWindow != NULL) + { + FocusWindow(g.HoveredWindow); + if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove)) + { + g.MovedWindow = g.HoveredWindow; + g.MovedWindowMoveId = g.HoveredRootWindow->MoveId; + SetActiveID(g.MovedWindowMoveId, g.HoveredRootWindow); + } + } + else if (g.FocusedWindow != NULL && GetFrontMostModalRootWindow() == NULL) + { + // Clicking on void disable focus + FocusWindow(NULL); + } + } + } + + // Sort the window list so that all child windows are after their parent + // We cannot do that on FocusWindow() because childs may not exist yet + g.WindowsSortBuffer.resize(0); + g.WindowsSortBuffer.reserve(g.Windows.Size); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it + continue; + AddWindowToSortedBuffer(g.WindowsSortBuffer, window); + } + IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong + g.Windows.swap(g.WindowsSortBuffer); + + // Clear Input data for next frame + g.IO.MouseWheel = 0.0f; + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + + g.FrameCountEnded = g.FrameCount; +} + +void ImGui::Render() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + + if (g.FrameCountEnded != g.FrameCount) + ImGui::EndFrame(); + g.FrameCountRendered = g.FrameCount; + + // Skip render altogether if alpha is 0.0 + // Note that vertex buffers have been created and are wasted, so it is best practice that you don't create windows in the first place, or consistently respond to Begin() returning false. + if (g.Style.Alpha > 0.0f) + { + // Gather windows to render + g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; + for (int i = 0; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + g.RenderDrawLists[i].resize(0); + for (int i = 0; i != g.Windows.Size; i++) + { + ImGuiWindow* window = g.Windows[i]; + if (window->Active && window->HiddenFrames <= 0 && (window->Flags & (ImGuiWindowFlags_ChildWindow)) == 0) + { + // FIXME: Generalize this with a proper layering system so e.g. user can draw in specific layers, below text, .. + g.IO.MetricsActiveWindows++; + if (window->Flags & ImGuiWindowFlags_Popup) + AddWindowToRenderList(g.RenderDrawLists[1], window); + else if (window->Flags & ImGuiWindowFlags_Tooltip) + AddWindowToRenderList(g.RenderDrawLists[2], window); + else + AddWindowToRenderList(g.RenderDrawLists[0], window); + } + } + + // Flatten layers + int n = g.RenderDrawLists[0].Size; + int flattened_size = n; + for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + flattened_size += g.RenderDrawLists[i].Size; + g.RenderDrawLists[0].resize(flattened_size); + for (int i = 1; i < IM_ARRAYSIZE(g.RenderDrawLists); i++) + { + ImVector& layer = g.RenderDrawLists[i]; + if (layer.empty()) + continue; + memcpy(&g.RenderDrawLists[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); + n += layer.Size; + } + + // Draw software mouse cursor if requested + if (g.IO.MouseDrawCursor) + { + const ImGuiMouseCursorData& cursor_data = g.MouseCursorData[g.MouseCursor]; + const ImVec2 pos = g.IO.MousePos - cursor_data.HotOffset; + const ImVec2 size = cursor_data.Size; + const ImTextureID tex_id = g.IO.Fonts->TexID; + g.OverlayDrawList.PushTextureID(tex_id); + g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(1,0), pos+ImVec2(1,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos+ImVec2(2,0), pos+ImVec2(2,0) + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,48)); // Shadow + g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[1], cursor_data.TexUvMax[1], IM_COL32(0,0,0,255)); // Black border + g.OverlayDrawList.AddImage(tex_id, pos, pos + size, cursor_data.TexUvMin[0], cursor_data.TexUvMax[0], IM_COL32(255,255,255,255)); // White fill + g.OverlayDrawList.PopTextureID(); + } + if (!g.OverlayDrawList.VtxBuffer.empty()) + AddDrawListToRenderList(g.RenderDrawLists[0], &g.OverlayDrawList); + + // Setup draw data + g.RenderDrawData.Valid = true; + g.RenderDrawData.CmdLists = (g.RenderDrawLists[0].Size > 0) ? &g.RenderDrawLists[0][0] : NULL; + g.RenderDrawData.CmdListsCount = g.RenderDrawLists[0].Size; + g.RenderDrawData.TotalVtxCount = g.IO.MetricsRenderVertices; + g.RenderDrawData.TotalIdxCount = g.IO.MetricsRenderIndices; + + // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() + if (g.RenderDrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) + g.IO.RenderDrawListsFn(&g.RenderDrawData); + } +} + +const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) +{ + const char* text_display_end = text; + if (!text_end) + text_end = (const char*)-1; + + while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) + text_display_end++; + return text_display_end; +} + +// Pass text data straight to log (without being displayed) +void ImGui::LogText(const char* fmt, ...) +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + va_list args; + va_start(args, fmt); + if (g.LogFile) + { + vfprintf(g.LogFile, fmt, args); + } + else + { + g.LogClipboard->appendv(fmt, args); + } + va_end(args); +} + +// Internal version that takes a position to decide on newline placement and pad items according to their depth. +// We split text into individual lines to add current tree level padding +static void LogRenderedText(const ImVec2& ref_pos, const char* text, const char* text_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindowRead(); + + if (!text_end) + text_end = ImGui::FindRenderedTextEnd(text, text_end); + + const bool log_new_line = ref_pos.y > window->DC.LogLinePosY+1; + window->DC.LogLinePosY = ref_pos.y; + + const char* text_remaining = text; + if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth + g.LogStartDepth = window->DC.TreeDepth; + const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); + for (;;) + { + // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. + const char* line_end = text_remaining; + while (line_end < text_end) + if (*line_end == '\n') + break; + else + line_end++; + if (line_end >= text_end) + line_end = NULL; + + const bool is_first_line = (text == text_remaining); + bool is_last_line = false; + if (line_end == NULL) + { + is_last_line = true; + line_end = text_end; + } + if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) + { + const int char_count = (int)(line_end - text_remaining); + if (log_new_line || !is_first_line) + ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); + else + ImGui::LogText(" %.*s", char_count, text_remaining); + } + + if (is_last_line) + break; + text_remaining = line_end + 1; + } +} + +// Internal ImGui functions to render text +// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() +void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Hide anything after a '##' string + const char* text_display_end; + if (hide_text_after_hash) + { + text_display_end = FindRenderedTextEnd(text, text_end); + } + else + { + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + text_display_end = text_end; + } + + const int text_len = (int)(text_display_end - text); + if (text_len > 0) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); + if (g.LogEnabled) + LogRenderedText(pos, text, text_display_end); + } +} + +void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (!text_end) + text_end = text + strlen(text); // FIXME-OPT + + const int text_len = (int)(text_end - text); + if (text_len > 0) + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); + if (g.LogEnabled) + LogRenderedText(pos, text, text_end); + } +} + +// Default clip_rect uses (pos_min,pos_max) +// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) +void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) +{ + // Hide anything after a '##' string + const char* text_display_end = FindRenderedTextEnd(text, text_end); + const int text_len = (int)(text_display_end - text); + if (text_len == 0) + return; + + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Perform CPU side clipping for single clipped element to avoid using scissor state + ImVec2 pos = pos_min; + const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); + + const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; + const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; + bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); + if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min + need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); + + // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. + if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); + if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); + + // Render + if (need_clipping) + { + ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); + } + else + { + window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); + } + if (g.LogEnabled) + LogRenderedText(pos, text, text_display_end); +} + +// Render a rectangle shaped with optional rounding and borders +void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) +{ + ImGuiWindow* window = GetCurrentWindow(); + + window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); + if (border && (window->Flags & ImGuiWindowFlags_ShowBorders)) + { + window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding); + window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding); + } +} + +// Render a triangle to denote expanded/collapsed state +void ImGui::RenderCollapseTriangle(ImVec2 p_min, bool is_open, float scale) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + const float h = g.FontSize * 1.00f; + const float r = h * 0.40f * scale; + ImVec2 center = p_min + ImVec2(h*0.50f, h*0.50f*scale); + + ImVec2 a, b, c; + if (is_open) + { + center.y -= r*0.25f; + a = center + ImVec2(0,1)*r; + b = center + ImVec2(-0.866f,-0.5f)*r; + c = center + ImVec2(0.866f,-0.5f)*r; + } + else + { + a = center + ImVec2(1,0)*r; + b = center + ImVec2(-0.500f,0.866f)*r; + c = center + ImVec2(-0.500f,-0.866f)*r; + } + + window->DrawList->AddTriangleFilled(a, b, c, GetColorU32(ImGuiCol_Text)); +} + +void ImGui::RenderBullet(ImVec2 pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); +} + +void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + ImVec2 a, b, c; + float start_x = (float)(int)(g.FontSize * 0.307f + 0.5f); + float rem_third = (float)(int)((g.FontSize - start_x) / 3.0f); + a.x = pos.x + 0.5f + start_x; + b.x = a.x + rem_third; + c.x = a.x + rem_third * 3.0f; + b.y = pos.y - 1.0f + (float)(int)(g.Font->Ascent * (g.FontSize / g.Font->FontSize) + 0.5f) + (float)(int)(g.Font->DisplayOffset.y); + a.y = b.y - rem_third; + c.y = b.y - rem_third * 2.0f; + + window->DrawList->PathLineTo(a); + window->DrawList->PathLineTo(b); + window->DrawList->PathLineTo(c); + window->DrawList->PathStroke(col, false); +} + +// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. +// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) +ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) +{ + ImGuiContext& g = *GImGui; + + const char* text_display_end; + if (hide_text_after_double_hash) + text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string + else + text_display_end = text_end; + + ImFont* font = g.Font; + const float font_size = g.FontSize; + if (text == text_display_end) + return ImVec2(0.0f, font_size); + ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); + + // Cancel out character spacing for the last character of a line (it is baked into glyph->XAdvance field) + const float font_scale = font_size / font->FontSize; + const float character_spacing_x = 1.0f * font_scale; + if (text_size.x > 0.0f) + text_size.x -= character_spacing_x; + text_size.x = (float)(int)(text_size.x + 0.95f); + + return text_size; +} + +// Helper to calculate coarse clipping of large list of evenly sized items. +// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. +// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX +void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + if (g.LogEnabled) + { + // If logging is active, do not perform any clipping + *out_items_display_start = 0; + *out_items_display_end = items_count; + return; + } + if (window->SkipItems) + { + *out_items_display_start = *out_items_display_end = 0; + return; + } + + const ImVec2 pos = window->DC.CursorPos; + int start = (int)((window->ClipRect.Min.y - pos.y) / items_height); + int end = (int)((window->ClipRect.Max.y - pos.y) / items_height); + start = ImClamp(start, 0, items_count); + end = ImClamp(end + 1, start, items_count); + *out_items_display_start = start; + *out_items_display_end = end; +} + +// Find window given position, search front-to-back +// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. +static ImGuiWindow* FindHoveredWindow(ImVec2 pos, bool excluding_childs) +{ + ImGuiContext& g = *GImGui; + for (int i = g.Windows.Size-1; i >= 0; i--) + { + ImGuiWindow* window = g.Windows[i]; + if (!window->Active) + continue; + if (window->Flags & ImGuiWindowFlags_NoInputs) + continue; + if (excluding_childs && (window->Flags & ImGuiWindowFlags_ChildWindow) != 0) + continue; + + // Using the clipped AABB so a child window will typically be clipped by its parent. + ImRect bb(window->WindowRectClipped.Min - g.Style.TouchExtraPadding, window->WindowRectClipped.Max + g.Style.TouchExtraPadding); + if (bb.Contains(pos)) + return window; + } + return NULL; +} + +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + + // Clip + ImRect rect_clipped(r_min, r_max); + if (clip) + rect_clipped.Clip(window->ClipRect); + + // Expand for touch input + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + return rect_for_touch.Contains(g.IO.MousePos); +} + +bool ImGui::IsMouseHoveringWindow() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow == g.CurrentWindow; +} + +bool ImGui::IsMouseHoveringAnyWindow() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow != NULL; +} + +bool ImGui::IsPosHoveringAnyWindow(const ImVec2& pos) +{ + return FindHoveredWindow(pos, false) != NULL; +} + +static bool IsKeyPressedMap(ImGuiKey key, bool repeat) +{ + const int key_index = GImGui->IO.KeyMap[key]; + return ImGui::IsKeyPressed(key_index, repeat); +} + +int ImGui::GetKeyIndex(ImGuiKey key) +{ + IM_ASSERT(key >= 0 && key < ImGuiKey_COUNT); + return GImGui->IO.KeyMap[key]; +} + +bool ImGui::IsKeyDown(int key_index) +{ + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown)); + return GImGui->IO.KeysDown[key_index]; +} + +bool ImGui::IsKeyPressed(int key_index, bool repeat) +{ + ImGuiContext& g = *GImGui; + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + const float t = g.IO.KeysDownDuration[key_index]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) + { + float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; + if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + return true; + } + return false; +} + +bool ImGui::IsKeyReleased(int key_index) +{ + ImGuiContext& g = *GImGui; + if (key_index < 0) return false; + IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); + if (g.IO.KeysDownDurationPrev[key_index] >= 0.0f && !g.IO.KeysDown[key_index]) + return true; + return false; +} + +bool ImGui::IsMouseDown(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button]; +} + +bool ImGui::IsMouseClicked(int button, bool repeat) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + const float t = g.IO.MouseDownDuration[button]; + if (t == 0.0f) + return true; + + if (repeat && t > g.IO.KeyRepeatDelay) + { + float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; + if ((fmodf(t - delay, rate) > rate*0.5f) != (fmodf(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) + return true; + } + + return false; +} + +bool ImGui::IsMouseReleased(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseReleased[button]; +} + +bool ImGui::IsMouseDoubleClicked(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDoubleClicked[button]; +} + +bool ImGui::IsMouseDragging(int button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (!g.IO.MouseDown[button]) + return false; + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; +} + +ImVec2 ImGui::GetMousePos() +{ + return GImGui->IO.MousePos; +} + +// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! +ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() +{ + ImGuiContext& g = *GImGui; + if (g.CurrentPopupStack.Size > 0) + return g.OpenPopupStack[g.CurrentPopupStack.Size-1].MousePosOnOpen; + return g.IO.MousePos; +} + +ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (lock_threshold < 0.0f) + lock_threshold = g.IO.MouseDragThreshold; + if (g.IO.MouseDown[button]) + if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) + return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). + return ImVec2(0.0f, 0.0f); +} + +void ImGui::ResetMouseDragDelta(int button) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr + g.IO.MouseClickedPos[button] = g.IO.MousePos; +} + +ImGuiMouseCursor ImGui::GetMouseCursor() +{ + return GImGui->MouseCursor; +} + +void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) +{ + GImGui->MouseCursor = cursor_type; +} + +void ImGui::CaptureKeyboardFromApp(bool capture) +{ + GImGui->CaptureKeyboardNextFrame = capture ? 1 : 0; +} + +void ImGui::CaptureMouseFromApp(bool capture) +{ + GImGui->CaptureMouseNextFrame = capture ? 1 : 0; +} + +bool ImGui::IsItemHovered() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemHoveredAndUsable; +} + +bool ImGui::IsItemHoveredRect() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemHoveredRect; +} + +bool ImGui::IsItemActive() +{ + ImGuiContext& g = *GImGui; + if (g.ActiveId) + { + ImGuiWindow* window = GetCurrentWindowRead(); + return g.ActiveId == window->DC.LastItemId; + } + return false; +} + +bool ImGui::IsItemClicked(int mouse_button) +{ + return IsMouseClicked(mouse_button) && IsItemHovered(); +} + +bool ImGui::IsAnyItemHovered() +{ + return GImGui->HoveredId != 0 || GImGui->HoveredIdPreviousFrame != 0; +} + +bool ImGui::IsAnyItemActive() +{ + return GImGui->ActiveId != 0; +} + +bool ImGui::IsItemVisible() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImRect r(window->ClipRect); + return r.Overlaps(window->DC.LastItemRect); +} + +// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. +void ImGui::SetItemAllowOverlap() +{ + ImGuiContext& g = *GImGui; + if (g.HoveredId == g.CurrentWindow->DC.LastItemId) + g.HoveredIdAllowOverlap = true; + if (g.ActiveId == g.CurrentWindow->DC.LastItemId) + g.ActiveIdAllowOverlap = true; +} + +ImVec2 ImGui::GetItemRectMin() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.Min; +} + +ImVec2 ImGui::GetItemRectMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.Max; +} + +ImVec2 ImGui::GetItemRectSize() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.LastItemRect.GetSize(); +} + +ImVec2 ImGui::CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge, float outward) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImRect rect = window->DC.LastItemRect; + rect.Expand(outward); + return rect.GetClosestPoint(pos, on_edge); +} + +// Tooltip is stored and turned into a BeginTooltip()/EndTooltip() sequence at the end of the frame. Each call override previous value. +void ImGui::SetTooltipV(const char* fmt, va_list args) +{ + ImGuiContext& g = *GImGui; + ImFormatStringV(g.Tooltip, IM_ARRAYSIZE(g.Tooltip), fmt, args); +} + +void ImGui::SetTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + SetTooltipV(fmt, args); + va_end(args); +} + +static ImRect GetVisibleRect() +{ + ImGuiContext& g = *GImGui; + if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) + return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); + return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); +} + +void ImGui::BeginTooltip() +{ + ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + ImGui::Begin("##Tooltip", NULL, flags); +} + +void ImGui::EndTooltip() +{ + IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls + ImGui::End(); +} + +static bool IsPopupOpen(ImGuiID id) +{ + ImGuiContext& g = *GImGui; + return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; +} + +// Mark popup as open (toggle toward open state). +// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. +// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). +// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) +void ImGui::OpenPopupEx(const char* str_id, bool reopen_existing) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetID(str_id); + int current_stack_size = g.CurrentPopupStack.Size; + ImGuiPopupRef popup_ref = ImGuiPopupRef(id, window, window->GetID("##menus"), g.IO.MousePos); // Tagged as new ref because constructor sets Window to NULL (we are passing the ParentWindow info here) + if (g.OpenPopupStack.Size < current_stack_size + 1) + g.OpenPopupStack.push_back(popup_ref); + else if (reopen_existing || g.OpenPopupStack[current_stack_size].PopupId != id) + { + g.OpenPopupStack.resize(current_stack_size+1); + g.OpenPopupStack[current_stack_size] = popup_ref; + } +} + +void ImGui::OpenPopup(const char* str_id) +{ + ImGui::OpenPopupEx(str_id, false); +} + +static void CloseInactivePopups() +{ + ImGuiContext& g = *GImGui; + if (g.OpenPopupStack.empty()) + return; + + // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. + // Don't close our own child popup windows + int n = 0; + if (g.FocusedWindow) + { + for (n = 0; n < g.OpenPopupStack.Size; n++) + { + ImGuiPopupRef& popup = g.OpenPopupStack[n]; + if (!popup.Window) + continue; + IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); + if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) + continue; + + bool has_focus = false; + for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) + has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == g.FocusedWindow->RootWindow); + if (!has_focus) + break; + } + } + if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a useful breakpoint on the line below + g.OpenPopupStack.resize(n); +} + +static ImGuiWindow* GetFrontMostModalRootWindow() +{ + ImGuiContext& g = *GImGui; + for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) + if (ImGuiWindow* front_most_popup = g.OpenPopupStack.Data[n].Window) + if (front_most_popup->Flags & ImGuiWindowFlags_Modal) + return front_most_popup; + return NULL; +} + +static void ClosePopupToLevel(int remaining) +{ + ImGuiContext& g = *GImGui; + if (remaining > 0) + ImGui::FocusWindow(g.OpenPopupStack[remaining-1].Window); + else + ImGui::FocusWindow(g.OpenPopupStack[0].ParentWindow); + g.OpenPopupStack.resize(remaining); +} + +static void ClosePopup(ImGuiID id) +{ + if (!IsPopupOpen(id)) + return; + ImGuiContext& g = *GImGui; + ClosePopupToLevel(g.OpenPopupStack.Size - 1); +} + +// Close the popup we have begin-ed into. +void ImGui::CloseCurrentPopup() +{ + ImGuiContext& g = *GImGui; + int popup_idx = g.CurrentPopupStack.Size - 1; + if (popup_idx < 0 || popup_idx > g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) + return; + while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) + popup_idx--; + ClosePopupToLevel(popup_idx); +} + +static inline void ClearSetNextWindowData() +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosCond = g.SetNextWindowSizeCond = g.SetNextWindowContentSizeCond = g.SetNextWindowCollapsedCond = 0; + g.SetNextWindowSizeConstraint = g.SetNextWindowFocus = false; +} + +static bool BeginPopupEx(const char* str_id, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(str_id); + if (!IsPopupOpen(id)) + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize; + + char name[20]; + if (flags & ImGuiWindowFlags_ChildMenu) + ImFormatString(name, IM_ARRAYSIZE(name), "##menu_%d", g.CurrentPopupStack.Size); // Recycle windows based on depth + else + ImFormatString(name, IM_ARRAYSIZE(name), "##popup_%08x", id); // Not recycling, so we can close/open during the same frame + + bool is_open = ImGui::Begin(name, NULL, flags); + if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) + g.CurrentWindow->Flags &= ~ImGuiWindowFlags_ShowBorders; + if (!is_open) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + ImGui::EndPopup(); + + return is_open; +} + +bool ImGui::BeginPopup(const char* str_id) +{ + if (GImGui->OpenPopupStack.Size <= GImGui->CurrentPopupStack.Size) // Early out for performance + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + return BeginPopupEx(str_id, ImGuiWindowFlags_ShowBorders); +} + +bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + const ImGuiID id = window->GetID(name); + if (!IsPopupOpen(id)) + { + ClearSetNextWindowData(); // We behave like Begin() and need to consume those values + return false; + } + + ImGuiWindowFlags flags = extra_flags|ImGuiWindowFlags_Popup|ImGuiWindowFlags_Modal|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_NoSavedSettings; + bool is_open = ImGui::Begin(name, p_open, flags); + if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + { + ImGui::EndPopup(); + if (is_open) + ClosePopup(id); + return false; + } + + return is_open; +} + +void ImGui::EndPopup() +{ + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls + IM_ASSERT(GImGui->CurrentPopupStack.Size > 0); + ImGui::End(); + if (!(window->Flags & ImGuiWindowFlags_Modal)) + ImGui::PopStyleVar(); +} + +// This is a helper to handle the most simple case of associating one named popup to one given widget. +// 1. If you have many possible popups (for different "instances" of a same widget, or for wholly different widgets), you may be better off handling +// this yourself so you can store data relative to the widget that opened the popup instead of choosing different popup identifiers. +// 2. If you want right-clicking on the same item to reopen the popup at new location, use the same code replacing IsItemHovered() with IsItemHoveredRect() +// and passing true to the OpenPopupEx(). +// Because: hovering an item in a window below the popup won't normally trigger is hovering behavior/coloring. The pattern of ignoring the fact that +// the item isn't interactable (because it is blocked by the active popup) may useful in some situation when e.g. large canvas as one item, content of menu +// driven by click position. +bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) +{ + if (IsItemHovered() && IsMouseClicked(mouse_button)) + OpenPopupEx(str_id, false); + return BeginPopup(str_id); +} + +bool ImGui::BeginPopupContextWindow(bool also_over_items, const char* str_id, int mouse_button) +{ + if (!str_id) str_id = "window_context_menu"; + if (IsMouseHoveringWindow() && IsMouseClicked(mouse_button)) + if (also_over_items || !IsAnyItemHovered()) + OpenPopupEx(str_id, true); + return BeginPopup(str_id); +} + +bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) +{ + if (!str_id) str_id = "void_context_menu"; + if (!IsMouseHoveringAnyWindow() && IsMouseClicked(mouse_button)) + OpenPopupEx(str_id, true); + return BeginPopup(str_id); +} + +bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; + + const ImVec2 content_avail = GetContentRegionAvail(); + ImVec2 size = ImFloor(size_arg); + if (size.x <= 0.0f) + { + if (size.x == 0.0f) + flags |= ImGuiWindowFlags_ChildWindowAutoFitX; + size.x = ImMax(content_avail.x, 4.0f) - fabsf(size.x); // Arbitrary minimum zero-ish child size of 4.0f (0.0f causing too much issues) + } + if (size.y <= 0.0f) + { + if (size.y == 0.0f) + flags |= ImGuiWindowFlags_ChildWindowAutoFitY; + size.y = ImMax(content_avail.y, 4.0f) - fabsf(size.y); + } + if (border) + flags |= ImGuiWindowFlags_ShowBorders; + flags |= extra_flags; + + char title[256]; + ImFormatString(title, IM_ARRAYSIZE(title), "%s.%s", window->Name, str_id); + + bool ret = ImGui::Begin(title, NULL, size, -1.0f, flags); + + if (!(window->Flags & ImGuiWindowFlags_ShowBorders)) + GetCurrentWindow()->Flags &= ~ImGuiWindowFlags_ShowBorders; + + return ret; +} + +bool ImGui::BeginChild(ImGuiID id, const ImVec2& size, bool border, ImGuiWindowFlags extra_flags) +{ + char str_id[32]; + ImFormatString(str_id, IM_ARRAYSIZE(str_id), "child_%08x", id); + bool ret = ImGui::BeginChild(str_id, size, border, extra_flags); + return ret; +} + +void ImGui::EndChild() +{ + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss + if ((window->Flags & ImGuiWindowFlags_ComboBox) || window->BeginCount > 1) + { + ImGui::End(); + } + else + { + // When using auto-filling child window, we don't provide full width/height to ItemSize so that it doesn't feed back into automatic size-fitting. + ImVec2 sz = GetWindowSize(); + if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitX) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f + sz.x = ImMax(4.0f, sz.x); + if (window->Flags & ImGuiWindowFlags_ChildWindowAutoFitY) + sz.y = ImMax(4.0f, sz.y); + + ImGui::End(); + + window = GetCurrentWindow(); + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + sz); + ItemSize(sz); + ItemAdd(bb, NULL); + } +} + +// Helper to create a child window / scrolling region that looks like a normal widget frame. +bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + ImGui::PushStyleColor(ImGuiCol_ChildWindowBg, style.Colors[ImGuiCol_FrameBg]); + ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, style.FrameRounding); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); + return ImGui::BeginChild(id, size, (g.CurrentWindow->Flags & ImGuiWindowFlags_ShowBorders) ? true : false, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); +} + +void ImGui::EndChildFrame() +{ + ImGui::EndChild(); + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(); +} + +// Save and compare stack sizes on Begin()/End() to detect usage errors +static void CheckStacksSize(ImGuiWindow* window, bool write) +{ + // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) + ImGuiContext& g = *GImGui; + int* p_backup = &window->DC.StackSizesBackup[0]; + { + int current = window->IDStack.Size; + if (write) + *p_backup = current; + else + IM_ASSERT(*p_backup == current && "PushID/PopID Mismatch!"); + p_backup++; + } // User forgot PopID() + + { + int current = window->DC.GroupStack.Size; + if (write) + *p_backup = current; + else + IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); + p_backup++; + } // User forgot EndGroup() + + { + int current = g.CurrentPopupStack.Size; + if (write) + *p_backup = current; + else + IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); + p_backup++; + }// User forgot EndPopup()/EndMenu() + + { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // User forgot PopStyleColor() + { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // User forgot PopStyleVar() + { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushFont/PopFont Mismatch!"); p_backup++; } // User forgot PopFont() + IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); +} + +static ImVec2 FindBestPopupWindowPos(const ImVec2& base_pos, const ImVec2& size, int* last_dir, const ImRect& r_inner) +{ + const ImGuiStyle& style = GImGui->Style; + + // Clamp into visible area while not overlapping the cursor. Safety padding is optional if our popup size won't fit without it. + ImVec2 safe_padding = style.DisplaySafeAreaPadding; + ImRect r_outer(GetVisibleRect()); + r_outer.Reduce(ImVec2((size.x - r_outer.GetWidth() > safe_padding.x*2) ? safe_padding.x : 0.0f, (size.y - r_outer.GetHeight() > safe_padding.y*2) ? safe_padding.y : 0.0f)); + ImVec2 base_pos_clamped = ImClamp(base_pos, r_outer.Min, r_outer.Max - size); + + for (int n = (*last_dir != -1) ? -1 : 0; n < 4; n++) // Last, Right, down, up, left. (Favor last used direction). + { + const int dir = (n == -1) ? *last_dir : n; + ImRect rect(dir == 0 ? r_inner.Max.x : r_outer.Min.x, dir == 1 ? r_inner.Max.y : r_outer.Min.y, dir == 3 ? r_inner.Min.x : r_outer.Max.x, dir == 2 ? r_inner.Min.y : r_outer.Max.y); + if (rect.GetWidth() < size.x || rect.GetHeight() < size.y) + continue; + *last_dir = dir; + return ImVec2(dir == 0 ? r_inner.Max.x : dir == 3 ? r_inner.Min.x - size.x : base_pos_clamped.x, dir == 1 ? r_inner.Max.y : dir == 2 ? r_inner.Min.y - size.y : base_pos_clamped.y); + } + + // Fallback, try to keep within display + *last_dir = -1; + ImVec2 pos = base_pos; + pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); + pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); + return pos; +} + +ImGuiWindow* ImGui::FindWindowByName(const char* name) +{ + // FIXME-OPT: Store sorted hashes -> pointers so we can do a bissection in a contiguous block + ImGuiContext& g = *GImGui; + ImGuiID id = ImHash(name, 0); + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i]->ID == id) + return g.Windows[i]; + return NULL; +} + +static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + + // Create window the first time + ImGuiWindow* window = (ImGuiWindow*)ImGui::MemAlloc(sizeof(ImGuiWindow)); + IM_PLACEMENT_NEW(window) ImGuiWindow(name); + window->Flags = flags; + + if (flags & ImGuiWindowFlags_NoSavedSettings) + { + // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. + window->Size = window->SizeFull = size; + } + else + { + // Retrieve settings from .ini file + // Use SetWindowPos() or SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. + window->PosFloat = ImVec2(60, 60); + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + + ImGuiIniData* settings = FindWindowSettings(name); + if (!settings) + { + settings = AddWindowSettings(name); + } + else + { + window->SetWindowPosAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowSizeAllowFlags &= ~ImGuiSetCond_FirstUseEver; + window->SetWindowCollapsedAllowFlags &= ~ImGuiSetCond_FirstUseEver; + } + + if (settings->Pos.x != FLT_MAX) + { + window->PosFloat = settings->Pos; + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->Collapsed = settings->Collapsed; + } + + if (ImLengthSqr(settings->Size) > 0.00001f && !(flags & ImGuiWindowFlags_NoResize)) + size = settings->Size; + window->Size = window->SizeFull = size; + } + + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) + { + window->AutoFitFramesX = window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = false; + } + else + { + if (window->Size.x <= 0.0f) + window->AutoFitFramesX = 2; + if (window->Size.y <= 0.0f) + window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); + } + + if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) + g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once + else + g.Windows.push_back(window); + return window; +} + +static void ApplySizeFullWithConstraint(ImGuiWindow* window, ImVec2 new_size) +{ + ImGuiContext& g = *GImGui; + if (g.SetNextWindowSizeConstraint) + { + // Using -1,-1 on either X/Y axis to preserve the current size. + ImRect cr = g.SetNextWindowSizeConstraintRect; + new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; + new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; + if (g.SetNextWindowSizeConstraintCallback) + { + ImGuiSizeConstraintCallbackData data; + data.UserData = g.SetNextWindowSizeConstraintCallbackUserData; + data.Pos = window->Pos; + data.CurrentSize = window->SizeFull; + data.DesiredSize = new_size; + g.SetNextWindowSizeConstraintCallback(&data); + new_size = data.DesiredSize; + } + } + if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) + new_size = ImMax(new_size, g.Style.WindowMinSize); + window->SizeFull = new_size; +} + +// Push a new ImGui window to add widgets to. +// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. +// - Begin/End can be called multiple times during the frame with the same window name to append content. +// - 'size_on_first_use' for a regular window denote the initial size for first-time creation (no saved data) and isn't that useful. Use SetNextWindowSize() prior to calling Begin() for more flexible window manipulation. +// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). +// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. +// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. +// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. +// - Passing non-zero 'size' is roughly equivalent to calling SetNextWindowSize(size, ImGuiSetCond_FirstUseEver) prior to calling Begin(). +bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) +{ + return ImGui::Begin(name, p_open, ImVec2(0.f, 0.f), -1.0f, flags); +} + +bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha, ImGuiWindowFlags flags) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + IM_ASSERT(name != NULL); // Window name required + IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() + IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet + + if (flags & ImGuiWindowFlags_NoInputs) + flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; + + // Find or create + bool window_is_new = false; + ImGuiWindow* window = FindWindowByName(name); + if (!window) + { + window = CreateNewWindow(name, size_on_first_use, flags); + window_is_new = true; + } + + const int current_frame = ImGui::GetFrameCount(); + const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); + if (first_begin_of_the_frame) + window->Flags = (ImGuiWindowFlags)flags; + else + flags = window->Flags; + + // Add to stack + ImGuiWindow* parent_window = !g.CurrentWindowStack.empty() ? g.CurrentWindowStack.back() : NULL; + g.CurrentWindowStack.push_back(window); + SetCurrentWindow(window); + CheckStacksSize(window, true); + IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); + + bool window_was_active = (window->LastFrameActive == current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on + if (flags & ImGuiWindowFlags_Popup) + { + ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; + window_was_active &= (window->PopupId == popup_ref.PopupId); + window_was_active &= (window == popup_ref.Window); + popup_ref.Window = window; + g.CurrentPopupStack.push_back(popup_ref); + window->PopupId = popup_ref.PopupId; + } + + const bool window_appearing_after_being_hidden = (window->HiddenFrames == 1); + + // Process SetNextWindow***() calls + bool window_pos_set_by_api = false, window_size_set_by_api = false; + if (g.SetNextWindowPosCond) + { + const ImVec2 backup_cursor_pos = window->DC.CursorPos; // FIXME: not sure of the exact reason of this saving/restore anymore :( need to look into that. + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowPosAllowFlags |= ImGuiSetCond_Appearing; + window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.SetNextWindowPosCond) != 0; + if (window_pos_set_by_api && ImLengthSqr(g.SetNextWindowPosVal - ImVec2(-FLT_MAX,-FLT_MAX)) < 0.001f) + { + window->SetWindowPosCenterWanted = true; // May be processed on the next frame if this is our first frame and we are measuring size + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + } + else + { + SetWindowPos(window, g.SetNextWindowPosVal, g.SetNextWindowPosCond); + } + window->DC.CursorPos = backup_cursor_pos; + g.SetNextWindowPosCond = 0; + } + if (g.SetNextWindowSizeCond) + { + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowSizeAllowFlags |= ImGuiSetCond_Appearing; + window_size_set_by_api = (window->SetWindowSizeAllowFlags & g.SetNextWindowSizeCond) != 0; + SetWindowSize(window, g.SetNextWindowSizeVal, g.SetNextWindowSizeCond); + g.SetNextWindowSizeCond = 0; + } + if (g.SetNextWindowContentSizeCond) + { + window->SizeContentsExplicit = g.SetNextWindowContentSizeVal; + g.SetNextWindowContentSizeCond = 0; + } + else if (first_begin_of_the_frame) + { + window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); + } + if (g.SetNextWindowCollapsedCond) + { + if (!window_was_active || window_appearing_after_being_hidden) window->SetWindowCollapsedAllowFlags |= ImGuiSetCond_Appearing; + SetWindowCollapsed(window, g.SetNextWindowCollapsedVal, g.SetNextWindowCollapsedCond); + g.SetNextWindowCollapsedCond = 0; + } + if (g.SetNextWindowFocus) + { + ImGui::SetWindowFocus(); + g.SetNextWindowFocus = false; + } + + // Update known root window (if we are a child window, otherwise window == window->RootWindow) + int root_idx, root_non_popup_idx; + for (root_idx = g.CurrentWindowStack.Size - 1; root_idx > 0; root_idx--) + if (!(g.CurrentWindowStack[root_idx]->Flags & ImGuiWindowFlags_ChildWindow)) + break; + for (root_non_popup_idx = root_idx; root_non_popup_idx > 0; root_non_popup_idx--) + if (!(g.CurrentWindowStack[root_non_popup_idx]->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) + break; + window->ParentWindow = parent_window; + window->RootWindow = g.CurrentWindowStack[root_idx]; + window->RootNonPopupWindow = g.CurrentWindowStack[root_non_popup_idx]; // This is merely for displaying the TitleBgActive color. + + // When reusing window again multiple times a frame, just append content (don't need to setup again) + if (first_begin_of_the_frame) + { + window->Active = true; + window->IndexWithinParent = 0; + window->BeginCount = 0; + window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); + window->LastFrameActive = current_frame; + window->IDStack.resize(1); + + // Clear draw list, setup texture, outer clipping rectangle + window->DrawList->Clear(); + window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); + ImRect fullscreen_rect(GetVisibleRect()); + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_ComboBox|ImGuiWindowFlags_Popup))) + PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); + else + PushClipRect(fullscreen_rect.Min, fullscreen_rect.Max, true); + + if (!window_was_active) + { + // Popup first latch mouse position, will position itself when it appears next frame + window->AutoPosLastDirection = -1; + if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) + window->PosFloat = g.IO.MousePos; + } + + // Collapse window by double-clicking on title bar + // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing + if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) + { + ImRect title_bar_rect = window->TitleBarRect(); + if (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0]) + { + window->Collapsed = !window->Collapsed; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + FocusWindow(window); + } + } + else + { + window->Collapsed = false; + } + + // SIZE + + // Save contents size from last frame for auto-fitting (unless explicitly specified) + window->SizeContents.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.x - window->Pos.x) + window->Scroll.x)); + window->SizeContents.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : ((window_is_new ? 0.0f : window->DC.CursorMaxPos.y - window->Pos.y) + window->Scroll.y)); + + // Hide popup/tooltip window when first appearing while we measure size (because we recycle them) + if (window->HiddenFrames > 0) + window->HiddenFrames--; + if ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0 && !window_was_active) + { + window->HiddenFrames = 1; + if (flags & ImGuiWindowFlags_AlwaysAutoResize) + { + if (!window_size_set_by_api) + window->Size = window->SizeFull = ImVec2(0.f, 0.f); + window->SizeContents = ImVec2(0.f, 0.f); + } + } + + // Lock window padding so that altering the ShowBorders flag for children doesn't have side-effects. + window->WindowPadding = ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_ShowBorders | ImGuiWindowFlags_ComboBox | ImGuiWindowFlags_Popup))) ? ImVec2(0,0) : style.WindowPadding; + + // Calculate auto-fit size + ImVec2 size_auto_fit; + if ((flags & ImGuiWindowFlags_Tooltip) != 0) + { + // Tooltip always resize. We keep the spacing symmetric on both axises for aesthetic purpose. + size_auto_fit = window->SizeContents + window->WindowPadding - ImVec2(0.0f, style.ItemSpacing.y); + } + else + { + size_auto_fit = ImClamp(window->SizeContents + window->WindowPadding, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding)); + + // Handling case of auto fit window not fitting in screen on one axis, we are growing auto fit size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. + if (size_auto_fit.x < window->SizeContents.x && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)) + size_auto_fit.y += style.ScrollbarSize; + if (size_auto_fit.y < window->SizeContents.y && !(flags & ImGuiWindowFlags_NoScrollbar)) + size_auto_fit.x += style.ScrollbarSize; + size_auto_fit.y = ImMax(size_auto_fit.y - style.ItemSpacing.y, 0.0f); + } + + // Handle automatic resize + if (window->Collapsed) + { + // We still process initial auto-fit on collapsed windows to get a window width, + // But otherwise we don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. + if (window->AutoFitFramesX > 0) + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + if (window->AutoFitFramesY > 0) + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + } + else + { + if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window_size_set_by_api) + { + window->SizeFull = size_auto_fit; + } + else if ((window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) && !window_size_set_by_api) + { + // Auto-fit only grows during the first few frames + if (window->AutoFitFramesX > 0) + window->SizeFull.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; + if (window->AutoFitFramesY > 0) + window->SizeFull.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + } + } + + // Apply minimum/maximum window size constraints and final size + ApplySizeFullWithConstraint(window, window->SizeFull); + window->Size = window->Collapsed ? window->TitleBarRect().GetSize() : window->SizeFull; + + // POSITION + + // Position child window + if (flags & ImGuiWindowFlags_ChildWindow) + { + window->IndexWithinParent = parent_window->DC.ChildWindows.Size; + parent_window->DC.ChildWindows.push_back(window); + } + if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup)) + { + window->Pos = window->PosFloat = parent_window->DC.CursorPos; + window->Size = window->SizeFull = size_on_first_use; // NB: argument name 'size_on_first_use' misleading here, it's really just 'size' as provided by user passed via BeginChild()->Begin(). + } + + bool window_pos_center = false; + window_pos_center |= (window->SetWindowPosCenterWanted && window->HiddenFrames == 0); + window_pos_center |= ((flags & ImGuiWindowFlags_Modal) && !window_pos_set_by_api && window_appearing_after_being_hidden); + if (window_pos_center) + { + // Center (any sort of window) + SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, fullscreen_rect.GetCenter() - window->SizeFull * 0.5f), 0); + } + else if (flags & ImGuiWindowFlags_ChildMenu) + { + IM_ASSERT(window_pos_set_by_api); + ImRect rect_to_avoid; + if (parent_window->DC.MenuBarAppending) + rect_to_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); + else + rect_to_avoid = ImRect(parent_window->Pos.x + style.ItemSpacing.x, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - style.ItemSpacing.x - parent_window->ScrollbarSizes.x, FLT_MAX); // We want some overlap to convey the relative depth of each popup (here hard-coded to 4) + window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_appearing_after_being_hidden) + { + ImRect rect_to_avoid(window->PosFloat.x - 1, window->PosFloat.y - 1, window->PosFloat.x + 1, window->PosFloat.y + 1); + window->PosFloat = FindBestPopupWindowPos(window->PosFloat, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + } + + // Position tooltip (always follows mouse) + if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api) + { + ImRect rect_to_avoid(g.IO.MousePos.x - 16, g.IO.MousePos.y - 8, g.IO.MousePos.x + 24, g.IO.MousePos.y + 24); // FIXME: Completely hard-coded. Perhaps center on cursor hit-point instead? + window->PosFloat = FindBestPopupWindowPos(g.IO.MousePos, window->Size, &window->AutoPosLastDirection, rect_to_avoid); + if (window->AutoPosLastDirection == -1) + window->PosFloat = g.IO.MousePos + ImVec2(2,2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. + } + + // Clamp position so it stays visible + if (!(flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Tooltip)) + { + if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. + { + ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); + window->PosFloat = ImMax(window->PosFloat + window->Size, padding) - window->Size; + window->PosFloat = ImMin(window->PosFloat, g.IO.DisplaySize - padding); + } + } + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + + // Default item width. Make it proportional to window size if window manually resizes + if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) + window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); + else + window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f); + + // Prepare for focus requests + window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); + window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); + window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; + window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; + + // Apply scrolling + if (window->ScrollTarget.x < FLT_MAX) + { + window->Scroll.x = window->ScrollTarget.x; + window->ScrollTarget.x = FLT_MAX; + } + if (window->ScrollTarget.y < FLT_MAX) + { + float center_ratio = window->ScrollTargetCenterRatio.y; + window->Scroll.y = window->ScrollTarget.y - ((1.0f - center_ratio) * (window->TitleBarHeight() + window->MenuBarHeight())) - (center_ratio * window->SizeFull.y); + window->ScrollTarget.y = FLT_MAX; + } + window->Scroll = ImMax(window->Scroll, ImVec2(0.0f, 0.0f)); + if (!window->Collapsed && !window->SkipItems) + window->Scroll = ImMin(window->Scroll, ImMax(ImVec2(0.0f, 0.0f), window->SizeContents - window->SizeFull + window->ScrollbarSizes)); + + // Modal window darkens what is behind them + if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostModalRootWindow()) + window->DrawList->AddRectFilled(fullscreen_rect.Min, fullscreen_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); + + // Draw window + handle manual resize + ImRect title_bar_rect = window->TitleBarRect(); + const float window_rounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; + if (window->Collapsed) + { + // Draw title bar only + RenderFrame(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32(ImGuiCol_TitleBgCollapsed), true, window_rounding); + } + else + { + ImU32 resize_col = 0; + const float resize_corner_size = ImMax(g.FontSize * 1.35f, window_rounding + 1.0f + g.FontSize * 0.2f); + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && !(flags & ImGuiWindowFlags_NoResize)) + { + // Manual resize + const ImVec2 br = window->Rect().GetBR(); + const ImRect resize_rect(br - ImVec2(resize_corner_size * 0.75f, resize_corner_size * 0.75f), br); + const ImGuiID resize_id = window->GetID("#RESIZE"); + bool hovered, held; + ButtonBehavior(resize_rect, resize_id, &hovered, &held, ImGuiButtonFlags_FlattenChilds); + resize_col = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); + + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeNWSE; + + if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0]) + { + // Manual auto-fit when double-clicking + ApplySizeFullWithConstraint(window, size_auto_fit); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + SetActiveID(0); + } + else if (held) + { + // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position + ApplySizeFullWithConstraint(window, (g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize()) - window->Pos); + if (!(flags & ImGuiWindowFlags_NoSavedSettings)) + MarkSettingsDirty(); + } + + window->Size = window->SizeFull; + title_bar_rect = window->TitleBarRect(); + } + + // Scrollbars + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > window->Size.y + style.ItemSpacing.y) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > window->Size.x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f) - window->WindowPadding.x) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + window->BorderSize = (flags & ImGuiWindowFlags_ShowBorders) ? 1.0f : 0.0f; + + // Window background, Default Alpha + ImGuiCol bg_color_idx = ImGuiCol_WindowBg; + if ((flags & ImGuiWindowFlags_ComboBox) != 0) + bg_color_idx = ImGuiCol_ComboBg; + else if ((flags & ImGuiWindowFlags_Tooltip) != 0 || (flags & ImGuiWindowFlags_Popup) != 0) + bg_color_idx = ImGuiCol_PopupBg; + else if ((flags & ImGuiWindowFlags_ChildWindow) != 0) + bg_color_idx = ImGuiCol_ChildWindowBg; + ImVec4 bg_color = style.Colors[bg_color_idx]; + if (bg_alpha >= 0.0f) + bg_color.w = bg_alpha; + bg_color.w *= style.Alpha; + if (bg_color.w > 0.0f) + window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, ColorConvertFloat4ToU32(bg_color), window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImGuiCorner_All : ImGuiCorner_BottomLeft|ImGuiCorner_BottomRight); + + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddRectFilled(title_bar_rect.GetTL(), title_bar_rect.GetBR(), GetColorU32((g.FocusedWindow && window->RootNonPopupWindow == g.FocusedWindow->RootNonPopupWindow) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg), window_rounding, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); + + // Menu bar + if (flags & ImGuiWindowFlags_MenuBar) + { + ImRect menu_bar_rect = window->MenuBarRect(); + if (flags & ImGuiWindowFlags_ShowBorders) + window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border)); + window->DrawList->AddRectFilled(menu_bar_rect.GetTL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImGuiCorner_TopLeft|ImGuiCorner_TopRight); + } + + // Scrollbars + if (window->ScrollbarX) + Scrollbar(window, true); + if (window->ScrollbarY) + Scrollbar(window, false); + + // Render resize grip + // (after the input handling so we don't have a frame of latency) + if (!(flags & ImGuiWindowFlags_NoResize)) + { + const ImVec2 br = window->Rect().GetBR(); + window->DrawList->PathLineTo(br + ImVec2(-resize_corner_size, -window->BorderSize)); + window->DrawList->PathLineTo(br + ImVec2(-window->BorderSize, -resize_corner_size)); + window->DrawList->PathArcToFast(ImVec2(br.x - window_rounding - window->BorderSize, br.y - window_rounding - window->BorderSize), window_rounding, 0, 3); + window->DrawList->PathFill(resize_col); + } + + // Borders + if (flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddRect(window->Pos+ImVec2(1,1), window->Pos+window->Size+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), window_rounding); + window->DrawList->AddRect(window->Pos, window->Pos+window->Size, GetColorU32(ImGuiCol_Border), window_rounding); + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + window->DrawList->AddLine(title_bar_rect.GetBL()+ImVec2(1,0), title_bar_rect.GetBR()-ImVec2(1,0), GetColorU32(ImGuiCol_Border)); + } + } + + // Update ContentsRegionMax. All the variable it depends on are set above in this function. + window->ContentsRegionRect.Min.x = -window->Scroll.x + window->WindowPadding.x; + window->ContentsRegionRect.Min.y = -window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); + window->ContentsRegionRect.Max.x = -window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); + window->ContentsRegionRect.Max.y = -window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); + + // Setup drawing context + window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; + window->DC.GroupOffsetX = 0.0f; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); + window->DC.CursorPos = window->DC.CursorStartPos; + window->DC.CursorPosPrevLine = window->DC.CursorPos; + window->DC.CursorMaxPos = window->DC.CursorStartPos; + window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.MenuBarAppending = false; + window->DC.MenuBarOffsetX = ImMax(window->WindowPadding.x, style.ItemSpacing.x); + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + window->DC.ChildWindows.resize(0); + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.ItemWidth = window->ItemWidthDefault; + window->DC.TextWrapPos = -1.0f; // disabled + window->DC.AllowKeyboardFocus = true; + window->DC.ButtonRepeat = false; + window->DC.ItemWidthStack.resize(0); + window->DC.AllowKeyboardFocusStack.resize(0); + window->DC.ButtonRepeatStack.resize(0); + window->DC.TextWrapPosStack.resize(0); + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsCount = 1; + window->DC.ColumnsStartPosY = window->DC.CursorPos.y; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.ColumnsStartPosY; + window->DC.TreeDepth = 0; + window->DC.StateStorage = &window->StateStorage; + window->DC.GroupStack.resize(0); + window->DC.ColorEditMode = ImGuiColorEditMode_UserSelect; + window->MenuColumns.Update(3, style.ItemSpacing.x, !window_was_active); + + if (window->AutoFitFramesX > 0) + window->AutoFitFramesX--; + if (window->AutoFitFramesY > 0) + window->AutoFitFramesY--; + + // New windows appears in front (we need to do that AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) + if (!window_was_active && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) + if (!(flags & (ImGuiWindowFlags_ChildWindow|ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) + FocusWindow(window); + + // Title bar + if (!(flags & ImGuiWindowFlags_NoTitleBar)) + { + if (p_open != NULL) + { + const float pad = 2.0f; + const float rad = (window->TitleBarHeight() - pad*2.0f) * 0.5f; + if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad)) + *p_open = false; + } + + const ImVec2 text_size = CalcTextSize(name, NULL, true); + if (!(flags & ImGuiWindowFlags_NoCollapse)) + RenderCollapseTriangle(window->Pos + style.FramePadding, !window->Collapsed, 1.0f); + + ImVec2 text_min = window->Pos; + ImVec2 text_max = window->Pos + ImVec2(window->Size.x, style.FramePadding.y*2 + text_size.y); + ImRect clip_rect; + clip_rect.Max = ImVec2(window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x), text_max.y); // Match the size of CloseWindowButton() + float pad_left = (flags & ImGuiWindowFlags_NoCollapse) == 0 ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : 0.0f; + float pad_right = (p_open != NULL) ? (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x) : 0.0f; + if (style.WindowTitleAlign.x > 0.0f) pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); + text_min.x += pad_left; + text_max.x -= pad_right; + clip_rect.Min = ImVec2(text_min.x, window->Pos.y); + RenderTextClipped(text_min, text_max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); + } + + // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() + window->WindowRectClipped = window->Rect(); + window->WindowRectClipped.Clip(window->ClipRect); + + // Pressing CTRL+C while holding on a window copy its content to the clipboard + // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. + // Maybe we can support CTRL+C on every element? + /* + if (g.ActiveId == move_id) + if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + ImGui::LogToClipboard(); + */ + } + + // Inner clipping rectangle + // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame + // Note that if our window is collapsed we will end up with a null clipping rectangle which is the correct behavior. + const ImRect title_bar_rect = window->TitleBarRect(); + const float border_size = window->BorderSize; + ImRect clip_rect; // Force round to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. + clip_rect.Min.x = ImFloor(0.5f + title_bar_rect.Min.x + ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Min.y = ImFloor(0.5f + title_bar_rect.Max.y + window->MenuBarHeight() + border_size); + clip_rect.Max.x = ImFloor(0.5f + window->Pos.x + window->Size.x - window->ScrollbarSizes.x - ImMax(border_size, ImFloor(window->WindowPadding.x*0.5f))); + clip_rect.Max.y = ImFloor(0.5f + window->Pos.y + window->Size.y - window->ScrollbarSizes.y - border_size); + PushClipRect(clip_rect.Min, clip_rect.Max, true); + + // Clear 'accessed' flag last thing + if (first_begin_of_the_frame) + window->Accessed = false; + window->BeginCount++; + g.SetNextWindowSizeConstraint = false; + + // Child window can be out of sight and have "negative" clip windows. + // Mark them as collapsed so commands are skipped earlier (we can't manually collapse because they have no title bar). + if (flags & ImGuiWindowFlags_ChildWindow) + { + IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); + window->Collapsed = parent_window && parent_window->Collapsed; + + if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + window->Collapsed |= (window->WindowRectClipped.Min.x >= window->WindowRectClipped.Max.x || window->WindowRectClipped.Min.y >= window->WindowRectClipped.Max.y); + + // We also hide the window from rendering because we've already added its border to the command list. + // (we could perform the check earlier in the function but it is simpler at this point) + if (window->Collapsed) + window->Active = false; + } + if (style.Alpha <= 0.0f) + window->Active = false; + + // Return false if we don't intend to display anything to allow user to perform an early out optimization + window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0; + return !window->SkipItems; +} + +void ImGui::End() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + + Columns(1, "#CloseColumns"); + PopClipRect(); // inner window clip rectangle + + // Stop logging + if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging + LogFinish(); + + // Pop + // NB: we don't clear 'window->RootWindow'. The pointer is allowed to live until the next call to Begin(). + g.CurrentWindowStack.pop_back(); + if (window->Flags & ImGuiWindowFlags_Popup) + g.CurrentPopupStack.pop_back(); + CheckStacksSize(window, false); + SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); +} + +// Vertical scrollbar +// The entire piece of code below is rather confusing because: +// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) +// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar +// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. +static void Scrollbar(ImGuiWindow* window, bool horizontal) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); + + // Render background + bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); + float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; + const ImRect window_rect = window->Rect(); + const float border_size = window->BorderSize; + ImRect bb = horizontal + ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) + : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); + if (!horizontal) + bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); + + float window_rounding = (window->Flags & ImGuiWindowFlags_ChildWindow) ? style.ChildWindowRounding : style.WindowRounding; + int window_rounding_corners; + if (horizontal) + window_rounding_corners = ImGuiCorner_BottomLeft | (other_scrollbar ? 0 : ImGuiCorner_BottomRight); + else + window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImGuiCorner_TopRight : 0) | (other_scrollbar ? 0 : ImGuiCorner_BottomRight); + window->DrawList->AddRectFilled(bb.Min, bb.Max, ImGui::GetColorU32(ImGuiCol_ScrollbarBg), window_rounding, window_rounding_corners); + bb.Reduce(ImVec2(ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); + + // V denote the main axis of the scrollbar + float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); + float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; + float win_size_avail_v = (horizontal ? window->Size.x : window->Size.y) - other_scrollbar_size_w; + float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; + + // The grabable box size generally represent the amount visible (vs the total scrollable amount) + // But we maintain a minimum size in pixel to allow for the user to still aim inside. + const float grab_h_pixels = ImMin(ImMax(scrollbar_size_v * ImSaturate(win_size_avail_v / ImMax(win_size_contents_v, win_size_avail_v)), style.GrabMinSize), scrollbar_size_v); + const float grab_h_norm = grab_h_pixels / scrollbar_size_v; + + // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). + bool held = false; + bool hovered = false; + const bool previously_held = (g.ActiveId == id); + ImGui::ButtonBehavior(bb, id, &hovered, &held); + + float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); + float scroll_ratio = ImSaturate(scroll_v / scroll_max); + float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + if (held && grab_h_norm < 1.0f) + { + float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; + float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; + + // Click position in scrollbar normalized space (0.0f->1.0f) + const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); + ImGui::SetHoveredID(id); + + bool seek_absolute = false; + if (!previously_held) + { + // On initial click calculate the distance between mouse and the center of the grab + if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) + { + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + else + { + seek_absolute = true; + *click_delta_to_grab_center_v = 0.0f; + } + } + + // Apply scroll + // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position + const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); + scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); + if (horizontal) + window->Scroll.x = scroll_v; + else + window->Scroll.y = scroll_v; + + // Update values for rendering + scroll_ratio = ImSaturate(scroll_v / scroll_max); + grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; + + // Update distance to grab now that we have seeked and saturated + if (seek_absolute) + *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; + } + + // Render + const ImU32 grab_col = ImGui::GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); + if (horizontal) + window->DrawList->AddRectFilled(ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y), ImVec2(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, bb.Max.y), grab_col, style.ScrollbarRounding); + else + window->DrawList->AddRectFilled(ImVec2(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm)), ImVec2(bb.Max.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels), grab_col, style.ScrollbarRounding); +} + +// Moving window to front of display (which happens to be back of our sorted list) +void ImGui::FocusWindow(ImGuiWindow* window) +{ + ImGuiContext& g = *GImGui; + + // Always mark the window we passed as focused. This is used for keyboard interactions such as tabbing. + g.FocusedWindow = window; + + // Passing NULL allow to disable keyboard focus + if (!window) + return; + + // And move its root window to the top of the pile + if (window->RootWindow) + window = window->RootWindow; + + // Steal focus on active widgets + if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it.. + if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window) + SetActiveID(0); + + // Bring to front + if ((window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus) || g.Windows.back() == window) + return; + for (int i = 0; i < g.Windows.Size; i++) + if (g.Windows[i] == window) + { + g.Windows.erase(g.Windows.begin() + i); + break; + } + g.Windows.push_back(window); +} + +void ImGui::PushItemWidth(float item_width) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); + window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); +} + +static void PushMultiItemsWidths(int components, float w_full) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + const ImGuiStyle& style = GImGui->Style; + if (w_full <= 0.0f) + w_full = ImGui::CalcItemWidth(); + const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + window->DC.ItemWidthStack.push_back(w_item_last); + for (int i = 0; i < components-1; i++) + window->DC.ItemWidthStack.push_back(w_item_one); + window->DC.ItemWidth = window->DC.ItemWidthStack.back(); +} + +void ImGui::PopItemWidth() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ItemWidthStack.pop_back(); + window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); +} + +float ImGui::CalcItemWidth() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + float w = window->DC.ItemWidth; + if (w < 0.0f) + { + // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. + float width_to_right_edge = GetContentRegionAvail().x; + w = ImMax(1.0f, width_to_right_edge + w); + } + w = (float)(int)w; + return w; +} + +static void SetCurrentFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? + IM_ASSERT(font->Scale > 0.0f); + g.Font = font; + g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; + g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; + g.FontTexUvWhitePixel = g.Font->ContainerAtlas->TexUvWhitePixel; +} + +void ImGui::PushFont(ImFont* font) +{ + ImGuiContext& g = *GImGui; + if (!font) + font = g.IO.Fonts->Fonts[0]; + SetCurrentFont(font); + g.FontStack.push_back(font); + g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); +} + +void ImGui::PopFont() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DrawList->PopTextureID(); + g.FontStack.pop_back(); + SetCurrentFont(g.FontStack.empty() ? g.IO.Fonts->Fonts[0] : g.FontStack.back()); +} + +void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.AllowKeyboardFocus = allow_keyboard_focus; + window->DC.AllowKeyboardFocusStack.push_back(allow_keyboard_focus); +} + +void ImGui::PopAllowKeyboardFocus() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.AllowKeyboardFocusStack.pop_back(); + window->DC.AllowKeyboardFocus = window->DC.AllowKeyboardFocusStack.empty() ? true : window->DC.AllowKeyboardFocusStack.back(); +} + +void ImGui::PushButtonRepeat(bool repeat) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ButtonRepeat = repeat; + window->DC.ButtonRepeatStack.push_back(repeat); +} + +void ImGui::PopButtonRepeat() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ButtonRepeatStack.pop_back(); + window->DC.ButtonRepeat = window->DC.ButtonRepeatStack.empty() ? false : window->DC.ButtonRepeatStack.back(); +} + +void ImGui::PushTextWrapPos(float wrap_pos_x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPos = wrap_pos_x; + window->DC.TextWrapPosStack.push_back(wrap_pos_x); +} + +void ImGui::PopTextWrapPos() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.TextWrapPosStack.pop_back(); + window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); +} + +void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) +{ + ImGuiContext& g = *GImGui; + ImGuiColMod backup; + backup.Col = idx; + backup.BackupValue = g.Style.Colors[idx]; + g.ColorModifiers.push_back(backup); + g.Style.Colors[idx] = col; +} + +void ImGui::PopStyleColor(int count) +{ + ImGuiContext& g = *GImGui; + while (count > 0) + { + ImGuiColMod& backup = g.ColorModifiers.back(); + g.Style.Colors[backup.Col] = backup.BackupValue; + g.ColorModifiers.pop_back(); + count--; + } +} + +struct ImGuiStyleVarInfo +{ + ImGuiDataType Type; + ImU32 Offset; + void* GetVarPtr() const { return (void*)((unsigned char*)&GImGui->Style + Offset); } +}; + +static const ImGuiStyleVarInfo GStyleVarInfo[ImGuiStyleVar_Count_] = +{ + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildWindowRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, + { ImGuiDataType_Float, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, + { ImGuiDataType_Float2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, +}; + +static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) +{ + IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_Count_); + return &GStyleVarInfo[idx]; +} + +void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) +{ + const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type == ImGuiDataType_Float) + { + float* pvar = (float*)var_info->GetVarPtr(); + GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; + return; + } + IM_ASSERT(0); // Called function with wrong-type? Variable is not a float. +} + +void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) +{ + const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); + if (var_info->Type == ImGuiDataType_Float2) + { + ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(); + GImGui->StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); + *pvar = val; + return; + } + IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2. +} + +void ImGui::PopStyleVar(int count) +{ + ImGuiContext& g = *GImGui; + while (count > 0) + { + ImGuiStyleMod& backup = g.StyleModifiers.back(); + const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); + if (info->Type == ImGuiDataType_Float) (*(float*)info->GetVarPtr()) = backup.BackupFloat[0]; + else if (info->Type == ImGuiDataType_Float2) (*(ImVec2*)info->GetVarPtr()) = ImVec2(backup.BackupFloat[0], backup.BackupFloat[1]); + else if (info->Type == ImGuiDataType_Int) (*(int*)info->GetVarPtr()) = backup.BackupInt[0]; + g.StyleModifiers.pop_back(); + count--; + } +} + +const char* ImGui::GetStyleColName(ImGuiCol idx) +{ + // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; + switch (idx) + { + case ImGuiCol_Text: return "Text"; + case ImGuiCol_TextDisabled: return "TextDisabled"; + case ImGuiCol_WindowBg: return "WindowBg"; + case ImGuiCol_ChildWindowBg: return "ChildWindowBg"; + case ImGuiCol_PopupBg: return "PopupBg"; + case ImGuiCol_Border: return "Border"; + case ImGuiCol_BorderShadow: return "BorderShadow"; + case ImGuiCol_FrameBg: return "FrameBg"; + case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; + case ImGuiCol_FrameBgActive: return "FrameBgActive"; + case ImGuiCol_TitleBg: return "TitleBg"; + case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; + case ImGuiCol_TitleBgActive: return "TitleBgActive"; + case ImGuiCol_MenuBarBg: return "MenuBarBg"; + case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; + case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; + case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; + case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; + case ImGuiCol_ComboBg: return "ComboBg"; + case ImGuiCol_CheckMark: return "CheckMark"; + case ImGuiCol_SliderGrab: return "SliderGrab"; + case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; + case ImGuiCol_Button: return "Button"; + case ImGuiCol_ButtonHovered: return "ButtonHovered"; + case ImGuiCol_ButtonActive: return "ButtonActive"; + case ImGuiCol_Header: return "Header"; + case ImGuiCol_HeaderHovered: return "HeaderHovered"; + case ImGuiCol_HeaderActive: return "HeaderActive"; + case ImGuiCol_Column: return "Column"; + case ImGuiCol_ColumnHovered: return "ColumnHovered"; + case ImGuiCol_ColumnActive: return "ColumnActive"; + case ImGuiCol_ResizeGrip: return "ResizeGrip"; + case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; + case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; + case ImGuiCol_CloseButton: return "CloseButton"; + case ImGuiCol_CloseButtonHovered: return "CloseButtonHovered"; + case ImGuiCol_CloseButtonActive: return "CloseButtonActive"; + case ImGuiCol_PlotLines: return "PlotLines"; + case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; + case ImGuiCol_PlotHistogram: return "PlotHistogram"; + case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; + case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; + case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; + } + IM_ASSERT(0); + return "Unknown"; +} + +bool ImGui::IsWindowHovered() +{ + ImGuiContext& g = *GImGui; + return g.HoveredWindow == g.CurrentWindow && IsWindowContentHoverable(g.HoveredRootWindow); +} + +bool ImGui::IsWindowFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow == g.CurrentWindow; +} + +bool ImGui::IsRootWindowFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow == g.CurrentWindow->RootWindow; +} + +bool ImGui::IsRootWindowOrAnyChildFocused() +{ + ImGuiContext& g = *GImGui; + return g.FocusedWindow && g.FocusedWindow->RootWindow == g.CurrentWindow->RootWindow; +} + +bool ImGui::IsRootWindowOrAnyChildHovered() +{ + ImGuiContext& g = *GImGui; + return g.HoveredRootWindow && (g.HoveredRootWindow == g.CurrentWindow->RootWindow) && IsWindowContentHoverable(g.HoveredRootWindow); +} + +float ImGui::GetWindowWidth() +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Size.x; +} + +float ImGui::GetWindowHeight() +{ + ImGuiWindow* window = GImGui->CurrentWindow; + return window->Size.y; +} + +ImVec2 ImGui::GetWindowPos() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + return window->Pos; +} + +static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) +{ + window->DC.CursorMaxPos.y += window->Scroll.y; + window->Scroll.y = new_scroll_y; + window->DC.CursorMaxPos.y -= window->Scroll.y; +} + +static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowPosAllowFlags & cond) == 0) + return; + window->SetWindowPosAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + window->SetWindowPosCenterWanted = false; + + // Set + const ImVec2 old_pos = window->Pos; + window->PosFloat = pos; + window->Pos = ImVec2((float)(int)window->PosFloat.x, (float)(int)window->PosFloat.y); + window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor + window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. +} + +void ImGui::SetWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + SetWindowPos(window, pos, cond); +} + +void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond) +{ + if (ImGuiWindow* window = FindWindowByName(name)) + SetWindowPos(window, pos, cond); +} + +ImVec2 ImGui::GetWindowSize() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->Size; +} + +static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) + return; + window->SetWindowSizeAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + if (size.x > 0.0f) + { + window->AutoFitFramesX = 0; + window->SizeFull.x = size.x; + } + else + { + window->AutoFitFramesX = 2; + window->AutoFitOnlyGrows = false; + } + if (size.y > 0.0f) + { + window->AutoFitFramesY = 0; + window->SizeFull.y = size.y; + } + else + { + window->AutoFitFramesY = 2; + window->AutoFitOnlyGrows = false; + } +} + +void ImGui::SetWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + SetWindowSize(GImGui->CurrentWindow, size, cond); +} + +void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowSize(window, size, cond); +} + +static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiSetCond cond) +{ + // Test condition (NB: bit 0 is always true) and clear flags for next time + if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) + return; + window->SetWindowCollapsedAllowFlags &= ~(ImGuiSetCond_Once | ImGuiSetCond_FirstUseEver | ImGuiSetCond_Appearing); + + // Set + window->Collapsed = collapsed; +} + +void ImGui::SetWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); +} + +bool ImGui::IsWindowCollapsed() +{ + return GImGui->CurrentWindow->Collapsed; +} + +void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond) +{ + ImGuiWindow* window = FindWindowByName(name); + if (window) + SetWindowCollapsed(window, collapsed, cond); +} + +void ImGui::SetWindowFocus() +{ + FocusWindow(GImGui->CurrentWindow); +} + +void ImGui::SetWindowFocus(const char* name) +{ + if (name) + { + if (ImGuiWindow* window = FindWindowByName(name)) + FocusWindow(window); + } + else + { + FocusWindow(NULL); + } +} + +void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosVal = pos; + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowPosCenter(ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowPosVal = ImVec2(-FLT_MAX, -FLT_MAX); + g.SetNextWindowPosCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowSizeVal = size; + g.SetNextWindowSizeCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback, void* custom_callback_user_data) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowSizeConstraint = true; + g.SetNextWindowSizeConstraintRect = ImRect(size_min, size_max); + g.SetNextWindowSizeConstraintCallback = custom_callback; + g.SetNextWindowSizeConstraintCallbackUserData = custom_callback_user_data; +} + +void ImGui::SetNextWindowContentSize(const ImVec2& size) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowContentSizeVal = size; + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowContentWidth(float width) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowContentSizeVal = ImVec2(width, g.SetNextWindowContentSizeCond ? g.SetNextWindowContentSizeVal.y : 0.0f); + g.SetNextWindowContentSizeCond = ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowCollapsedVal = collapsed; + g.SetNextWindowCollapsedCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::SetNextWindowFocus() +{ + ImGuiContext& g = *GImGui; + g.SetNextWindowFocus = true; +} + +// In window space (not screen space!) +ImVec2 ImGui::GetContentRegionMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + ImVec2 mx = window->ContentsRegionRect.Max; + if (window->DC.ColumnsCount != 1) + mx.x = GetColumnOffset(window->DC.ColumnsCurrent + 1) - window->WindowPadding.x; + return mx; +} + +ImVec2 ImGui::GetContentRegionAvail() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return GetContentRegionMax() - (window->DC.CursorPos - window->Pos); +} + +float ImGui::GetContentRegionAvailWidth() +{ + return GetContentRegionAvail().x; +} + +// In window space (not screen space!) +ImVec2 ImGui::GetWindowContentRegionMin() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Min; +} + +ImVec2 ImGui::GetWindowContentRegionMax() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Max; +} + +float ImGui::GetWindowContentRegionWidth() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ContentsRegionRect.Max.x - window->ContentsRegionRect.Min.x; +} + +float ImGui::GetTextLineHeight() +{ + ImGuiContext& g = *GImGui; + return g.FontSize; +} + +float ImGui::GetTextLineHeightWithSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + g.Style.ItemSpacing.y; +} + +float ImGui::GetItemsLineHeightWithSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; +} + +ImDrawList* ImGui::GetWindowDrawList() +{ + ImGuiWindow* window = GetCurrentWindow(); + return window->DrawList; +} + +ImFont* ImGui::GetFont() +{ + return GImGui->Font; +} + +float ImGui::GetFontSize() +{ + return GImGui->FontSize; +} + +ImVec2 ImGui::GetFontTexUvWhitePixel() +{ + return GImGui->FontTexUvWhitePixel; +} + +void ImGui::SetWindowFontScale(float scale) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->FontWindowScale = scale; + g.FontSize = window->CalcFontSize(); +} + +// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. +// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'. +ImVec2 ImGui::GetCursorPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos - window->Pos + window->Scroll; +} + +float ImGui::GetCursorPosX() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; +} + +float ImGui::GetCursorPosY() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; +} + +void ImGui::SetCursorPos(const ImVec2& local_pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos = window->Pos - window->Scroll + local_pos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +} + +void ImGui::SetCursorPosX(float x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; + window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); +} + +void ImGui::SetCursorPosY(float y) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; + window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); +} + +ImVec2 ImGui::GetCursorStartPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorStartPos - window->Pos; +} + +ImVec2 ImGui::GetCursorScreenPos() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.CursorPos; +} + +void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.CursorPos = screen_pos; + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +} + +float ImGui::GetScrollX() +{ + return GImGui->CurrentWindow->Scroll.x; +} + +float ImGui::GetScrollY() +{ + return GImGui->CurrentWindow->Scroll.y; +} + +float ImGui::GetScrollMaxX() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->SizeContents.x - window->SizeFull.x - window->ScrollbarSizes.x; +} + +float ImGui::GetScrollMaxY() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->SizeContents.y - window->SizeFull.y - window->ScrollbarSizes.y; +} + +void ImGui::SetScrollX(float scroll_x) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->ScrollTarget.x = scroll_x; + window->ScrollTargetCenterRatio.x = 0.0f; +} + +void ImGui::SetScrollY(float scroll_y) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY + window->ScrollTargetCenterRatio.y = 0.0f; +} + +void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) +{ + // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); + window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); + if (center_y_ratio <= 0.0f && window->ScrollTarget.y <= window->WindowPadding.y) // Minor hack to make "scroll to top" take account of WindowPadding, else it would scroll to (WindowPadding.y - ItemSpacing.y) + window->ScrollTarget.y = 0.0f; + window->ScrollTargetCenterRatio.y = center_y_ratio; +} + +// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. +void ImGui::SetScrollHere(float center_y_ratio) +{ + ImGuiWindow* window = GetCurrentWindow(); + float target_y = window->DC.CursorPosPrevLine.y + (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. + SetScrollFromPosY(target_y - window->Pos.y, center_y_ratio); +} + +void ImGui::SetKeyboardFocusHere(int offset) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; + window->FocusIdxTabRequestNext = INT_MAX; +} + +void ImGui::SetStateStorage(ImGuiStorage* tree) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.StateStorage = tree ? tree : &window->StateStorage; +} + +ImGuiStorage* ImGui::GetStateStorage() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.StateStorage; +} + +void ImGui::TextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + TextUnformatted(g.TempBuffer, text_end); +} + +void ImGui::Text(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextV(fmt, args); + va_end(args); +} + +void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, col); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextColoredV(col, fmt, args); + va_end(args); +} + +void ImGui::TextDisabledV(const char* fmt, va_list args) +{ + PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); + TextV(fmt, args); + PopStyleColor(); +} + +void ImGui::TextDisabled(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextDisabledV(fmt, args); + va_end(args); +} + +void ImGui::TextWrappedV(const char* fmt, va_list args) +{ + bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set + if (need_wrap) PushTextWrapPos(0.0f); + TextV(fmt, args); + if (need_wrap) PopTextWrapPos(); +} + +void ImGui::TextWrapped(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + TextWrappedV(fmt, args); + va_end(args); +} + +void ImGui::TextUnformatted(const char* text, const char* text_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + IM_ASSERT(text != NULL); + const char* text_begin = text; + if (text_end == NULL) + text_end = text + strlen(text); // FIXME-OPT + + const float wrap_pos_x = window->DC.TextWrapPos; + const bool wrap_enabled = wrap_pos_x >= 0.0f; + if (text_end - text > 2000 && !wrap_enabled) + { + // Long text! + // Perform manual coarse clipping to optimize for long multi-line text + // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. + // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. + const char* line = text; + const float line_height = GetTextLineHeight(); + const ImVec2 text_pos = window->DC.CursorPos + ImVec2(0.0f, window->DC.CurrentLineTextBaseOffset); + const ImRect clip_rect = window->ClipRect; + ImVec2 text_size(0,0); + + if (text_pos.y <= clip_rect.Max.y) + { + ImVec2 pos = text_pos; + + // Lines to skip (can't skip when logging text) + if (!g.LogEnabled) + { + int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); + if (lines_skippable > 0) + { + int lines_skipped = 0; + while (line < text_end && lines_skipped < lines_skippable) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + } + + // Lines to render + if (line < text_end) + { + ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (IsClippedEx(line_rect, NULL, false)) + break; + + const ImVec2 line_size = CalcTextSize(line, line_end, false); + text_size.x = ImMax(text_size.x, line_size.x); + RenderText(pos, line, line_end, false); + if (!line_end) + line_end = text_end; + line = line_end + 1; + line_rect.Min.y += line_height; + line_rect.Max.y += line_height; + pos.y += line_height; + } + + // Count remaining lines + int lines_skipped = 0; + while (line < text_end) + { + const char* line_end = strchr(line, '\n'); + if (!line_end) + line_end = text_end; + line = line_end + 1; + lines_skipped++; + } + pos.y += lines_skipped * line_height; + } + + text_size.y += (pos - text_pos).y; + } + + ImRect bb(text_pos, text_pos + text_size); + ItemSize(bb); + ItemAdd(bb, NULL); + } + else + { + const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; + const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); + + // Account of baseline offset + ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); + ImRect bb(text_pos, text_pos + text_size); + ItemSize(text_size); + if (!ItemAdd(bb, NULL)) + return; + + // Render (we don't hide text after ## in this end-user function) + RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); + } +} + +void ImGui::AlignFirstTextHeightToWidgets() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + // Declare a dummy item size to that upcoming items that are smaller will center-align on the newly expanded line height. + ImGuiContext& g = *GImGui; + ItemSize(ImVec2(0, g.FontSize + g.Style.FramePadding.y*2), g.Style.FramePadding.y); + SameLine(0, 0); +} + +// Add a label+text combo aligned to other label+value widgets +void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); + const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) + return; + + // Render + const char* value_text_begin = &g.TempBuffer[0]; + const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); +} + +void ImGui::LabelText(const char* label, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + LabelTextV(label, fmt, args); + va_end(args); +} + +static inline bool IsWindowContentHoverable(ImGuiWindow* window) +{ + // An active popup disable hovering on other windows (apart from its own children) + ImGuiContext& g = *GImGui; + if (ImGuiWindow* focused_window = g.FocusedWindow) + if (ImGuiWindow* focused_root_window = focused_window->RootWindow) + if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) != 0 && focused_root_window->WasActive && focused_root_window != window->RootWindow) + return false; + + return true; +} + +bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + if (flags & ImGuiButtonFlags_Disabled) + { + if (out_hovered) *out_hovered = false; + if (out_held) *out_held = false; + if (g.ActiveId == id) SetActiveID(0); + return false; + } + + if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) + flags |= ImGuiButtonFlags_PressedOnClickRelease; + + bool pressed = false; + bool hovered = IsHovered(bb, id, (flags & ImGuiButtonFlags_FlattenChilds) != 0); + if (hovered) + { + SetHoveredID(id); + if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) + { + // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat + // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds + // PressedOnClick | | .. + // PressedOnRelease | | .. (NOT on release) + // PressedOnDoubleClick | | .. + if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); // Hold on ID + FocusWindow(window); + g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; + } + if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) + { + pressed = true; + SetActiveID(0); + FocusWindow(window); + } + if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) + { + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + SetActiveID(0); + } + + // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). + // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. + if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) + pressed = true; + } + } + + bool held = false; + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + held = true; + } + else + { + if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) + if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps + pressed = true; + SetActiveID(0); + } + } + + // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. + if (hovered && (flags & ImGuiButtonFlags_AllowOverlapMode) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) + hovered = pressed = held = false; + + if (out_hovered) *out_hovered = hovered; + if (out_held) *out_held = held; + + return pressed; +} + +bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + ImVec2 pos = window->DC.CursorPos; + if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) + pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; + ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); + + const ImRect bb(pos, pos + size); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, &id)) + return false; + + if (window->DC.ButtonRepeat) flags |= ImGuiButtonFlags_Repeat; + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); + + // Render + const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); + + // Automatically close popups + //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + // CloseCurrentPopup(); + + return pressed; +} + +bool ImGui::Button(const char* label, const ImVec2& size_arg) +{ + return ButtonEx(label, size_arg, 0); +} + +// Small buttons fits within text without additional vertical spacing. +bool ImGui::SmallButton(const char* label) +{ + ImGuiContext& g = *GImGui; + float backup_padding_y = g.Style.FramePadding.y; + g.Style.FramePadding.y = 0.0f; + bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine); + g.Style.FramePadding.y = backup_padding_y; + return pressed; +} + +// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. +// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) +bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiID id = window->GetID(str_id); + ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + return pressed; +} + +// Upper-right button to close a window. +bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) +{ + ImGuiWindow* window = GetCurrentWindow(); + + const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_CloseButtonActive : hovered ? ImGuiCol_CloseButtonHovered : ImGuiCol_CloseButton); + const ImVec2 center = bb.GetCenter(); + window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), col, 12); + + const float cross_extent = (radius * 0.7071f) - 1.0f; + if (hovered) + { + window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), GetColorU32(ImGuiCol_Text)); + window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), GetColorU32(ImGuiCol_Text)); + } + + return pressed; +} + +void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + if (border_col.w > 0.0f) + bb.Max += ImVec2(2,2); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + if (border_col.w > 0.0f) + { + window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); + window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col)); + } + else + { + window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); + } +} + +// frame_padding < 0: uses FramePadding from style (default) +// frame_padding = 0: no framing +// frame_padding > 0: set framing size +// The color used are the button colors. +bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Default to using texture ID as ID. User can still push string/integer prefixes. + // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. + PushID((void *)user_texture_id); + const ImGuiID id = window->GetID("#image"); + PopID(); + + const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); + const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); + ItemSize(bb); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + + // Render + const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); + RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); + if (bg_col.w > 0.0f) + window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); + window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); + + return pressed; +} + +// Start logging ImGui output to TTY +void ImGui::LogToTTY(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + g.LogEnabled = true; + g.LogFile = stdout; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to given file +void ImGui::LogToFile(int max_depth, const char* filename) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + if (!filename) + { + filename = g.IO.LogFilename; + if (!filename) + return; + } + + g.LogFile = fopen(filename, "ab"); + if (!g.LogFile) + { + IM_ASSERT(g.LogFile != NULL); // Consider this an error + return; + } + g.LogEnabled = true; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +// Start logging ImGui output to clipboard +void ImGui::LogToClipboard(int max_depth) +{ + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + return; + ImGuiWindow* window = GetCurrentWindowRead(); + + g.LogEnabled = true; + g.LogFile = NULL; + g.LogStartDepth = window->DC.TreeDepth; + if (max_depth >= 0) + g.LogAutoExpandMaxDepth = max_depth; +} + +void ImGui::LogFinish() +{ + ImGuiContext& g = *GImGui; + if (!g.LogEnabled) + return; + + LogText(IM_NEWLINE); + g.LogEnabled = false; + if (g.LogFile != NULL) + { + if (g.LogFile == stdout) + fflush(g.LogFile); + else + fclose(g.LogFile); + g.LogFile = NULL; + } + if (g.LogClipboard->size() > 1) + { + SetClipboardText(g.LogClipboard->begin()); + g.LogClipboard->clear(); + } +} + +// Helper to display logging buttons +void ImGui::LogButtons() +{ + ImGuiContext& g = *GImGui; + + PushID("LogButtons"); + const bool log_to_tty = Button("Log To TTY"); SameLine(); + const bool log_to_file = Button("Log To File"); SameLine(); + const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); + PushItemWidth(80.0f); + PushAllowKeyboardFocus(false); + SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); + PopAllowKeyboardFocus(); + PopItemWidth(); + PopID(); + + // Start logging at the end of the function so that the buttons don't appear in the log + if (log_to_tty) + LogToTTY(g.LogAutoExpandMaxDepth); + if (log_to_file) + LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); + if (log_to_clipboard) + LogToClipboard(g.LogAutoExpandMaxDepth); +} + +bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +{ + if (flags & ImGuiTreeNodeFlags_Leaf) + return true; + + // We only write to the tree storage if the user clicks (or explicitely use SetNextTreeNode*** functions) + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiStorage* storage = window->DC.StateStorage; + + bool is_open; + if (g.SetNextTreeNodeOpenCond != 0) + { + if (g.SetNextTreeNodeOpenCond & ImGuiSetCond_Always) + { + is_open = g.SetNextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + // We treat ImGuiSetCondition_Once and ImGuiSetCondition_FirstUseEver the same because tree node state are not saved persistently. + const int stored_value = storage->GetInt(id, -1); + if (stored_value == -1) + { + is_open = g.SetNextTreeNodeOpenVal; + storage->SetInt(id, is_open); + } + else + { + is_open = stored_value != 0; + } + } + g.SetNextTreeNodeOpenCond = 0; + } + else + { + is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; + } + + // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). + // NB- If we are above max depth we still allow manually opened nodes to be logged. + if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) + is_open = true; + + return is_open; +} + +bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; + const ImVec2 padding = display_frame ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); + + if (!label_end) + label_end = FindRenderedTextEnd(label); + const ImVec2 label_size = CalcTextSize(label, label_end, false); + + // We vertically grow up to current line height up the typical widget height. + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset - padding.y); // Latch before ItemSize changes it + const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); + ImRect bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); + if (display_frame) + { + // Framed header expand a little outside the default padding + bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; + bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; + } + + const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing + const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser + ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); + + // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing + // (Ideally we'd want to add a flag for the user to specify we want want the hit test to be done up to the right side of the content or not) + const ImRect interact_bb = display_frame ? bb : ImRect(bb.Min.x, bb.Min.y, bb.Min.x + text_width + style.ItemSpacing.x*2, bb.Max.y); + bool is_open = TreeNodeBehaviorIsOpen(id, flags); + if (!ItemAdd(interact_bb, &id)) + { + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; + } + + // Flags that affects opening behavior: + // - 0(default) ..................... single-click anywhere to open + // - OpenOnDoubleClick .............. double-click anywhere to open + // - OpenOnArrow .................... single-click on arrow to open + // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open + ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowOverlapMode) ? ImGuiButtonFlags_AllowOverlapMode : 0); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); + bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); + if (pressed && !(flags & ImGuiTreeNodeFlags_Leaf)) + { + bool toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)); + if (flags & ImGuiTreeNodeFlags_OpenOnArrow) + toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)); + if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) + toggled |= g.IO.MouseDoubleClicked[0]; + if (toggled) + { + is_open = !is_open; + window->DC.StateStorage->SetInt(id, is_open); + } + } + if (flags & ImGuiTreeNodeFlags_AllowOverlapMode) + SetItemAllowOverlap(); + + // Render + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + const ImVec2 text_pos = bb.Min + ImVec2(text_offset_x, padding.y + text_base_offset_y); + if (display_frame) + { + // Framed type + RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); + RenderCollapseTriangle(bb.Min + padding + ImVec2(0.0f, text_base_offset_y), is_open, 1.0f); + if (g.LogEnabled) + { + // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. + const char log_prefix[] = "\n##"; + const char log_suffix[] = "##"; + LogRenderedText(text_pos, log_prefix, log_prefix+3); + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + LogRenderedText(text_pos, log_suffix+1, log_suffix+3); + } + else + { + RenderTextClipped(text_pos, bb.Max, label, label_end, &label_size); + } + } + else + { + // Unframed typed for tree nodes + if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) + RenderFrame(bb.Min, bb.Max, col, false); + + if (flags & ImGuiTreeNodeFlags_Bullet) + RenderBullet(bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); + else if (!(flags & ImGuiTreeNodeFlags_Leaf)) + RenderCollapseTriangle(bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open, 0.70f); + if (g.LogEnabled) + LogRenderedText(text_pos, ">"); + RenderText(text_pos, label, label_end, false); + } + + if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) + TreePushRawID(id); + return is_open; +} + +// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). +// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). +bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen, label); +} + +bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + if (p_open && !*p_open) + return false; + + ImGuiID id = window->GetID(label); + bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | ImGuiTreeNodeFlags_NoTreePushOnOpen | (p_open ? ImGuiTreeNodeFlags_AllowOverlapMode : 0), label); + if (p_open) + { + // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. + ImGuiContext& g = *GImGui; + float button_sz = g.FontSize * 0.5f; + if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) + *p_open = false; + } + + return is_open; +} + +bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + return TreeNodeBehavior(window->GetID(label), flags, label, NULL); +} + +bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); +} + +bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) +{ + return TreeNodeExV(str_id, 0, fmt, args); +} + +bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) +{ + return TreeNodeExV(ptr_id, 0, fmt, args); +} + +bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(str_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); + va_end(args); + return is_open; +} + +bool ImGui::TreeNode(const char* label) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + return TreeNodeBehavior(window->GetID(label), 0, label, NULL); +} + +void ImGui::TreeAdvanceToLabelPos() +{ + ImGuiContext& g = *GImGui; + g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); +} + +// Horizontal distance preceding label when using TreeNode() or Bullet() +float ImGui::GetTreeNodeToLabelSpacing() +{ + ImGuiContext& g = *GImGui; + return g.FontSize + (g.Style.FramePadding.x * 2.0f); +} + +void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond) +{ + ImGuiContext& g = *GImGui; + g.SetNextTreeNodeOpenVal = is_open; + g.SetNextTreeNodeOpenCond = cond ? cond : ImGuiSetCond_Always; +} + +void ImGui::PushID(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(str_id)); +} + +void ImGui::PushID(const char* str_id_begin, const char* str_id_end) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); +} + +void ImGui::PushID(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(ptr_id)); +} + +void ImGui::PushID(int int_id) +{ + const void* ptr_id = (void*)(intptr_t)int_id; + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.push_back(window->GetID(ptr_id)); +} + +void ImGui::PopID() +{ + ImGuiWindow* window = GetCurrentWindow(); + window->IDStack.pop_back(); +} + +ImGuiID ImGui::GetID(const char* str_id) +{ + return GImGui->CurrentWindow->GetID(str_id); +} + +ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) +{ + return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); +} + +ImGuiID ImGui::GetID(const void* ptr_id) +{ + return GImGui->CurrentWindow->GetID(ptr_id); +} + +void ImGui::Bullet() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + { + SameLine(0, style.FramePadding.x*2); + return; + } + + // Render and stay on same line + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + SameLine(0, style.FramePadding.x*2); +} + +// Text with a little bullet aligned to the typical tree node. +void ImGui::BulletTextV(const char* fmt, va_list args) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const char* text_begin = g.TempBuffer; + const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); + const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); + const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it + const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding + ItemSize(bb); + if (!ItemAdd(bb, NULL)) + return; + + // Render + RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); + RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); +} + +void ImGui::BulletText(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + BulletTextV(fmt, args); + va_end(args); +} + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, const char* display_format, char* buf, int buf_size) +{ + if (data_type == ImGuiDataType_Int) + ImFormatString(buf, buf_size, display_format, *(int*)data_ptr); + else if (data_type == ImGuiDataType_Float) + ImFormatString(buf, buf_size, display_format, *(float*)data_ptr); +} + +static inline void DataTypeFormatString(ImGuiDataType data_type, void* data_ptr, int decimal_precision, char* buf, int buf_size) +{ + if (data_type == ImGuiDataType_Int) + { + if (decimal_precision < 0) + ImFormatString(buf, buf_size, "%d", *(int*)data_ptr); + else + ImFormatString(buf, buf_size, "%.*d", decimal_precision, *(int*)data_ptr); + } + else if (data_type == ImGuiDataType_Float) + { + if (decimal_precision < 0) + ImFormatString(buf, buf_size, "%f", *(float*)data_ptr); // Ideally we'd have a minimum decimal precision of 1 to visually denote that it is a float, while hiding non-significant digits? + else + ImFormatString(buf, buf_size, "%.*f", decimal_precision, *(float*)data_ptr); + } +} + +static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* value1, const void* value2)// Store into value1 +{ + if (data_type == ImGuiDataType_Int) + { + if (op == '+') + *(int*)value1 = *(int*)value1 + *(const int*)value2; + else if (op == '-') + *(int*)value1 = *(int*)value1 - *(const int*)value2; + } + else if (data_type == ImGuiDataType_Float) + { + if (op == '+') + *(float*)value1 = *(float*)value1 + *(const float*)value2; + else if (op == '-') + *(float*)value1 = *(float*)value1 - *(const float*)value2; + } +} + +// User can input math operators (e.g. +100) to edit a numerical values. +static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* scalar_format) +{ + while (ImCharIsSpace(*buf)) + buf++; + + // We don't support '-' op because it would conflict with inputing negative value. + // Instead you can use +-100 to subtract from an existing value + char op = buf[0]; + if (op == '+' || op == '*' || op == '/') + { + buf++; + while (ImCharIsSpace(*buf)) + buf++; + } + else + { + op = 0; + } + if (!buf[0]) + return false; + + if (data_type == ImGuiDataType_Int) + { + if (!scalar_format) + scalar_format = "%d"; + int* v = (int*)data_ptr; + const int old_v = *v; + int arg0 = *v; + if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1) + return false; + + // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision + float arg1 = 0.0f; + if (op == '+') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 + arg1); } // Add (use "+-" to subtract) + else if (op == '*') { if (sscanf(buf, "%f", &arg1) == 1) *v = (int)(arg0 * arg1); } // Multiply + else if (op == '/') { if (sscanf(buf, "%f", &arg1) == 1 && arg1 != 0.0f) *v = (int)(arg0 / arg1); }// Divide + else { if (sscanf(buf, scalar_format, &arg0) == 1) *v = arg0; } // Assign constant + return (old_v != *v); + } + else if (data_type == ImGuiDataType_Float) + { + // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in + scalar_format = "%f"; + float* v = (float*)data_ptr; + const float old_v = *v; + float arg0 = *v; + if (op && sscanf(initial_value_buf, scalar_format, &arg0) < 1) + return false; + + float arg1 = 0.0f; + if (sscanf(buf, scalar_format, &arg1) < 1) + return false; + if (op == '+') { *v = arg0 + arg1; } // Add (use "+-" to subtract) + else if (op == '*') { *v = arg0 * arg1; } // Multiply + else if (op == '/') { if (arg1 != 0.0f) *v = arg0 / arg1; } // Divide + else { *v = arg1; } // Assign constant + return (old_v != *v); + } + + return false; +} + +// Create text input in place of a slider (when CTRL+Clicking on slider) +bool ImGui::InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) + SetActiveID(g.ScalarAsInputTextId, window); + SetHoveredID(0); + FocusableItemUnregister(window); + + char buf[32]; + DataTypeFormatString(data_type, data_ptr, decimal_precision, buf, IM_ARRAYSIZE(buf)); + bool text_value_changed = InputTextEx(label, buf, IM_ARRAYSIZE(buf), aabb.GetSize(), ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_AutoSelectAll); + if (g.ScalarAsInputTextId == 0) + { + // First frame + IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID (else we'd need to store them both, which is also possible) + g.ScalarAsInputTextId = g.ActiveId; + SetHoveredID(id); + } + else if (g.ActiveId != g.ScalarAsInputTextId) + { + // Release + g.ScalarAsInputTextId = 0; + } + if (text_value_changed) + return DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, NULL); + return false; +} + +// Parse display precision back from the display format string +int ImGui::ParseFormatPrecision(const char* fmt, int default_precision) +{ + int precision = default_precision; + while ((fmt = strchr(fmt, '%')) != NULL) + { + fmt++; + if (fmt[0] == '%') { fmt++; continue; } // Ignore "%%" + while (*fmt >= '0' && *fmt <= '9') + fmt++; + if (*fmt == '.') + { + precision = atoi(fmt + 1); + if (precision < 0 || precision > 10) + precision = default_precision; + } + break; + } + return precision; +} + +float ImGui::RoundScalar(float value, int decimal_precision) +{ + // Round past decimal precision + // So when our value is 1.99999 with a precision of 0.001 we'll end up rounding to 2.0 + // FIXME: Investigate better rounding methods + static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; + float min_step = (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : powf(10.0f, (float)-decimal_precision); + bool negative = value < 0.0f; + value = fabsf(value); + float remainder = fmodf(value, min_step); + if (remainder <= min_step*0.5f) + value -= remainder; + else + value += (min_step - remainder); + return negative ? -value : value; +} + +bool ImGui::SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + const ImGuiStyle& style = g.Style; + + // Draw frame + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const bool is_non_linear = fabsf(power - 1.0f) > 0.0001f; + const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; + + const float grab_padding = 2.0f; + const float slider_sz = is_horizontal ? (frame_bb.GetWidth() - grab_padding * 2.0f) : (frame_bb.GetHeight() - grab_padding * 2.0f); + float grab_sz; + if (decimal_precision > 0) + grab_sz = ImMin(style.GrabMinSize, slider_sz); + else + grab_sz = ImMin(ImMax(1.0f * (slider_sz / (v_max-v_min+1.0f)), style.GrabMinSize), slider_sz); // Integer sliders, if possible have the grab size represent 1 unit + const float slider_usable_sz = slider_sz - grab_sz; + const float slider_usable_pos_min = (is_horizontal ? frame_bb.Min.x : frame_bb.Min.y) + grab_padding + grab_sz*0.5f; + const float slider_usable_pos_max = (is_horizontal ? frame_bb.Max.x : frame_bb.Max.y) - grab_padding - grab_sz*0.5f; + + // For logarithmic sliders that cross over sign boundary we want the exponential increase to be symmetric around 0.0f + float linear_zero_pos = 0.0f; // 0.0->1.0f + if (v_min * v_max < 0.0f) + { + // Different sign + const float linear_dist_min_to_0 = powf(fabsf(0.0f - v_min), 1.0f/power); + const float linear_dist_max_to_0 = powf(fabsf(v_max - 0.0f), 1.0f/power); + linear_zero_pos = linear_dist_min_to_0 / (linear_dist_min_to_0+linear_dist_max_to_0); + } + else + { + // Same sign + linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; + } + + // Process clicking on the slider + bool value_changed = false; + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; + float normalized_pos = ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f); + if (!is_horizontal) + normalized_pos = 1.0f - normalized_pos; + + float new_value; + if (is_non_linear) + { + // Account for logarithmic scale on both sides of the zero + if (normalized_pos < linear_zero_pos) + { + // Negative: rescale to the negative range before powering + float a = 1.0f - (normalized_pos / linear_zero_pos); + a = powf(a, power); + new_value = ImLerp(ImMin(v_max,0.0f), v_min, a); + } + else + { + // Positive: rescale to the positive range before powering + float a; + if (fabsf(linear_zero_pos - 1.0f) > 1.e-6f) + a = (normalized_pos - linear_zero_pos) / (1.0f - linear_zero_pos); + else + a = normalized_pos; + a = powf(a, power); + new_value = ImLerp(ImMax(v_min,0.0f), v_max, a); + } + } + else + { + // Linear slider + new_value = ImLerp(v_min, v_max, normalized_pos); + } + + // Round past decimal precision + new_value = RoundScalar(new_value, decimal_precision); + if (*v != new_value) + { + *v = new_value; + value_changed = true; + } + } + else + { + SetActiveID(0); + } + } + + // Calculate slider grab positioning + float grab_t; + if (is_non_linear) + { + float v_clamped = ImClamp(*v, v_min, v_max); + if (v_clamped < 0.0f) + { + const float f = 1.0f - (v_clamped - v_min) / (ImMin(0.0f,v_max) - v_min); + grab_t = (1.0f - powf(f, 1.0f/power)) * linear_zero_pos; + } + else + { + const float f = (v_clamped - ImMax(0.0f,v_min)) / (v_max - ImMax(0.0f,v_min)); + grab_t = linear_zero_pos + powf(f, 1.0f/power) * (1.0f - linear_zero_pos); + } + } + else + { + // Linear slider + grab_t = (ImClamp(*v, v_min, v_max) - v_min) / (v_max - v_min); + } + + // Draw + if (!is_horizontal) + grab_t = 1.0f - grab_t; + const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + ImRect grab_bb; + if (is_horizontal) + grab_bb = ImRect(ImVec2(grab_pos - grab_sz*0.5f, frame_bb.Min.y + grab_padding), ImVec2(grab_pos + grab_sz*0.5f, frame_bb.Max.y - grab_padding)); + else + grab_bb = ImRect(ImVec2(frame_bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f), ImVec2(frame_bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f)); + window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); + + return value_changed; +} + +// Use power!=1.0 for logarithmic sliders. +// Adjust display_format to decorate the value with a prefix or a suffix. +// "%.3f" 1.234 +// "%5.2f secs" 01.23 secs +// "Gold: %.0f" Gold: 1 +bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + // Tabbing or CTRL-clicking on Slider turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); + if (tab_focus_requested || (hovered && g.IO.MouseClicked[0])) + { + SetActiveID(id, window); + FocusWindow(window); + + if (tab_focus_requested || g.IO.KeyCtrl) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); + + ItemSize(total_bb, style.FramePadding.y); + + // Actual slider behavior + render grab + const bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(frame_bb, &id)) + return false; + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + if (hovered && g.IO.MouseClicked[0]) + { + SetActiveID(id, window); + FocusWindow(window); + } + + // Actual slider behavior + render grab + bool value_changed = SliderBehavior(frame_bb, id, v, v_min, v_max, power, decimal_precision, ImGuiSliderFlags_Vertical); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + // For the vertical slider we allow centered text to overlap the frame padding + char value_buf[64]; + char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + return value_changed; +} + +bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) +{ + float v_deg = (*v_rad) * 360.0f / (2*IM_PI); + bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); + *v_rad = v_deg * (2*IM_PI) / 360.0f; + return value_changed; +} + +bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = SliderFloat(label, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); + *v = (int)v_f; + return value_changed; +} + +bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = VSliderFloat(label, size, &v_f, (float)v_min, (float)v_max, display_format, 1.0f); + *v = (int)v_f; + return value_changed; +} + +// Add multiple sliders on 1 line for compact edition of multiple components +bool ImGui::SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= SliderFloat("##v", &v[i], v_min, v_max, display_format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 2, v_min, v_max, display_format, power); +} + +bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 3, v_min, v_max, display_format, power); +} + +bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format, float power) +{ + return SliderFloatN(label, v, 4, v_min, v_max, display_format, power); +} + +bool ImGui::SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= SliderInt("##v", &v[i], v_min, v_max, display_format); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 2, v_min, v_max, display_format); +} + +bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 3, v_min, v_max, display_format); +} + +bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format) +{ + return SliderIntN(label, v, 4, v_min, v_max, display_format); +} + +bool ImGui::DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power) +{ + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + // Draw frame + const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); + RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); + + bool value_changed = false; + + // Process clicking on the drag + if (g.ActiveId == id) + { + if (g.IO.MouseDown[0]) + { + if (g.ActiveIdIsJustActivated) + { + // Lock current value on click + g.DragCurrentValue = *v; + g.DragLastMouseDelta = ImVec2(0.f, 0.f); + } + + float v_cur = g.DragCurrentValue; + const ImVec2 mouse_drag_delta = GetMouseDragDelta(0, 1.0f); + if (fabsf(mouse_drag_delta.x - g.DragLastMouseDelta.x) > 0.0f) + { + float speed = v_speed; + if (speed == 0.0f && (v_max - v_min) != 0.0f && (v_max - v_min) < FLT_MAX) + speed = (v_max - v_min) * g.DragSpeedDefaultRatio; + if (g.IO.KeyShift && g.DragSpeedScaleFast >= 0.0f) + speed = speed * g.DragSpeedScaleFast; + if (g.IO.KeyAlt && g.DragSpeedScaleSlow >= 0.0f) + speed = speed * g.DragSpeedScaleSlow; + + float delta = (mouse_drag_delta.x - g.DragLastMouseDelta.x) * speed; + if (fabsf(power - 1.0f) > 0.001f) + { + // Logarithmic curve on both side of 0.0 + float v0_abs = v_cur >= 0.0f ? v_cur : -v_cur; + float v0_sign = v_cur >= 0.0f ? 1.0f : -1.0f; + float v1 = powf(v0_abs, 1.0f / power) + (delta * v0_sign); + float v1_abs = v1 >= 0.0f ? v1 : -v1; + float v1_sign = v1 >= 0.0f ? 1.0f : -1.0f; // Crossed sign line + v_cur = powf(v1_abs, power) * v0_sign * v1_sign; // Reapply sign + } + else + { + v_cur += delta; + } + g.DragLastMouseDelta.x = mouse_drag_delta.x; + + // Clamp + if (v_min < v_max) + v_cur = ImClamp(v_cur, v_min, v_max); + g.DragCurrentValue = v_cur; + } + + // Round to user desired precision, then apply + v_cur = RoundScalar(v_cur, decimal_precision); + if (*v != v_cur) + { + *v = v_cur; + value_changed = true; + } + } + else + { + SetActiveID(0); + } + } + + return value_changed; +} + +bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + + // NB- we don't call ItemSize() yet because we may turn into a text edit box below + if (!ItemAdd(total_bb, &id)) + { + ItemSize(total_bb, style.FramePadding.y); + return false; + } + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + SetHoveredID(id); + + if (!display_format) + display_format = "%.3f"; + int decimal_precision = ParseFormatPrecision(display_format, 3); + + // Tabbing or CTRL-clicking on Drag turns it into an input box + bool start_text_input = false; + const bool tab_focus_requested = FocusableItemRegister(window, g.ActiveId == id); + if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] | g.IO.MouseDoubleClicked[0]))) + { + SetActiveID(id, window); + FocusWindow(window); + + if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0]) + { + start_text_input = true; + g.ScalarAsInputTextId = 0; + } + } + if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) + return InputScalarAsWidgetReplacement(frame_bb, label, ImGuiDataType_Float, v, id, decimal_precision); + + // Actual drag behavior + ItemSize(total_bb, style.FramePadding.y); + const bool value_changed = DragBehavior(frame_bb, id, v, v_speed, v_min, v_max, decimal_precision, power); + + // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. + char value_buf[64]; + const char* value_buf_end = value_buf + ImFormatString(value_buf, IM_ARRAYSIZE(value_buf), display_format, *v); + RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); + + return value_changed; +} + +bool ImGui::DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= DragFloat("##v", &v[i], v_speed, v_min, v_max, display_format, power); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 2, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 3, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* display_format, float power) +{ + return DragFloatN(label, v, 4, v_speed, v_min, v_max, display_format, power); +} + +bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* display_format, const char* display_format_max, float power) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + + bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, display_format_max ? display_format_max : display_format, power); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +// NB: v_speed is float to allow adjusting the drag speed with more precision +bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* display_format) +{ + if (!display_format) + display_format = "%.0f"; + float v_f = (float)*v; + bool value_changed = DragFloat(label, &v_f, v_speed, (float)v_min, (float)v_max, display_format); + *v = (int)v_f; + return value_changed; +} + +bool ImGui::DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= DragInt("##v", &v[i], v_speed, v_min, v_max, display_format); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 2, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 3, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* display_format) +{ + return DragIntN(label, v, 4, v_speed, v_min, v_max, display_format); +} + +bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* display_format, const char* display_format_max) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + PushID(label); + BeginGroup(); + PushMultiItemsWidths(2); + + bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), display_format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, display_format_max ? display_format_max : display_format); + PopItemWidth(); + SameLine(0, g.Style.ItemInnerSpacing.x); + + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + PopID(); + + return value_changed; +} + +void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + if (graph_size.x == 0.0f) + graph_size.x = CalcItemWidth(); + if (graph_size.y == 0.0f) + graph_size.y = label_size.y + (style.FramePadding.y * 2); + + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); + const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, NULL)) + return; + + // Determine scale from values if not specified + if (scale_min == FLT_MAX || scale_max == FLT_MAX) + { + float v_min = FLT_MAX; + float v_max = -FLT_MAX; + for (int i = 0; i < values_count; i++) + { + const float v = values_getter(data, i); + v_min = ImMin(v_min, v); + v_max = ImMax(v_max, v); + } + if (scale_min == FLT_MAX) + scale_min = v_min; + if (scale_max == FLT_MAX) + scale_max = v_max; + } + + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + if (values_count > 0) + { + int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); + + // Tooltip on hover + int v_hovered = -1; + if (IsHovered(inner_bb, 0)) + { + const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); + const int v_idx = (int)(t * item_count); + IM_ASSERT(v_idx >= 0 && v_idx < values_count); + + const float v0 = values_getter(data, (v_idx + values_offset) % values_count); + const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); + if (plot_type == ImGuiPlotType_Lines) + SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); + else if (plot_type == ImGuiPlotType_Histogram) + SetTooltip("%d: %8.4g", v_idx, v0); + v_hovered = v_idx; + } + + const float t_step = 1.0f / (float)res_w; + + float v0 = values_getter(data, (0 + values_offset) % values_count); + float t0 = 0.0f; + ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) / (scale_max - scale_min)) ); // Point in the normalized space of our target rectangle + + const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); + const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); + + for (int n = 0; n < res_w; n++) + { + const float t1 = t0 + t_step; + const int v1_idx = (int)(t0 * item_count + 0.5f); + IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); + const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); + const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) / (scale_max - scale_min)) ); + + // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. + ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); + ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, 1.0f)); + if (plot_type == ImGuiPlotType_Lines) + { + window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + else if (plot_type == ImGuiPlotType_Histogram) + { + if (pos1.x >= pos0.x + 2.0f) + pos1.x -= 1.0f; + window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); + } + + t0 = t1; + tp0 = tp1; + } + } + + // Text overlay + if (overlay_text) + RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); + + if (label_size.x > 0.0f) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); +} + +struct ImGuiPlotArrayGetterData +{ + const float* Values; + int Stride; + + ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } +}; + +static float Plot_ArrayGetter(void* data, int idx) +{ + ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; + const float v = *(float*)(void*)((unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); + return v; +} + +void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) +{ + ImGuiPlotArrayGetterData data(values, stride); + PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) +{ + PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); +} + +// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size +void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + ImVec2 pos = window->DC.CursorPos; + ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); + ItemSize(bb, style.FramePadding.y); + if (!ItemAdd(bb, NULL)) + return; + + // Render + fraction = ImSaturate(fraction); + RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + bb.Reduce(ImVec2(window->BorderSize, window->BorderSize)); + const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); + RenderFrame(bb.Min, fill_br, GetColorU32(ImGuiCol_PlotHistogram), false, style.FrameRounding); + + // Default displaying the fraction as percentage string, but user can override it + char overlay_buf[32]; + if (!overlay) + { + ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); + overlay = overlay_buf; + } + + ImVec2 overlay_size = CalcTextSize(overlay, NULL); + if (overlay_size.x > 0.0f) + RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); +} + +bool ImGui::Checkbox(const char* label, bool* v) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); + } + + if (!ItemAdd(total_bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + if (pressed) + *v = !(*v); + + RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); + if (*v) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + window->DrawList->AddRectFilled(check_bb.Min+ImVec2(pad,pad), check_bb.Max-ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), style.FrameRounding); + } + + if (g.LogEnabled) + LogRenderedText(text_bb.GetTL(), *v ? "[x]" : "[ ]"); + if (label_size.x > 0.0f) + RenderText(text_bb.GetTL(), label); + + return pressed; +} + +bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) +{ + bool v = ((*flags & flags_value) == flags_value); + bool pressed = Checkbox(label, &v); + if (pressed) + { + if (v) + *flags |= flags_value; + else + *flags &= ~flags_value; + } + + return pressed; +} + +bool ImGui::RadioButton(const char* label, bool active) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); + ItemSize(check_bb, style.FramePadding.y); + + ImRect total_bb = check_bb; + if (label_size.x > 0) + SameLine(0, style.ItemInnerSpacing.x); + const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); + if (label_size.x > 0) + { + ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); + total_bb.Add(text_bb); + } + + if (!ItemAdd(total_bb, &id)) + return false; + + ImVec2 center = check_bb.GetCenter(); + center.x = (float)(int)center.x + 0.5f; + center.y = (float)(int)center.y + 0.5f; + const float radius = check_bb.GetHeight() * 0.5f; + + bool hovered, held; + bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); + + window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); + if (active) + { + const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); + const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); + window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); + } + + if (window->Flags & ImGuiWindowFlags_ShowBorders) + { + window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16); + window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16); + } + + if (g.LogEnabled) + LogRenderedText(text_bb.GetTL(), active ? "(x)" : "( )"); + if (label_size.x > 0.0f) + RenderText(text_bb.GetTL(), label); + + return pressed; +} + +bool ImGui::RadioButton(const char* label, int* v, int v_button) +{ + const bool pressed = RadioButton(label, *v == v_button); + if (pressed) + { + *v = v_button; + } + return pressed; +} + +static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) +{ + int line_count = 0; + const char* s = text_begin; + while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding + if (c == '\n') + line_count++; + s--; + if (s[0] != '\n' && s[0] != '\r') + line_count++; + *out_text_end = s; + return line_count; +} + +static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +{ + ImFont* font = GImGui->Font; + const float line_height = GImGui->FontSize; + const float scale = line_height / font->FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const ImWchar* s = text_begin; + while (s < text_end) + { + unsigned int c = (unsigned int)(*s++); + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + if (stop_on_new_line) + break; + continue; + } + if (c == '\r') + continue; + + const float char_width = font->GetCharAdvance((unsigned short)c) * scale; + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (out_offset) + *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n + + if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) +namespace ImGuiStb +{ + +static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } +static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } +static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } +static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } +static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; +static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) +{ + const ImWchar* text = obj->Text.Data; + const ImWchar* text_remaining = NULL; + const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + r->x0 = 0.0f; + r->x1 = size.x; + r->baseline_y_delta = size.y; + r->ymin = 0.0f; + r->ymax = size.y; + r->num_chars = (int)(text_remaining - (text + line_start_idx)); +} + +static bool is_separator(unsigned int c) { return ImCharIsSpace(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } +static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } +#ifdef __APPLE__ // FIXME: Move setting to IO structure +static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } +#else +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } +#endif +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL + +static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) +{ + ImWchar* dst = obj->Text.Data + pos; + + // We maintain our buffer length in both UTF-8 and wchar formats + obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); + obj->CurLenW -= n; + + // Offset remaining text + const ImWchar* src = obj->Text.Data + pos + n; + while (ImWchar c = *src++) + *dst++ = c; + *dst = '\0'; +} + +static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) +{ + const int text_len = obj->CurLenW; + IM_ASSERT(pos <= text_len); + if (new_text_len + text_len + 1 > obj->Text.Size) + return false; + + const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); + if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) + return false; + + ImWchar* text = obj->Text.Data; + if (pos != text_len) + memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); + memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); + + obj->CurLenW += new_text_len; + obj->CurLenA += new_text_len_utf8; + obj->Text[obj->CurLenW] = '\0'; + + return true; +} + +// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) +#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left +#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right +#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up +#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down +#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line +#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line +#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text +#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text +#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor +#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor +#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo +#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo +#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word +#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word +#define STB_TEXTEDIT_K_SHIFT 0x20000 + +#define STB_TEXTEDIT_IMPLEMENTATION +#include "stb_textedit.h" + +} + +void ImGuiTextEditState::OnKeyPressed(int key) +{ + stb_textedit_key(this, &StbState, key); + CursorFollow = true; + CursorAnimReset(); +} + +// Public API to manipulate UTF-8 text +// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) +// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. +void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) +{ + IM_ASSERT(pos + bytes_count <= BufTextLen); + char* dst = Buf + pos; + const char* src = Buf + pos + bytes_count; + while (char c = *src++) + *dst++ = c; + *dst = '\0'; + + if (CursorPos + bytes_count >= pos) + CursorPos -= bytes_count; + else if (CursorPos >= pos) + CursorPos = pos; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen -= bytes_count; +} + +void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) +{ + const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); + if (new_text_len + BufTextLen + 1 >= BufSize) + return; + + if (BufTextLen != pos) + memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); + memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); + Buf[BufTextLen + new_text_len] = '\0'; + + if (CursorPos >= pos) + CursorPos += new_text_len; + SelectionStart = SelectionEnd = CursorPos; + BufDirty = true; + BufTextLen += new_text_len; +} + +// Return false to discard a character. +static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + unsigned int c = *p_char; + + if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) + { + bool pass = false; + pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); + pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); + if (!pass) + return false; + } + + if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. + return false; + + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank)) + { + if (flags & ImGuiInputTextFlags_CharsDecimal) + if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) + return false; + + if (flags & ImGuiInputTextFlags_CharsHexadecimal) + if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) + return false; + + if (flags & ImGuiInputTextFlags_CharsUppercase) + if (c >= 'a' && c <= 'z') + *p_char = (c += (unsigned int)('A'-'a')); + + if (flags & ImGuiInputTextFlags_CharsNoBlank) + if (ImCharIsSpace(c)) + return false; + } + + if (flags & ImGuiInputTextFlags_CallbackCharFilter) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; + callback_data.EventChar = (ImWchar)c; + callback_data.Flags = flags; + callback_data.UserData = user_data; + if (callback(&callback_data) != 0) + return false; + *p_char = callback_data.EventChar; + if (!callback_data.EventChar) + return false; + } + + return true; +} + +// Edit a string of text +// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. +// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 +bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) + IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) + + ImGuiContext& g = *GImGui; + const ImGuiIO& io = g.IO; + const ImGuiStyle& style = g.Style; + + const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; + const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; + const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; + + if (is_multiline) // Open group before calling GetID() because groups tracks id created during their spawn + BeginGroup(); + const ImGuiID id = window->GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); + + ImGuiWindow* draw_window = window; + if (is_multiline) + { + if (!BeginChildFrame(id, frame_bb.GetSize())) + { + EndChildFrame(); + EndGroup(); + return false; + } + draw_window = GetCurrentWindow(); + size.x -= draw_window->ScrollbarSizes.x; + } + else + { + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) + return false; + } + + // Password pushes a temporary font with only a fallback glyph + if (is_password) + { + const ImFont::Glyph* glyph = g.Font->FindGlyph('*'); + ImFont* password_font = &g.InputTextPasswordFont; + password_font->FontSize = g.Font->FontSize; + password_font->Scale = g.Font->Scale; + password_font->DisplayOffset = g.Font->DisplayOffset; + password_font->Ascent = g.Font->Ascent; + password_font->Descent = g.Font->Descent; + password_font->ContainerAtlas = g.Font->ContainerAtlas; + password_font->FallbackGlyph = glyph; + password_font->FallbackXAdvance = glyph->XAdvance; + IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexXAdvance.empty() && password_font->IndexLookup.empty()); + PushFont(password_font); + } + + // NB: we are only allowed to access 'edit_state' if we are the active widget. + ImGuiTextEditState& edit_state = g.InputTextState; + + const bool focus_requested = FocusableItemRegister(window, g.ActiveId == id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing + const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); + const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; + + const bool hovered = IsHovered(frame_bb, id); + if (hovered) + { + SetHoveredID(id); + g.MouseCursor = ImGuiMouseCursor_TextInput; + } + const bool user_clicked = hovered && io.MouseClicked[0]; + const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); + + bool select_all = (g.ActiveId != id) && (flags & ImGuiInputTextFlags_AutoSelectAll) != 0; + if (focus_requested || user_clicked || user_scrolled) + { + if (g.ActiveId != id) + { + // Start edition + // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) + // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) + const int prev_len_w = edit_state.CurLenW; + edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. + ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. + edit_state.CursorAnimReset(); + + // Preserve cursor position and undo/redo stack if we come back to same widget + // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). + const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); + if (recycle_state) + { + // Recycle existing cursor/selection/undo stack but clamp position + // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. + edit_state.CursorClamp(); + } + else + { + edit_state.Id = id; + edit_state.ScrollX = 0.0f; + stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); + if (!is_multiline && focus_requested_by_code) + select_all = true; + } + if (flags & ImGuiInputTextFlags_AlwaysInsertMode) + edit_state.StbState.insert_mode = true; + if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) + select_all = true; + } + SetActiveID(id, window); + FocusWindow(window); + } + else if (io.MouseClicked[0]) + { + // Release focus when we click outside + if (g.ActiveId == id) + SetActiveID(0); + } + + bool value_changed = false; + bool enter_pressed = false; + + if (g.ActiveId == id) + { + if (!is_editable && !g.ActiveIdIsJustActivated) + { + // When read-only we always use the live data passed to the function + edit_state.Text.resize(buf_size+1); + const char* buf_end = NULL; + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); + edit_state.CurLenA = (int)(buf_end - buf); + edit_state.CursorClamp(); + } + + edit_state.BufSizeA = buf_size; + + // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. + // Down the line we should have a cleaner library-wide concept of Selected vs Active. + g.ActiveIdAllowOverlap = !io.MouseDown[0]; + + // Edit in progress + const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; + const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); + + const bool osx_double_click_selects_words = io.OSXBehaviors; // OS X style: Double click selects by word instead of selecting whole text + if (select_all || (hovered && !osx_double_click_selects_words && io.MouseDoubleClicked[0])) + { + edit_state.SelectAll(); + edit_state.SelectedAllMouseLock = true; + } + else if (hovered && osx_double_click_selects_words && io.MouseDoubleClicked[0]) + { + // Select a word only, OS X style (by simulating keystrokes) + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); + edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); + } + else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) + { + stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + } + else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) + { + stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); + edit_state.CursorAnimReset(); + edit_state.CursorFollow = true; + } + if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) + edit_state.SelectedAllMouseLock = false; + + if (io.InputCharacters[0]) + { + // Process text input (before we check for Return because using some IME will effectively send a Return?) + // We ignore CTRL inputs, but need to allow CTRL+ALT as some keyboards (e.g. German) use AltGR - which is Alt+Ctrl - to input certain characters. + if (!(io.KeyCtrl && !io.KeyAlt) && is_editable) + { + for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) + if (unsigned int c = (unsigned int)io.InputCharacters[n]) + { + // Insert character if they pass filtering + if (!InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; + edit_state.OnKeyPressed((int)c); + } + } + + // Consume characters + memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); + } + + // Handle various key-presses + bool cancel_edit = false; + const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); + const bool is_shortcut_key_only = (io.OSXBehaviors ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl + const bool is_wordmove_key_down = io.OSXBehaviors ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl + const bool is_startend_key_down = io.OSXBehaviors && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End + + if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } + else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) + { + if (!edit_state.HasSelection()) + { + if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); + else if (io.OSXBehaviors && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); + } + edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); + } + else if (IsKeyPressedMap(ImGuiKey_Enter)) + { + bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; + if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + { + SetActiveID(0); + enter_pressed = true; + } + else if (is_editable) + { + unsigned int c = '\n'; // Insert new line + if (InputTextFilterCharacter(&c, flags, callback, user_data)) + edit_state.OnKeyPressed((int)c); + } + } + else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) + { + unsigned int c = '\t'; // Insert TAB + if (InputTextFilterCharacter(&c, flags, callback, user_data)) + edit_state.OnKeyPressed((int)c); + } + else if (IsKeyPressedMap(ImGuiKey_Escape)) { SetActiveID(0); cancel_edit = true; } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Z) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_UNDO); edit_state.ClearSelection(); } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_Y) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_REDO); edit_state.ClearSelection(); } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_A)) { edit_state.SelectAll(); edit_state.CursorFollow = true; } + else if (is_shortcut_key_only && !is_password && ((IsKeyPressedMap(ImGuiKey_X) && is_editable) || IsKeyPressedMap(ImGuiKey_C)) && (!is_multiline || edit_state.HasSelection())) + { + // Cut, Copy + const bool cut = IsKeyPressedMap(ImGuiKey_X); + if (cut && !edit_state.HasSelection()) + edit_state.SelectAll(); + + if (io.SetClipboardTextFn) + { + const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; + const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; + edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1); + ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie); + SetClipboardText(edit_state.TempTextBuffer.Data); + } + + if (cut) + { + edit_state.CursorFollow = true; + stb_textedit_cut(&edit_state, &edit_state.StbState); + } + } + else if (is_shortcut_key_only && IsKeyPressedMap(ImGuiKey_V) && is_editable) + { + // Paste + if (const char* clipboard = GetClipboardText()) + { + // Filter pasted buffer + const int clipboard_len = (int)strlen(clipboard); + ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); + int clipboard_filtered_len = 0; + for (const char* s = clipboard; *s; ) + { + unsigned int c; + s += ImTextCharFromUtf8(&c, s, NULL); + if (c == 0) + break; + if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) + continue; + clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; + } + clipboard_filtered[clipboard_filtered_len] = 0; + if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation + { + stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); + edit_state.CursorFollow = true; + } + ImGui::MemFree(clipboard_filtered); + } + } + + if (cancel_edit) + { + // Restore initial value + if (is_editable) + { + ImStrncpy(buf, edit_state.InitialText.Data, buf_size); + value_changed = true; + } + } + else + { + // Apply new value immediately - copy modified buffer back + // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer + // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. + // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. + if (is_editable) + { + edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); + ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); + } + + // User callback + if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) + { + IM_ASSERT(callback != NULL); + + // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. + ImGuiInputTextFlags event_flag = 0; + ImGuiKey event_key = ImGuiKey_COUNT; + if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) + { + event_flag = ImGuiInputTextFlags_CallbackCompletion; + event_key = ImGuiKey_Tab; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_UpArrow; + } + else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) + { + event_flag = ImGuiInputTextFlags_CallbackHistory; + event_key = ImGuiKey_DownArrow; + } + else if (flags & ImGuiInputTextFlags_CallbackAlways) + event_flag = ImGuiInputTextFlags_CallbackAlways; + + if (event_flag) + { + ImGuiTextEditCallbackData callback_data; + memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); + callback_data.EventFlag = event_flag; + callback_data.Flags = flags; + callback_data.UserData = user_data; + callback_data.ReadOnly = !is_editable; + + callback_data.EventKey = event_key; + callback_data.Buf = edit_state.TempTextBuffer.Data; + callback_data.BufTextLen = edit_state.CurLenA; + callback_data.BufSize = edit_state.BufSizeA; + callback_data.BufDirty = false; + + // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) + ImWchar* text = edit_state.Text.Data; + const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); + const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); + const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); + + // Call user code + callback(&callback_data); + + // Read back what user may have modified + IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields + IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); + IM_ASSERT(callback_data.Flags == flags); + if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); + if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); + if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); + if (callback_data.BufDirty) + { + IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! + edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); + edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() + edit_state.CursorAnimReset(); + } + } + } + + // Copy back to user buffer + if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) + { + ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); + value_changed = true; + } + } + } + + // Render + // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. + const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; + + if (!is_multiline) + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + + const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size + ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; + ImVec2 text_size(0.f, 0.f); + const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); + if (g.ActiveId == id || is_currently_scrolling) + { + edit_state.CursorAnim += io.DeltaTime; + + // This is going to be messy. We need to: + // - Display the text (this alone can be more easily clipped) + // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) + // - Measure text height (for scrollbar) + // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) + // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. + const ImWchar* text_begin = edit_state.Text.Data; + ImVec2 cursor_offset, select_start_offset; + + { + // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. + const ImWchar* searches_input_ptr[2]; + searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; + searches_input_ptr[1] = NULL; + int searches_remaining = 1; + int searches_result_line_number[2] = { -1, -999 }; + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + searches_result_line_number[1] = -1; + searches_remaining++; + } + + // Iterate all lines to find our line numbers + // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. + searches_remaining += is_multiline ? 1 : 0; + int line_count = 0; + for (const ImWchar* s = text_begin; *s != 0; s++) + if (*s == '\n') + { + line_count++; + if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } + if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } + } + line_count++; + if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; + if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; + + // Calculate 2d position by finding the beginning of the line and measuring distance + cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.y = searches_result_line_number[0] * g.FontSize; + if (searches_result_line_number[1] >= 0) + { + select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.y = searches_result_line_number[1] * g.FontSize; + } + + // Calculate text height + if (is_multiline) + text_size = ImVec2(size.x, line_count * g.FontSize); + } + + // Scroll + if (edit_state.CursorFollow) + { + // Horizontal scroll in chunks of quarter width + if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) + { + const float scroll_increment_x = size.x * 0.25f; + if (cursor_offset.x < edit_state.ScrollX) + edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); + else if (cursor_offset.x - size.x >= edit_state.ScrollX) + edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); + } + else + { + edit_state.ScrollX = 0.0f; + } + + // Vertical scroll + if (is_multiline) + { + float scroll_y = draw_window->Scroll.y; + if (cursor_offset.y - g.FontSize < scroll_y) + scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); + else if (cursor_offset.y - size.y >= scroll_y) + scroll_y = cursor_offset.y - size.y; + draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag + draw_window->Scroll.y = scroll_y; + render_pos.y = draw_window->DC.CursorPos.y; + } + } + edit_state.CursorFollow = false; + const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); + + // Draw selection + if (edit_state.StbState.select_start != edit_state.StbState.select_end) + { + const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); + const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); + + float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. + float bg_offy_dn = is_multiline ? 0.0f : 2.0f; + ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); + ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; + for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) + { + if (rect_pos.y > clip_rect.w + g.FontSize) + break; + if (rect_pos.y < clip_rect.y) + { + while (p < text_selected_end) + if (*p++ == '\n') + break; + } + else + { + ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines + ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); + rect.Clip(clip_rect); + if (rect.Overlaps(clip_rect)) + draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); + } + rect_pos.x = render_pos.x - render_scroll.x; + rect_pos.y += g.FontSize; + } + } + + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); + + // Draw blinking cursor + bool cursor_is_visible = (g.InputTextState.CursorAnim <= 0.0f) || fmodf(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; + ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; + ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); + if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) + draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); + + // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) + if (is_editable) + g.OsImePosRequest = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); + } + else + { + // Render text only + const char* buf_end = NULL; + if (is_multiline) + text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width + draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); + } + + if (is_multiline) + { + Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line + EndChildFrame(); + EndGroup(); + } + + if (is_password) + PopFont(); + + // Log as text + if (g.LogEnabled && !is_password) + LogRenderedText(render_pos, buf_display, NULL); + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) + return enter_pressed; + else + return value_changed; +} + +bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); +} + +bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) +{ + return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); +} + +// NB: scalar_format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "display_format" argument) +bool ImGui::InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + BeginGroup(); + PushID(label); + const ImVec2 button_sz = ImVec2(g.FontSize, g.FontSize) + style.FramePadding*2.0f; + if (step_ptr) + PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_sz.x + style.ItemInnerSpacing.x)*2)); + + char buf[64]; + DataTypeFormatString(data_type, data_ptr, scalar_format, buf, IM_ARRAYSIZE(buf)); + + bool value_changed = false; + if (!(extra_flags & ImGuiInputTextFlags_CharsHexadecimal)) + extra_flags |= ImGuiInputTextFlags_CharsDecimal; + extra_flags |= ImGuiInputTextFlags_AutoSelectAll; + if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view + value_changed = DataTypeApplyOpFromText(buf, GImGui->InputTextState.InitialText.begin(), data_type, data_ptr, scalar_format); + + // Step buttons + if (step_ptr) + { + PopItemWidth(); + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("-", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '-', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + value_changed = true; + } + SameLine(0, style.ItemInnerSpacing.x); + if (ButtonEx("+", button_sz, ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) + { + DataTypeApplyOp(data_type, '+', data_ptr, g.IO.KeyCtrl && step_fast_ptr ? step_fast_ptr : step_ptr); + value_changed = true; + } + } + PopID(); + + if (label_size.x > 0) + { + SameLine(0, style.ItemInnerSpacing.x); + RenderText(ImVec2(window->DC.CursorPos.x, window->DC.CursorPos.y + style.FramePadding.y), label); + ItemSize(label_size, style.FramePadding.y); + } + EndGroup(); + + return value_changed; +} + +bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + char display_format[16]; + if (decimal_precision < 0) + strcpy(display_format, "%f"); // Ideally we'd have a minimum decimal precision of 1 to visually denote that this is a float, while hiding non-significant digits? %f doesn't have a minimum of 1 + else + ImFormatString(display_format, IM_ARRAYSIZE(display_format), "%%.%df", decimal_precision); + return InputScalarEx(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), display_format, extra_flags); +} + +bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) +{ + // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. + const char* scalar_format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; + return InputScalarEx(label, ImGuiDataType_Int, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), scalar_format, extra_flags); +} + +bool ImGui::InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= InputFloat("##v", &v[i], 0, 0, decimal_precision, extra_flags); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 2, decimal_precision, extra_flags); +} + +bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 3, decimal_precision, extra_flags); +} + +bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) +{ + return InputFloatN(label, v, 4, decimal_precision, extra_flags); +} + +bool ImGui::InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + bool value_changed = false; + BeginGroup(); + PushID(label); + PushMultiItemsWidths(components); + for (int i = 0; i < components; i++) + { + PushID(i); + value_changed |= InputInt("##v", &v[i], 0, 0, extra_flags); + SameLine(0, g.Style.ItemInnerSpacing.x); + PopID(); + PopItemWidth(); + } + PopID(); + + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); + TextUnformatted(label, FindRenderedTextEnd(label)); + EndGroup(); + + return value_changed; +} + +bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 2, extra_flags); +} + +bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 3, extra_flags); +} + +bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) +{ + return InputIntN(label, v, 4, extra_flags); +} + +static bool Items_ArrayGetter(void* data, int idx, const char** out_text) +{ + const char** items = (const char**)data; + if (out_text) + *out_text = items[idx]; + return true; +} + +static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) +{ + // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. + const char* items_separated_by_zeros = (const char*)data; + int items_count = 0; + const char* p = items_separated_by_zeros; + while (*p) + { + if (idx == items_count) + break; + p += strlen(p) + 1; + items_count++; + } + if (!*p) + return false; + if (out_text) + *out_text = p; + return true; +} + +// Combo box helper allowing to pass an array of strings. +bool ImGui::Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items) +{ + const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); + return value_changed; +} + +// Combo box helper allowing to pass all items in a single string. +bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) +{ + int items_count = 0; + const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open + while (*p) + { + p += strlen(p) + 1; + items_count++; + } + bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); + return value_changed; +} + +// Combo box function. +bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w = CalcItemWidth(); + + const ImVec2 label_size = CalcTextSize(label, NULL, true); + const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); + const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + ItemSize(total_bb, style.FramePadding.y); + if (!ItemAdd(total_bb, &id)) + return false; + + const float arrow_size = (g.FontSize + style.FramePadding.x * 2.0f); + const bool hovered = IsHovered(frame_bb, id); + bool popup_open = IsPopupOpen(id); + bool popup_opened_now = false; + + const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); + RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); + RenderFrame(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32(popup_open || hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button), true, style.FrameRounding); // FIXME-ROUNDING + RenderCollapseTriangle(ImVec2(frame_bb.Max.x-arrow_size, frame_bb.Min.y) + style.FramePadding, true); + + if (*current_item >= 0 && *current_item < items_count) + { + const char* item_text; + if (items_getter(data, *current_item, &item_text)) + RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, item_text, NULL, NULL, ImVec2(0.0f,0.0f)); + } + + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + if (hovered) + { + SetHoveredID(id); + if (g.IO.MouseClicked[0]) + { + SetActiveID(0); + if (IsPopupOpen(id)) + { + ClosePopup(id); + } + else + { + FocusWindow(window); + OpenPopup(label); + popup_open = popup_opened_now = true; + } + } + } + + bool value_changed = false; + if (IsPopupOpen(id)) + { + // Size default to hold ~7 items + if (height_in_items < 0) + height_in_items = 7; + + float popup_height = (label_size.y + style.ItemSpacing.y) * ImMin(items_count, height_in_items) + (style.FramePadding.y * 3); + float popup_y1 = frame_bb.Max.y; + float popup_y2 = ImClamp(popup_y1 + popup_height, popup_y1, g.IO.DisplaySize.y - style.DisplaySafeAreaPadding.y); + if ((popup_y2 - popup_y1) < ImMin(popup_height, frame_bb.Min.y - style.DisplaySafeAreaPadding.y)) + { + // Position our combo ABOVE because there's more space to fit! (FIXME: Handle in Begin() or use a shared helper. We have similar code in Begin() for popup placement) + popup_y1 = ImClamp(frame_bb.Min.y - popup_height, style.DisplaySafeAreaPadding.y, frame_bb.Min.y); + popup_y2 = frame_bb.Min.y; + } + ImRect popup_rect(ImVec2(frame_bb.Min.x, popup_y1), ImVec2(frame_bb.Max.x, popup_y2)); + SetNextWindowPos(popup_rect.Min); + SetNextWindowSize(popup_rect.GetSize()); + PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); + + const ImGuiWindowFlags flags = ImGuiWindowFlags_ComboBox | ((window->Flags & ImGuiWindowFlags_ShowBorders) ? ImGuiWindowFlags_ShowBorders : 0); + if (BeginPopupEx(label, flags)) + { + // Display items + Spacing(); + for (int i = 0; i < items_count; i++) + { + PushID((void*)(intptr_t)i); + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + if (Selectable(item_text, item_selected)) + { + SetActiveID(0); + value_changed = true; + *current_item = i; + } + if (item_selected && popup_opened_now) + SetScrollHere(); + PopID(); + } + EndPopup(); + } + PopStyleVar(); + } + return value_changed; +} + +// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. +// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. +bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + PopClipRect(); + + ImGuiID id = window->GetID(label); + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); + ImVec2 pos = window->DC.CursorPos; + pos.y += window->DC.CurrentLineTextBaseOffset; + ImRect bb(pos, pos + size); + ItemSize(bb); + + // Fill horizontal space. + ImVec2 window_padding = window->WindowPadding; + float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; + float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); + ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); + ImRect bb_with_spacing(pos, pos + size_draw); + if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) + bb_with_spacing.Max.x += window_padding.x; + + // Selectables are tightly packed together, we extend the box to cover spacing between selectable. + float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); + float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); + float spacing_R = style.ItemSpacing.x - spacing_L; + float spacing_D = style.ItemSpacing.y - spacing_U; + bb_with_spacing.Min.x -= spacing_L; + bb_with_spacing.Min.y -= spacing_U; + bb_with_spacing.Max.x += spacing_R; + bb_with_spacing.Max.y += spacing_D; + if (!ItemAdd(bb_with_spacing, &id)) + { + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + PushColumnClipRect(); + return false; + } + + ImGuiButtonFlags button_flags = 0; + if (flags & ImGuiSelectableFlags_Menu) button_flags |= ImGuiButtonFlags_PressedOnClick; + if (flags & ImGuiSelectableFlags_MenuItem) button_flags |= ImGuiButtonFlags_PressedOnClick|ImGuiButtonFlags_PressedOnRelease; + if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; + if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; + bool hovered, held; + bool pressed = ButtonBehavior(bb_with_spacing, id, &hovered, &held, button_flags); + if (flags & ImGuiSelectableFlags_Disabled) + selected = false; + + // Render + if (hovered || selected) + { + const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); + RenderFrame(bb_with_spacing.Min, bb_with_spacing.Max, col, false, 0.0f); + } + + if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsCount > 1) + { + PushColumnClipRect(); + bb_with_spacing.Max.x -= (GetContentRegionMax().x - max_x); + } + + if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderTextClipped(bb.Min, bb_with_spacing.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); + if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); + + // Automatically close popups + if (pressed && !(flags & ImGuiSelectableFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) + CloseCurrentPopup(); + return pressed; +} + +bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) +{ + if (Selectable(label, *p_selected, flags, size_arg)) + { + *p_selected = !*p_selected; + return true; + } + return false; +} + +// Helper to calculate the size of a listbox and display a label on the right. +// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" +bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + const ImGuiStyle& style = GetStyle(); + const ImGuiID id = GetID(label); + const ImVec2 label_size = CalcTextSize(label, NULL, true); + + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); + ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); + ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); + ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); + window->DC.LastItemRect = bb; + + BeginGroup(); + if (label_size.x > 0) + RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); + + BeginChildFrame(id, frame_bb.GetSize()); + return true; +} + +bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) +{ + // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. + // However we don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. + // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. + if (height_in_items < 0) + height_in_items = ImMin(items_count, 7); + float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); + + // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). + ImVec2 size; + size.x = 0.0f; + size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; + return ListBoxHeader(label, size); +} + +void ImGui::ListBoxFooter() +{ + ImGuiWindow* parent_window = GetParentWindow(); + const ImRect bb = parent_window->DC.LastItemRect; + const ImGuiStyle& style = GetStyle(); + + EndChildFrame(); + + // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) + // We call SameLine() to restore DC.CurrentLine* data + SameLine(); + parent_window->DC.CursorPos = bb.Min; + ItemSize(bb, style.FramePadding.y); + EndGroup(); +} + +bool ImGui::ListBox(const char* label, int* current_item, const char** items, int items_count, int height_items) +{ + const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); + return value_changed; +} + +bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) +{ + if (!ListBoxHeader(label, items_count, height_in_items)) + return false; + + // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. + bool value_changed = false; + ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + { + const bool item_selected = (i == *current_item); + const char* item_text; + if (!items_getter(data, i, &item_text)) + item_text = "*Unknown item*"; + + PushID(i); + if (Selectable(item_text, item_selected)) + { + *current_item = i; + value_changed = true; + } + PopID(); + } + ListBoxFooter(); + return value_changed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + ImVec2 pos = window->DC.CursorPos; + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); + float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + + bool pressed = Selectable(label, false, ImGuiSelectableFlags_MenuItem | ImGuiSelectableFlags_DrawFillAvailWidth | (enabled ? 0 : ImGuiSelectableFlags_Disabled), ImVec2(w, 0.0f)); + if (shortcut_size.x > 0.0f) + { + PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + + if (selected) + RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled)); + + return pressed; +} + +bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) +{ + if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) + { + if (p_selected) + *p_selected = !*p_selected; + return true; + } + return false; +} + +bool ImGui::BeginMainMenuBar() +{ + ImGuiContext& g = *GImGui; + SetNextWindowPos(ImVec2(0.0f, 0.0f)); + SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.FontBaseSize + g.Style.FramePadding.y * 2.0f)); + PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); + if (!Begin("##MainMenuBar", NULL, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_MenuBar) + || !BeginMenuBar()) + { + End(); + PopStyleVar(2); + return false; + } + g.CurrentWindow->DC.MenuBarOffsetX += g.Style.DisplaySafeAreaPadding.x; + return true; +} + +void ImGui::EndMainMenuBar() +{ + EndMenuBar(); + End(); + PopStyleVar(2); +} + +bool ImGui::BeginMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + if (!(window->Flags & ImGuiWindowFlags_MenuBar)) + return false; + + IM_ASSERT(!window->DC.MenuBarAppending); + BeginGroup(); // Save position + PushID("##menubar"); + ImRect rect = window->MenuBarRect(); + PushClipRect(ImVec2(ImFloor(rect.Min.x+0.5f), ImFloor(rect.Min.y + window->BorderSize + 0.5f)), ImVec2(ImFloor(rect.Max.x+0.5f), ImFloor(rect.Max.y+0.5f)), false); + window->DC.CursorPos = ImVec2(rect.Min.x + window->DC.MenuBarOffsetX, rect.Min.y);// + g.Style.FramePadding.y); + window->DC.LayoutType = ImGuiLayoutType_Horizontal; + window->DC.MenuBarAppending = true; + AlignFirstTextHeightToWidgets(); + return true; +} + +void ImGui::EndMenuBar() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); + IM_ASSERT(window->DC.MenuBarAppending); + PopClipRect(); + PopID(); + window->DC.MenuBarOffsetX = window->DC.CursorPos.x - window->MenuBarRect().Min.x; + window->DC.GroupStack.back().AdvanceCursor = false; + EndGroup(); + window->DC.LayoutType = ImGuiLayoutType_Vertical; + window->DC.MenuBarAppending = false; +} + +bool ImGui::BeginMenu(const char* label, bool enabled) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + + ImVec2 label_size = CalcTextSize(label, NULL, true); + ImGuiWindow* backed_focused_window = g.FocusedWindow; + + bool pressed; + bool menu_is_open = IsPopupOpen(id); + bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentMenuSet == window->GetID("##menus")); + if (menuset_is_open) + g.FocusedWindow = window; + + ImVec2 popup_pos, pos = window->DC.CursorPos; + if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) + { + popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); + float w = label_size.x; + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + PopStyleVar(); + SameLine(); + window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); + } + else + { + popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); + float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame + float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); + pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_Menu | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); + if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); + RenderCollapseTriangle(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.20f, 0.0f), false); + if (!enabled) PopStyleColor(); + } + + bool hovered = enabled && IsHovered(window->DC.LastItemRect, id); + if (menuset_is_open) + g.FocusedWindow = backed_focused_window; + + bool want_open = false, want_close = false; + if (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) + { + // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. + bool moving_within_opened_triangle = false; + if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window) + { + if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) + { + ImRect next_window_rect = next_window->Rect(); + ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; + ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + float extra = ImClamp(fabsf(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. + ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues + tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? + tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); + moving_within_opened_triangle = ImIsPointInTriangle(g.IO.MousePos, ta, tb, tc); + //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug + } + } + + want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); + want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); + } + else if (menu_is_open && pressed && menuset_is_open) // menu-bar: click open menu to close + { + want_close = true; + want_open = menu_is_open = false; + } + else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // menu-bar: first click to open, then hover to open others + want_open = true; + if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' + want_close = true; + if (want_close && IsPopupOpen(id)) + ClosePopupToLevel(GImGui->CurrentPopupStack.Size); + + if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) + { + // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + OpenPopup(label); + return false; + } + + menu_is_open |= want_open; + if (want_open) + OpenPopup(label); + + if (menu_is_open) + { + SetNextWindowPos(popup_pos, ImGuiSetCond_Always); + ImGuiWindowFlags flags = ImGuiWindowFlags_ShowBorders | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); + menu_is_open = BeginPopupEx(label, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + } + + return menu_is_open; +} + +void ImGui::EndMenu() +{ + EndPopup(); +} + +// A little colored square. Return true when clicked. +// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. +bool ImGui::ColorButton(const ImVec4& col, bool small_height, bool outline_border) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID("#colorbutton"); + const float square_size = g.FontSize; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(square_size + style.FramePadding.y*2, square_size + (small_height ? 0 : style.FramePadding.y*2))); + ItemSize(bb, small_height ? 0.0f : style.FramePadding.y); + if (!ItemAdd(bb, &id)) + return false; + + bool hovered, held; + bool pressed = ButtonBehavior(bb, id, &hovered, &held); + RenderFrame(bb.Min, bb.Max, GetColorU32(col), outline_border, style.FrameRounding); + + if (hovered) + SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col.x, col.y, col.z, col.w, IM_F32_TO_INT8_SAT(col.x), IM_F32_TO_INT8_SAT(col.y), IM_F32_TO_INT8_SAT(col.z), IM_F32_TO_INT8_SAT(col.z)); + + return pressed; +} + +bool ImGui::ColorEdit3(const char* label, float col[3]) +{ + float col4[4]; + col4[0] = col[0]; + col4[1] = col[1]; + col4[2] = col[2]; + col4[3] = 1.0f; + const bool value_changed = ColorEdit4(label, col4, false); + col[0] = col4[0]; + col[1] = col4[1]; + col[2] = col4[2]; + return value_changed; +} + +// Edit colors components (each component in 0.0f..1.0f range +// Use CTRL-Click to input value and TAB to go to next item. +bool ImGui::ColorEdit4(const char* label, float col[4], bool alpha) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return false; + + ImGuiContext& g = *GImGui; + const ImGuiStyle& style = g.Style; + const ImGuiID id = window->GetID(label); + const float w_full = CalcItemWidth(); + const float square_sz = (g.FontSize + style.FramePadding.y * 2.0f); + + ImGuiColorEditMode edit_mode = window->DC.ColorEditMode; + if (edit_mode == ImGuiColorEditMode_UserSelect || edit_mode == ImGuiColorEditMode_UserSelectShowButton) + edit_mode = g.ColorEditModeStorage.GetInt(id, 0) % 3; + + float f[4] = { col[0], col[1], col[2], col[3] }; + if (edit_mode == ImGuiColorEditMode_HSV) + ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); + + int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; + + int components = alpha ? 4 : 3; + bool value_changed = false; + + BeginGroup(); + PushID(label); + + const bool hsv = (edit_mode == 1); + switch (edit_mode) + { + case ImGuiColorEditMode_RGB: + case ImGuiColorEditMode_HSV: + { + // RGB/HSV 0..255 Sliders + const float w_items_all = w_full - (square_sz + style.ItemInnerSpacing.x); + const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); + const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); + + const bool hide_prefix = (w_item_one <= CalcTextSize("M:999").x); + const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; + const char* fmt_table[3][4] = + { + { "%3.0f", "%3.0f", "%3.0f", "%3.0f" }, + { "R:%3.0f", "G:%3.0f", "B:%3.0f", "A:%3.0f" }, + { "H:%3.0f", "S:%3.0f", "V:%3.0f", "A:%3.0f" } + }; + const char** fmt = hide_prefix ? fmt_table[0] : hsv ? fmt_table[2] : fmt_table[1]; + + PushItemWidth(w_item_one); + for (int n = 0; n < components; n++) + { + if (n > 0) + SameLine(0, style.ItemInnerSpacing.x); + if (n + 1 == components) + PushItemWidth(w_item_last); + value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, 255, fmt[n]); + } + PopItemWidth(); + PopItemWidth(); + } + break; + case ImGuiColorEditMode_HEX: + { + // RGB Hexadecimal Input + const float w_slider_all = w_full - square_sz; + char buf[64]; + if (alpha) + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", i[0], i[1], i[2], i[3]); + else + ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", i[0], i[1], i[2]); + PushItemWidth(w_slider_all - style.ItemInnerSpacing.x); + if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) + { + value_changed |= true; + char* p = buf; + while (*p == '#' || ImCharIsSpace(*p)) + p++; + i[0] = i[1] = i[2] = i[3] = 0; + if (alpha) + sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) + else + sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); + } + PopItemWidth(); + } + break; + } + + SameLine(0, style.ItemInnerSpacing.x); + + const ImVec4 col_display(col[0], col[1], col[2], 1.0f); + if (ColorButton(col_display)) + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + + // Recreate our own tooltip over's ColorButton() one because we want to display correct alpha here + if (IsItemHovered()) + SetTooltip("Color:\n(%.2f,%.2f,%.2f,%.2f)\n#%02X%02X%02X%02X", col[0], col[1], col[2], col[3], IM_F32_TO_INT8_SAT(col[0]), IM_F32_TO_INT8_SAT(col[1]), IM_F32_TO_INT8_SAT(col[2]), IM_F32_TO_INT8_SAT(col[3])); + + if (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) + { + SameLine(0, style.ItemInnerSpacing.x); + const char* button_titles[3] = { "RGB", "HSV", "HEX" }; + if (ButtonEx(button_titles[edit_mode], ImVec2(0,0), ImGuiButtonFlags_DontClosePopups)) + g.ColorEditModeStorage.SetInt(id, (edit_mode + 1) % 3); // Don't set local copy of 'edit_mode' right away! + } + + const char* label_display_end = FindRenderedTextEnd(label); + if (label != label_display_end) + { + SameLine(0, (window->DC.ColorEditMode == ImGuiColorEditMode_UserSelectShowButton) ? -1.0f : style.ItemInnerSpacing.x); + TextUnformatted(label, label_display_end); + } + + // Convert back + for (int n = 0; n < 4; n++) + f[n] = i[n] / 255.0f; + if (edit_mode == 1) + ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); + + if (value_changed) + { + col[0] = f[0]; + col[1] = f[1]; + col[2] = f[2]; + if (alpha) + col[3] = f[3]; + } + + PopID(); + EndGroup(); + + return value_changed; +} + +void ImGui::ColorEditMode(ImGuiColorEditMode mode) +{ + ImGuiWindow* window = GetCurrentWindow(); + window->DC.ColorEditMode = mode; +} + +// Horizontal separating line. +void ImGui::Separator() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + if (window->DC.ColumnsCount > 1) + PopClipRect(); + + float x1 = window->Pos.x; + float x2 = window->Pos.x + window->Size.x; + if (!window->DC.GroupStack.empty()) + x1 += window->DC.IndentX; + + const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); + ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. + if (!ItemAdd(bb, NULL)) + { + if (window->DC.ColumnsCount > 1) + PushColumnClipRect(); + return; + } + + window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Border)); + + ImGuiContext& g = *GImGui; + if (g.LogEnabled) + LogText(IM_NEWLINE "--------------------------------"); + + if (window->DC.ColumnsCount > 1) + { + PushColumnClipRect(); + window->DC.ColumnsCellMinY = window->DC.CursorPos.y; + } +} + +void ImGui::Spacing() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + ItemSize(ImVec2(0,0)); +} + +void ImGui::Dummy(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); + ItemSize(bb); + ItemAdd(bb, NULL); +} + +bool ImGui::IsRectVisible(const ImVec2& size) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); +} + +bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); +} + +// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) +void ImGui::BeginGroup() +{ + ImGuiWindow* window = GetCurrentWindow(); + + window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + group_data.BackupCursorPos = window->DC.CursorPos; + group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; + group_data.BackupIndentX = window->DC.IndentX; + group_data.BackupGroupOffsetX = window->DC.GroupOffsetX; + group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; + group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; + group_data.BackupLogLinePosY = window->DC.LogLinePosY; + group_data.BackupActiveIdIsAlive = GImGui->ActiveIdIsAlive; + group_data.AdvanceCursor = true; + + window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX; + window->DC.IndentX = window->DC.GroupOffsetX; + window->DC.CursorMaxPos = window->DC.CursorPos; + window->DC.CurrentLineHeight = 0.0f; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; +} + +void ImGui::EndGroup() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + + IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls + + ImGuiGroupData& group_data = window->DC.GroupStack.back(); + + ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); + group_bb.Max.y -= g.Style.ItemSpacing.y; // Cancel out last vertical spacing because we are adding one ourselves. + group_bb.Max = ImMax(group_bb.Min, group_bb.Max); + + window->DC.CursorPos = group_data.BackupCursorPos; + window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); + window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; + window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; + window->DC.IndentX = group_data.BackupIndentX; + window->DC.GroupOffsetX = group_data.BackupGroupOffsetX; + window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; + + if (group_data.AdvanceCursor) + { + window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. + ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); + ItemAdd(group_bb, NULL); + } + + // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive() will function on the entire group. + // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but if you search for LastItemId you'll notice it is only used in that context. + const bool active_id_within_group = (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId && g.ActiveIdWindow->RootWindow == window->RootWindow); + if (active_id_within_group) + window->DC.LastItemId = g.ActiveId; + if (active_id_within_group && g.HoveredId == g.ActiveId) + window->DC.LastItemHoveredAndUsable = window->DC.LastItemHoveredRect = true; + + window->DC.GroupStack.pop_back(); + + //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // Debug +} + +// Gets back to previous line and continue with horizontal layout +// pos_x == 0 : follow right after previous item +// pos_x != 0 : align to specified x position (relative to window/group left) +// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 +// spacing_w >= 0 : enforce spacing amount +void ImGui::SameLine(float pos_x, float spacing_w) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + + ImGuiContext& g = *GImGui; + if (pos_x != 0.0f) + { + if (spacing_w < 0.0f) spacing_w = 0.0f; + window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX; + window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; + } + else + { + if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; + window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; + window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; + } + window->DC.CurrentLineHeight = window->DC.PrevLineHeight; + window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; +} + +void ImGui::NewLine() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems) + return; + if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. + ItemSize(ImVec2(0,0)); + else + ItemSize(ImVec2(0.0f, GImGui->FontSize)); +} + +void ImGui::NextColumn() +{ + ImGuiWindow* window = GetCurrentWindow(); + if (window->SkipItems || window->DC.ColumnsCount <= 1) + return; + + ImGuiContext& g = *GImGui; + PopItemWidth(); + PopClipRect(); + + window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); + if (++window->DC.ColumnsCurrent < window->DC.ColumnsCount) + { + // Columns 1+ cancel out IndentX + window->DC.ColumnsOffsetX = GetColumnOffset(window->DC.ColumnsCurrent) - window->DC.IndentX + g.Style.ItemSpacing.x; + window->DrawList->ChannelsSetCurrent(window->DC.ColumnsCurrent); + } + else + { + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY; + window->DrawList->ChannelsSetCurrent(0); + } + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + window->DC.CursorPos.y = window->DC.ColumnsCellMinY; + window->DC.CurrentLineHeight = 0.0f; + window->DC.CurrentLineTextBaseOffset = 0.0f; + + PushColumnClipRect(); + PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup +} + +int ImGui::GetColumnIndex() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.ColumnsCurrent; +} + +int ImGui::GetColumnsCount() +{ + ImGuiWindow* window = GetCurrentWindowRead(); + return window->DC.ColumnsCount; +} + +static float GetDraggedColumnOffset(int column_index) +{ + // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing + // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. + ImGuiContext& g = *GImGui; + ImGuiWindow* window = ImGui::GetCurrentWindowRead(); + IM_ASSERT(column_index > 0); // We cannot drag column 0. If you get this assert you may have a conflict between the ID of your columns and another widgets. + IM_ASSERT(g.ActiveId == window->DC.ColumnsSetId + ImGuiID(column_index)); + + float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x - window->Pos.x; + x = ImClamp(x, ImGui::GetColumnOffset(column_index-1)+g.Style.ColumnsMinSpacing, ImGui::GetColumnOffset(column_index+1)-g.Style.ColumnsMinSpacing); + + return (float)(int)x; +} + +float ImGui::GetColumnOffset(int column_index) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindowRead(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + if (g.ActiveId) + { + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + if (g.ActiveId == column_id) + return GetDraggedColumnOffset(column_index); + } + + IM_ASSERT(column_index < window->DC.ColumnsData.Size); + const float t = window->DC.ColumnsData[column_index].OffsetNorm; + const float x_offset = window->DC.ColumnsMinX + t * (window->DC.ColumnsMaxX - window->DC.ColumnsMinX); + return (float)(int)x_offset; +} + +void ImGui::SetColumnOffset(int column_index, float offset) +{ + ImGuiWindow* window = GetCurrentWindow(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + IM_ASSERT(column_index < window->DC.ColumnsData.Size); + const float t = (offset - window->DC.ColumnsMinX) / (window->DC.ColumnsMaxX - window->DC.ColumnsMinX); + window->DC.ColumnsData[column_index].OffsetNorm = t; + + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + window->DC.StateStorage->SetFloat(column_id, t); +} + +float ImGui::GetColumnWidth(int column_index) +{ + ImGuiWindow* window = GetCurrentWindowRead(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + float w = GetColumnOffset(column_index+1) - GetColumnOffset(column_index); + return w; +} + +static void PushColumnClipRect(int column_index) +{ + ImGuiWindow* window = ImGui::GetCurrentWindow(); + if (column_index < 0) + column_index = window->DC.ColumnsCurrent; + + float x1 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index) - 1.0f); + float x2 = ImFloor(0.5f + window->Pos.x + ImGui::GetColumnOffset(column_index+1) - 1.0f); + ImGui::PushClipRect(ImVec2(x1,-FLT_MAX), ImVec2(x2,+FLT_MAX), true); +} + +void ImGui::Columns(int columns_count, const char* id, bool border) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + IM_ASSERT(columns_count >= 1); + + if (window->DC.ColumnsCount != 1) + { + if (window->DC.ColumnsCurrent != 0) + ItemSize(ImVec2(0,0)); // Advance to column 0 + PopItemWidth(); + PopClipRect(); + window->DrawList->ChannelsMerge(); + + window->DC.ColumnsCellMaxY = ImMax(window->DC.ColumnsCellMaxY, window->DC.CursorPos.y); + window->DC.CursorPos.y = window->DC.ColumnsCellMaxY; + } + + // Draw columns borders and handle resize at the time of "closing" a columns set + if (window->DC.ColumnsCount != columns_count && window->DC.ColumnsCount != 1 && window->DC.ColumnsShowBorders && !window->SkipItems) + { + const float y1 = window->DC.ColumnsStartPosY; + const float y2 = window->DC.CursorPos.y; + for (int i = 1; i < window->DC.ColumnsCount; i++) + { + float x = window->Pos.x + GetColumnOffset(i); + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(i); + const ImRect column_rect(ImVec2(x-4,y1),ImVec2(x+4,y2)); + if (IsClippedEx(column_rect, &column_id, false)) + continue; + + bool hovered, held; + ButtonBehavior(column_rect, column_id, &hovered, &held); + if (hovered || held) + g.MouseCursor = ImGuiMouseCursor_ResizeEW; + + // Draw before resize so our items positioning are in sync with the line being drawn + const ImU32 col = GetColorU32(held ? ImGuiCol_ColumnActive : hovered ? ImGuiCol_ColumnHovered : ImGuiCol_Column); + const float xi = (float)(int)x; + window->DrawList->AddLine(ImVec2(xi, y1+1.0f), ImVec2(xi, y2), col); + + if (held) + { + if (g.ActiveIdIsJustActivated) + g.ActiveIdClickOffset.x -= 4; // Store from center of column line (we used a 8 wide rect for columns clicking) + x = GetDraggedColumnOffset(i); + SetColumnOffset(i, x); + } + } + } + + // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. + // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. + PushID(0x11223347 + (id ? 0 : columns_count)); + window->DC.ColumnsSetId = window->GetID(id ? id : "columns"); + PopID(); + + // Set state for first column + window->DC.ColumnsCurrent = 0; + window->DC.ColumnsCount = columns_count; + window->DC.ColumnsShowBorders = border; + + const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : window->Size.x; + window->DC.ColumnsMinX = window->DC.IndentX; // Lock our horizontal range + window->DC.ColumnsMaxX = content_region_width - window->Scroll.x - ((window->Flags & ImGuiWindowFlags_NoScrollbar) ? 0 : g.Style.ScrollbarSize);// - window->WindowPadding().x; + window->DC.ColumnsStartPosY = window->DC.CursorPos.y; + window->DC.ColumnsCellMinY = window->DC.ColumnsCellMaxY = window->DC.CursorPos.y; + window->DC.ColumnsOffsetX = 0.0f; + window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); + + if (window->DC.ColumnsCount != 1) + { + // Cache column offsets + window->DC.ColumnsData.resize(columns_count + 1); + for (int column_index = 0; column_index < columns_count + 1; column_index++) + { + const ImGuiID column_id = window->DC.ColumnsSetId + ImGuiID(column_index); + KeepAliveID(column_id); + const float default_t = column_index / (float)window->DC.ColumnsCount; + const float t = window->DC.StateStorage->GetFloat(column_id, default_t); // Cheaply store our floating point value inside the integer (could store a union into the map?) + window->DC.ColumnsData[column_index].OffsetNorm = t; + } + window->DrawList->ChannelsSplit(window->DC.ColumnsCount); + PushColumnClipRect(); + PushItemWidth(GetColumnWidth() * 0.65f); + } + else + { + window->DC.ColumnsData.resize(0); + } +} + +void ImGui::Indent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.IndentX += (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; +} + +void ImGui::Unindent(float indent_w) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = GetCurrentWindow(); + window->DC.IndentX -= (indent_w > 0.0f) ? indent_w : g.Style.IndentSpacing; + window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; +} + +void ImGui::TreePush(const char* str_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(str_id ? str_id : "#TreePush"); +} + +void ImGui::TreePush(const void* ptr_id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); +} + +void ImGui::TreePushRawID(ImGuiID id) +{ + ImGuiWindow* window = GetCurrentWindow(); + Indent(); + window->DC.TreeDepth++; + window->IDStack.push_back(id); +} + +void ImGui::TreePop() +{ + ImGuiWindow* window = GetCurrentWindow(); + Unindent(); + window->DC.TreeDepth--; + PopID(); +} + +void ImGui::Value(const char* prefix, bool b) +{ + Text("%s: %s", prefix, (b ? "true" : "false")); +} + +void ImGui::Value(const char* prefix, int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, unsigned int v) +{ + Text("%s: %d", prefix, v); +} + +void ImGui::Value(const char* prefix, float v, const char* float_format) +{ + if (float_format) + { + char fmt[64]; + ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); + Text(fmt, prefix, v); + } + else + { + Text("%s: %.3f", prefix, v); + } +} + +// FIXME: May want to remove those helpers? +void ImGui::ValueColor(const char* prefix, const ImVec4& v) +{ + Text("%s: (%.2f,%.2f,%.2f,%.2f)", prefix, v.x, v.y, v.z, v.w); + SameLine(); + ColorButton(v, true); +} + +void ImGui::ValueColor(const char* prefix, ImU32 v) +{ + Text("%s: %08X", prefix, v); + SameLine(); + ColorButton(ColorConvertU32ToFloat4(v), true); +} + +//----------------------------------------------------------------------------- +// PLATFORM DEPENDENT HELPERS +//----------------------------------------------------------------------------- + +#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS)) +#undef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#include +#endif + +// Win32 API clipboard implementation +#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS) + +#ifdef _MSC_VER +#pragma comment(lib, "user32") +#endif + +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + static ImVector buf_local; + buf_local.clear(); + if (!OpenClipboard(NULL)) + return NULL; + HANDLE wbuf_handle = GetClipboardData(CF_UNICODETEXT); + if (wbuf_handle == NULL) + return NULL; + if (ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle)) + { + int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; + buf_local.resize(buf_len); + ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL); + } + GlobalUnlock(wbuf_handle); + CloseClipboard(); + return buf_local.Data; +} + +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + if (!OpenClipboard(NULL)) + return; + const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; + HGLOBAL wbuf_handle = GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); + if (wbuf_handle == NULL) + return; + ImWchar* wbuf_global = (ImWchar*)GlobalLock(wbuf_handle); + ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); + GlobalUnlock(wbuf_handle); + EmptyClipboard(); + SetClipboardData(CF_UNICODETEXT, wbuf_handle); + CloseClipboard(); +} + +#else + +// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers +static const char* GetClipboardTextFn_DefaultImpl(void*) +{ + return GImGui->PrivateClipboard; +} + +// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers +static void SetClipboardTextFn_DefaultImpl(void*, const char* text) +{ + ImGuiContext& g = *GImGui; + if (g.PrivateClipboard) + { + ImGui::MemFree(g.PrivateClipboard); + g.PrivateClipboard = NULL; + } + const char* text_end = text + strlen(text); + g.PrivateClipboard = (char*)ImGui::MemAlloc((size_t)(text_end - text) + 1); + memcpy(g.PrivateClipboard, text, (size_t)(text_end - text)); + g.PrivateClipboard[(int)(text_end - text)] = 0; +} + +#endif + +// Win32 API IME support (for Asian languages, etc.) +#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS) + +#include +#ifdef _MSC_VER +#pragma comment(lib, "imm32") +#endif + +static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) +{ + // Notify OS Input Method Editor of text input position + if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) + if (HIMC himc = ImmGetContext(hwnd)) + { + COMPOSITIONFORM cf; + cf.ptCurrentPos.x = x; + cf.ptCurrentPos.y = y; + cf.dwStyle = CFS_FORCE_POSITION; + ImmSetCompositionWindow(himc, &cf); + } +} + +#else + +static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} + +#endif + +//----------------------------------------------------------------------------- +// HELP +//----------------------------------------------------------------------------- + +void ImGui::ShowMetricsWindow(bool* p_open) +{ + if (ImGui::Begin("ImGui Metrics", p_open)) + { + ImGui::Text("ImGui %s", ImGui::GetVersion()); + ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); + ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); + ImGui::Text("%d allocations", ImGui::GetIO().MetricsAllocs); + static bool show_clip_rects = true; + ImGui::Checkbox("Show clipping rectangles when hovering a ImDrawCmd", &show_clip_rects); + ImGui::Separator(); + + struct Funcs + { + static void NodeDrawList(ImDrawList* draw_list, const char* label) + { + bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); + if (draw_list == ImGui::GetWindowDrawList()) + { + ImGui::SameLine(); + ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) + if (node_open) ImGui::TreePop(); + return; + } + if (!node_open) + return; + + ImDrawList* overlay_draw_list = &GImGui->OverlayDrawList; // Render additional visuals into the top-most draw list + overlay_draw_list->PushClipRectFullScreen(); + int elem_offset = 0; + for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) + { + if (pcmd->UserCallback) + { + ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); + continue; + } + ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; + bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %-4d %s vtx, tex = %p, clip_rect = (%.0f,%.0f)..(%.0f,%.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); + if (show_clip_rects && ImGui::IsItemHovered()) + { + ImRect clip_rect = pcmd->ClipRect; + ImRect vtxs_rect; + for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) + vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); + clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); + vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); + } + if (!pcmd_node_open) + continue; + ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. + while (clipper.Step()) + for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) + { + char buf[300], *buf_p = buf; + ImVec2 triangles_pos[3]; + for (int n = 0; n < 3; n++, vtx_i++) + { + ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; + triangles_pos[n] = v.pos; + buf_p += sprintf(buf_p, "%s %04d { pos = (%8.2f,%8.2f), uv = (%.6f,%.6f), col = %08X }\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); + } + ImGui::Selectable(buf, false); + if (ImGui::IsItemHovered()) + overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f, false); // Add triangle without AA, more readable for large-thin triangle + } + ImGui::TreePop(); + } + overlay_draw_list->PopClipRect(); + ImGui::TreePop(); + } + + static void NodeWindows(ImVector& windows, const char* label) + { + if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) + return; + for (int i = 0; i < windows.Size; i++) + Funcs::NodeWindow(windows[i], "Window"); + ImGui::TreePop(); + } + + static void NodeWindow(ImGuiWindow* window, const char* label) + { + if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) + return; + NodeDrawList(window->DrawList, "DrawList"); + ImGui::BulletText("Pos: (%.1f,%.1f)", window->Pos.x, window->Pos.y); + ImGui::BulletText("Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); + ImGui::BulletText("Scroll: (%.2f,%.2f)", window->Scroll.x, window->Scroll.y); + if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); + if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); + ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); + ImGui::TreePop(); + } + }; + + ImGuiContext& g = *GImGui; // Access private state + Funcs::NodeWindows(g.Windows, "Windows"); + if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.RenderDrawLists[0].Size)) + { + for (int i = 0; i < g.RenderDrawLists[0].Size; i++) + Funcs::NodeDrawList(g.RenderDrawLists[0][i], "DrawList"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) + { + for (int i = 0; i < g.OpenPopupStack.Size; i++) + { + ImGuiWindow* window = g.OpenPopupStack[i].Window; + ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("Basic state")) + { + ImGui::Text("FocusedWindow: '%s'", g.FocusedWindow ? g.FocusedWindow->Name : "NULL"); + ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); + ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); + ImGui::Text("HoveredID: 0x%08X/0x%08X", g.HoveredId, g.HoveredIdPreviousFrame); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not + ImGui::Text("ActiveID: 0x%08X/0x%08X", g.ActiveId, g.ActiveIdPreviousFrame); + ImGui::TreePop(); + } + } + ImGui::End(); +} + +//----------------------------------------------------------------------------- + +// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. +// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github. +#ifdef IMGUI_INCLUDE_IMGUI_USER_INL +#include "imgui_user.inl" +#endif + +//----------------------------------------------------------------------------- diff --git a/apps/exampleViewer/common/imgui/imgui.h b/apps/exampleViewer/common/imgui/imgui.h new file mode 100644 index 0000000000..33da604c9f --- /dev/null +++ b/apps/exampleViewer/common/imgui/imgui.h @@ -0,0 +1,1401 @@ +// dear imgui, v1.50 WIP +// (headers) + +// See imgui.cpp file for documentation. +// See ImGui::ShowTestWindow() in imgui_demo.cpp for demo code. +// Read 'Programmer guide' in imgui.cpp for notes on how to setup ImGui in your codebase. +// Get latest version at https://github.com/ocornut/imgui + +#pragma once + +#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H) +#include "imconfig.h" // User-editable configuration file +#endif +#include // FLT_MAX +#include // va_list +#include // ptrdiff_t, NULL +#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp + +#define IMGUI_VERSION "1.50 WIP" + +// Define attributes of all API symbols declarations, e.g. for DLL under Windows. +#ifndef IMGUI_API +#define IMGUI_API +#endif + + + +// Define assertion handler. +#ifndef IM_ASSERT +#include +#define IM_ASSERT(_EXPR) assert(_EXPR) +#endif + +// Some compilers support applying printf-style warnings to user functions. +#if defined(__clang__) || defined(__GNUC__) +#define IM_PRINTFARGS(FMT) __attribute__((format(printf, FMT, (FMT+1)))) +#else +#define IM_PRINTFARGS(FMT) +#endif + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + +// Forward declarations +struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() +struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) +struct ImDrawData; // All draw command lists required to render the frame +struct ImDrawList; // A single draw command list (generally one per window) +struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) +struct ImFont; // Runtime data for a single font within a parent ImFontAtlas +struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF font loader +struct ImFontConfig; // Configuration data when adding a font or merging fonts +struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 +struct ImGuiIO; // Main configuration and I/O between your application and ImGui +struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro +struct ImGuiStorage; // Simple custom key value storage +struct ImGuiStyle; // Runtime data for styling/colors +struct ImGuiTextFilter; // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +struct ImGuiTextBuffer; // Text buffer for logging/accumulating text +struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) +struct ImGuiSizeConstraintCallbackData;// Structure used to constraint window size in custom ways when using custom ImGuiSizeConstraintCallback (rare/advanced use) +struct ImGuiListClipper; // Helper to manually clip large list of items +struct ImGuiContext; // ImGui context (opaque) + +// Typedefs and Enumerations (declared as int for compatibility and to not pollute the top of this file) +typedef unsigned int ImU32; // 32-bit unsigned integer (typically used to store packed colors) +typedef unsigned int ImGuiID; // unique ID used by widgets (typically hashed from a stack of string) +typedef unsigned short ImWchar; // character for keyboard input/display +typedef void* ImTextureID; // user data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) +typedef int ImGuiCol; // a color identifier for styling // enum ImGuiCol_ +typedef int ImGuiStyleVar; // a variable identifier for styling // enum ImGuiStyleVar_ +typedef int ImGuiKey; // a key identifier (ImGui-side enum) // enum ImGuiKey_ +typedef int ImGuiColorEditMode; // color edit mode for ColorEdit*() // enum ImGuiColorEditMode_ +typedef int ImGuiMouseCursor; // a mouse cursor identifier // enum ImGuiMouseCursor_ +typedef int ImGuiWindowFlags; // window flags for Begin*() // enum ImGuiWindowFlags_ +typedef int ImGuiSetCond; // condition flags for Set*() // enum ImGuiSetCond_ +typedef int ImGuiInputTextFlags; // flags for InputText*() // enum ImGuiInputTextFlags_ +typedef int ImGuiSelectableFlags; // flags for Selectable() // enum ImGuiSelectableFlags_ +typedef int ImGuiTreeNodeFlags; // flags for TreeNode*(), Collapsing*() // enum ImGuiTreeNodeFlags_ +typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); +typedef void (*ImGuiSizeConstraintCallback)(ImGuiSizeConstraintCallbackData* data); + +// Others helpers at bottom of the file: +// class ImVector<> // Lightweight std::vector like class. +// IMGUI_ONCE_UPON_A_FRAME // Execute a block of code once per frame only (convenient for creating UI within deep-nested code that runs multiple times) + +struct ImVec2 +{ + float x, y; + ImVec2() { x = y = 0.0f; } + ImVec2(float _x, float _y) { x = _x; y = _y; } +#ifdef IM_VEC2_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec2. + IM_VEC2_CLASS_EXTRA +#endif +}; + +struct ImVec4 +{ + float x, y, z, w; + ImVec4() { x = y = z = w = 0.0f; } + ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } +#ifdef IM_VEC4_CLASS_EXTRA // Define constructor and implicit cast operators in imconfig.h to convert back<>forth from your math types and ImVec4. + IM_VEC4_CLASS_EXTRA +#endif +}; + +// ImGui end-user API +// In a namespace so that user can add extra functions in a separate file (e.g. Value() helpers for your vector or common types) +namespace ImGui +{ + // Main + IMGUI_API ImGuiIO& GetIO(); + IMGUI_API ImGuiStyle& GetStyle(); + IMGUI_API ImDrawData* GetDrawData(); // same value as passed to your io.RenderDrawListsFn() function. valid after Render() and until the next call to NewFrame() + IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until NewFrame()/Render(). + IMGUI_API void Render(); // ends the ImGui frame, finalize rendering data, then call your io.RenderDrawListsFn() function if set. + IMGUI_API void Shutdown(); + IMGUI_API void ShowUserGuide(); // help block + IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // style editor block. you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) + IMGUI_API void ShowTestWindow(bool* p_open = NULL); // test window demonstrating ImGui features + IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // metrics window for debugging ImGui + + // Window + IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // push window to the stack and start appending to it. see .cpp for details. return false when window is collapsed, so you can early out in your code. 'bool* p_open' creates a widget on the upper-right to close the window (which sets your bool to false). + IMGUI_API bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags = 0); // OBSOLETE. this is the older/longer API. the extra parameters aren't very relevant. call SetNextWindowSize() instead if you want to set a window size. For regular windows, 'size_on_first_use' only applies to the first time EVER the window is created and probably not what you want! might obsolete this API eventually. + IMGUI_API void End(); // finish appending to current window, pop it off the window stack. + IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). + IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags extra_flags = 0); // " + IMGUI_API void EndChild(); + IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates + IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() + IMGUI_API float GetContentRegionAvailWidth(); // + IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates + IMGUI_API float GetWindowContentRegionWidth(); // + IMGUI_API ImDrawList* GetWindowDrawList(); // get rendering command-list if you want to append your own draw primitives + IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList api) + IMGUI_API ImVec2 GetWindowSize(); // get current window size + IMGUI_API float GetWindowWidth(); + IMGUI_API float GetWindowHeight(); + IMGUI_API bool IsWindowCollapsed(); + IMGUI_API void SetWindowFontScale(float scale); // per-window font scale. Adjust IO.FontGlobalScale if you want to scale all windows + + IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // set next window position. call before Begin() + IMGUI_API void SetNextWindowPosCenter(ImGuiSetCond cond = 0); // set next window position to be centered on screen. call before Begin() + IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() + IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeConstraintCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. + IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (enforce the range of scrollbars). set axis to 0.0f to leave it automatic. call before Begin() + IMGUI_API void SetNextWindowContentWidth(float width); // set next window content width (enforce the range of horizontal scrollbar). call before Begin() + IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // set next window collapsed state. call before Begin() + IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() + IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiSetCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. + IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiSetCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. + IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiSetCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). + IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / front-most. prefer using SetNextWindowFocus(). + IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiSetCond cond = 0); // set named window position. + IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiSetCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. + IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiSetCond cond = 0); // set named window collapsed state + IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. + + IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] + IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] + IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X + IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y + IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] + IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] + IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. + IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. + IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use negative 'offset' to access previous widgets. + IMGUI_API void SetStateStorage(ImGuiStorage* tree); // replace tree state storage with our own (if you want to manipulate it yourself, typically clear subsection of it) + IMGUI_API ImGuiStorage* GetStateStorage(); + + // Parameters stacks (shared) + IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font + IMGUI_API void PopFont(); + IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); + IMGUI_API void PopStyleColor(int count = 1); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); + IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); + IMGUI_API void PopStyleVar(int count = 1); + IMGUI_API ImFont* GetFont(); // get current font + IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied + IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API + IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier + IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied + + // Parameters stacks (current window) + IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) + IMGUI_API void PopItemWidth(); + IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position + IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space + IMGUI_API void PopTextWrapPos(); + IMGUI_API void PushAllowKeyboardFocus(bool v); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets + IMGUI_API void PopAllowKeyboardFocus(); + IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (uses io.KeyRepeatDelay/io.KeyRepeatRate for now). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. + IMGUI_API void PopButtonRepeat(); + + // Cursor / Layout + IMGUI_API void Separator(); // horizontal line + IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally + IMGUI_API void NewLine(); // undo a SameLine() + IMGUI_API void Spacing(); // add vertical spacing + IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size + IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if >0 + IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if >0 + IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) + IMGUI_API void EndGroup(); + IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position + IMGUI_API float GetCursorPosX(); // " + IMGUI_API float GetCursorPosY(); // " + IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " + IMGUI_API void SetCursorPosX(float x); // " + IMGUI_API void SetCursorPosY(float y); // " + IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position + IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) + IMGUI_API void SetCursorScreenPos(const ImVec2& pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] + IMGUI_API void AlignFirstTextHeightToWidgets(); // call once if the first item on the line is a Text() item and you want to vertically lower it to match subsequent (bigger) widgets + IMGUI_API float GetTextLineHeight(); // height of font == GetWindowFontSize() + IMGUI_API float GetTextLineHeightWithSpacing(); // distance (in pixels) between 2 consecutive lines of text == GetWindowFontSize() + GetStyle().ItemSpacing.y + IMGUI_API float GetItemsLineHeightWithSpacing(); // distance (in pixels) between 2 consecutive lines of standard height widgets == GetWindowFontSize() + GetStyle().FramePadding.y*2 + GetStyle().ItemSpacing.y + + // Columns + // You can also use SameLine(pos_x) for simplified columning. The columns API is still work-in-progress and rather lacking. + IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); // setup number of columns. use an identifier to distinguish multiple column sets. close with Columns(1). + IMGUI_API void NextColumn(); // next column + IMGUI_API int GetColumnIndex(); // get current column index + IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetcolumnsCount() inclusive. column 0 is usually 0.0f and not resizable unless you call this + IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column + IMGUI_API float GetColumnWidth(int column_index = -1); // column width (== GetColumnOffset(GetColumnIndex()+1) - GetColumnOffset(GetColumnOffset()) + IMGUI_API int GetColumnsCount(); // number of columns (what was passed to Columns()) + + // ID scopes + // If you are creating widgets in a loop you most likely want to push a unique identifier so ImGui can differentiate them. + // You can also use the "##foobar" syntax within widget label to distinguish them from each others. Read "A primer on the use of labels/IDs" in the FAQ for more details. + IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the *entire* stack! + IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); + IMGUI_API void PushID(const void* ptr_id); + IMGUI_API void PushID(int int_id); + IMGUI_API void PopID(); + IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). useful if you want to query into ImGuiStorage yourself. otherwise rarely needed + IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); + IMGUI_API ImGuiID GetID(const void* ptr_id); + + // Widgets + IMGUI_API void Text(const char* fmt, ...) IM_PRINTFARGS(1); + IMGUI_API void TextV(const char* fmt, va_list args); + IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_PRINTFARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args); + IMGUI_API void TextDisabled(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); + IMGUI_API void TextDisabledV(const char* fmt, va_list args); + IMGUI_API void TextWrapped(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). + IMGUI_API void TextWrappedV(const char* fmt, va_list args); + IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // doesn't require null terminated string if 'text_end' is specified. no copy done to any bounded stack buffer, recommended for long chunks of text + IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_PRINTFARGS(2); // display text+label aligned the same way as value+label widgets + IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args); + IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses + IMGUI_API void BulletText(const char* fmt, ...) IM_PRINTFARGS(1); // shortcut for Bullet()+Text() + IMGUI_API void BulletTextV(const char* fmt, va_list args); + IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button + IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) + IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); + IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding + IMGUI_API bool Checkbox(const char* label, bool* v); + IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); + IMGUI_API bool RadioButton(const char* label, bool active); + IMGUI_API bool RadioButton(const char* label, int* v, int v_button); + IMGUI_API bool Combo(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items = -1); // separate items with \0, end item-list with \0\0 + IMGUI_API bool Combo(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ColorButton(const ImVec4& col, bool small_height = false, bool outline_border = true); + IMGUI_API bool ColorEdit3(const char* label, float col[3]); // Hint: 'float col[3]' function argument is same as 'float* col'. You can pass address of first element out of a contiguous set, e.g. &myvector.x + IMGUI_API bool ColorEdit4(const char* label, float col[4], bool show_alpha = true); // " + IMGUI_API void ColorEditMode(ImGuiColorEditMode mode); // FIXME-OBSOLETE: This is inconsistent with most of the API and will be obsoleted/replaced. + IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); + IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); + IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); + IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); + + // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) + // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, remember than a 'float v[3]' function argument is the same as 'float* v'. You can pass address of your first element out of a contiguous set, e.g. &myvector.x + IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound + IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* display_format = "%.3f", const char* display_format_max = NULL, float power = 1.0f); + IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); // If v_min >= v_max we have no bound + IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f"); + IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* display_format = "%.0f", const char* display_format_max = NULL); + + // Widgets: Input with Keyboard + IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision = -1, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); + IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); + + // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) + IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); // adjust display_format to decorate the value with a prefix or a suffix. Use power!=1.0 for logarithmic sliders + IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); + IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* display_format = "%.0f"); + IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* display_format = "%.3f", float power = 1.0f); + IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* display_format = "%.0f"); + + // Widgets: Trees + IMGUI_API bool TreeNode(const char* label); // if returning 'true' the node is open and the tree id is pushed into the id stack. user is responsible for calling TreePop(). + IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_PRINTFARGS(2); // read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). + IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_PRINTFARGS(2); // " + IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args); // " + IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args); // " + IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); + IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_PRINTFARGS(3); + IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_PRINTFARGS(3); + IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args); + IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args); + IMGUI_API void TreePush(const char* str_id = NULL); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call Push/Pop yourself for layout purpose + IMGUI_API void TreePush(const void* ptr_id = NULL); // " + IMGUI_API void TreePop(); // ~ Unindent()+PopId() + IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() + IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode + IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiSetCond cond = 0); // set next TreeNode/CollapsingHeader open state. + IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). + IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header + + // Widgets: Selectable / Lists + IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height + IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); + IMGUI_API bool ListBox(const char* label, int* current_item, const char** items, int items_count, int height_in_items = -1); + IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); + IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. make sure to call ListBoxFooter() afterwards. + IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " + IMGUI_API void ListBoxFooter(); // terminate the scrolling region + + // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) + IMGUI_API void Value(const char* prefix, bool b); + IMGUI_API void Value(const char* prefix, int v); + IMGUI_API void Value(const char* prefix, unsigned int v); + IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); + IMGUI_API void ValueColor(const char* prefix, const ImVec4& v); + IMGUI_API void ValueColor(const char* prefix, ImU32 v); + + // Tooltips + IMGUI_API void SetTooltip(const char* fmt, ...) IM_PRINTFARGS(1); // set tooltip under mouse-cursor, typically use with ImGui::IsHovered(). last call wins + IMGUI_API void SetTooltipV(const char* fmt, va_list args); + IMGUI_API void BeginTooltip(); // use to create full-featured tooltip windows that aren't just text + IMGUI_API void EndTooltip(); + + // Menus + IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. only call EndMainMenuBar() if this returns true! + IMGUI_API void EndMainMenuBar(); + IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set). only call EndMenuBar() if this returns true! + IMGUI_API void EndMenuBar(); + IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! + IMGUI_API void EndMenu(); + IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment + IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL + + // Popups + IMGUI_API void OpenPopup(const char* str_id); // mark popup as open. popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). + IMGUI_API bool BeginPopup(const char* str_id); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returned true! + IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags extra_flags = 0); // modal dialog (can't close them by clicking outside) + IMGUI_API bool BeginPopupContextItem(const char* str_id, int mouse_button = 1); // helper to open and begin popup when clicked on last item. read comments in .cpp! + IMGUI_API bool BeginPopupContextWindow(bool also_over_items = true, const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on current window. + IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (no window). + IMGUI_API void EndPopup(); + IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. + + // Logging: all text output from interface is redirected to tty/file/clipboard. By default, tree nodes are automatically opened during logging. + IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty + IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file + IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard + IMGUI_API void LogFinish(); // stop logging (close file, etc.) + IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard + IMGUI_API void LogText(const char* fmt, ...) IM_PRINTFARGS(1); // pass text data straight to log (without being displayed) + + // Clipping + IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); + IMGUI_API void PopClipRect(); + + // Utilities + IMGUI_API bool IsItemHovered(); // was the last item hovered by mouse? + IMGUI_API bool IsItemHoveredRect(); // was the last item hovered by mouse? even if another item is active or window is blocked by popup while we are hovering this + IMGUI_API bool IsItemActive(); // was the last item active? (e.g. button being held, text field being edited- items that don't interact will always return false) + IMGUI_API bool IsItemClicked(int mouse_button = 0); // was the last item clicked? (e.g. button/node just clicked on) + IMGUI_API bool IsItemVisible(); // was the last item visible? (aka not out of sight due to clipping/scrolling.) + IMGUI_API bool IsAnyItemHovered(); + IMGUI_API bool IsAnyItemActive(); + IMGUI_API ImVec2 GetItemRectMin(); // get bounding rect of last item in screen space + IMGUI_API ImVec2 GetItemRectMax(); // " + IMGUI_API ImVec2 GetItemRectSize(); // " + IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. + IMGUI_API bool IsWindowHovered(); // is current window hovered and hoverable (not blocked by a popup) (differentiate child windows from each others) + IMGUI_API bool IsWindowFocused(); // is current window focused + IMGUI_API bool IsRootWindowFocused(); // is current root window focused (root = top-most parent of a child, otherwise self) + IMGUI_API bool IsRootWindowOrAnyChildFocused(); // is current root window or any of its child (including current window) focused + IMGUI_API bool IsRootWindowOrAnyChildHovered(); // is current root window or any of its child (including current window) hovered and hoverable (not blocked by a popup) + IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. + IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. + IMGUI_API bool IsPosHoveringAnyWindow(const ImVec2& pos); // is given position hovering any active imgui window + IMGUI_API float GetTime(); + IMGUI_API int GetFrameCount(); + IMGUI_API const char* GetStyleColName(ImGuiCol idx); + IMGUI_API ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = +0.0f); // utility to find the closest point the last item bounding rectangle edge. useful to visually link items + IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); + IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. + + IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame + IMGUI_API void EndChildFrame(); + + IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); + IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); + IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); + IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); + + // Inputs + IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] + IMGUI_API bool IsKeyDown(int key_index); // key_index into the keys_down[] array, imgui doesn't know the semantic of each entry, uses your own indices! + IMGUI_API bool IsKeyPressed(int key_index, bool repeat = true); // uses user's key indices as stored in the keys_down[] array. if repeat=true. uses io.KeyRepeatDelay / KeyRepeatRate + IMGUI_API bool IsKeyReleased(int key_index); // " + IMGUI_API bool IsMouseDown(int button); // is mouse button held + IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) + IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. + IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) + IMGUI_API bool IsMouseHoveringWindow(); // is mouse hovering current window ("window" in API names always refer to current window). disregarding of any consideration of being blocked by a popup. (unlike IsWindowHovered() this will return true even if the window is blocked because of a popup) + IMGUI_API bool IsMouseHoveringAnyWindow(); // is mouse hovering any visible window + IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. + IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls + IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse positioning at the time of opening popup we have BeginPopup() into + IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold + IMGUI_API void ResetMouseDragDelta(int button = 0); // + IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you + IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type + IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application handle). e.g. force capture keyboard when your widget is being hovered. + IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application handle). + + // Helpers functions to access functions pointers in ImGui::GetIO() + IMGUI_API void* MemAlloc(size_t sz); + IMGUI_API void MemFree(void* ptr); + IMGUI_API const char* GetClipboardText(); + IMGUI_API void SetClipboardText(const char* text); + + // Internal context access - if you want to use multiple context, share context between modules (e.g. DLL). There is a default context created and active by default. + // All contexts share a same ImFontAtlas by default. If you want different font atlas, you can new() them and overwrite the GetIO().Fonts variable of an ImGui context. + IMGUI_API const char* GetVersion(); + IMGUI_API ImGuiContext* CreateContext(void* (*malloc_fn)(size_t) = NULL, void (*free_fn)(void*) = NULL); + IMGUI_API void DestroyContext(ImGuiContext* ctx); + IMGUI_API ImGuiContext* GetCurrentContext(); + IMGUI_API void SetCurrentContext(ImGuiContext* ctx); + + // Obsolete (will be removed) +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1<<5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } // OBSOLETE 1.49+ + static inline ImFont* GetWindowFont() { return GetFont(); } // OBSOLETE 1.48+ + static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETE 1.48+ + static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETE 1.42+ + static inline bool GetWindowCollapsed() { return ImGui::IsWindowCollapsed(); } // OBSOLETE 1.39+ + static inline bool IsRectClipped(const ImVec2& size) { return !IsRectVisible(size); } // OBSOLETE 1.39+ +#endif + +} // namespace ImGui + +// Flags for ImGui::Begin() +enum ImGuiWindowFlags_ +{ + // Default: 0 + ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar + ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip + ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window + ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) + ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel + ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it + ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame + ImGuiWindowFlags_ShowBorders = 1 << 7, // Show borders around windows and items + ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file + ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs + ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar + ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. + ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state + ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) + ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) + ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) + ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) + // [Internal] + ImGuiWindowFlags_ChildWindow = 1 << 20, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitX = 1 << 21, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ChildWindowAutoFitY = 1 << 22, // Don't use! For internal use by BeginChild() + ImGuiWindowFlags_ComboBox = 1 << 23, // Don't use! For internal use by ComboBox() + ImGuiWindowFlags_Tooltip = 1 << 24, // Don't use! For internal use by BeginTooltip() + ImGuiWindowFlags_Popup = 1 << 25, // Don't use! For internal use by BeginPopup() + ImGuiWindowFlags_Modal = 1 << 26, // Don't use! For internal use by BeginPopupModal() + ImGuiWindowFlags_ChildMenu = 1 << 27 // Don't use! For internal use by BeginMenu() +}; + +// Flags for ImGui::InputText() +enum ImGuiInputTextFlags_ +{ + // Default: 0 + ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ + ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef + ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z + ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs + ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus + ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) + ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) + ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) + ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. + ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. + ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field + ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, allow exiting edition by pressing Enter. Ctrl+Enter to add new line (by default adds new lines with Enter). + ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally + ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode + ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode + ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' + // [Internal] + ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() +}; + +// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() +enum ImGuiTreeNodeFlags_ +{ + ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected + ImGuiTreeNodeFlags_Framed = 1 << 1, // Full colored frame (e.g. for CollapsingHeader) + ImGuiTreeNodeFlags_AllowOverlapMode = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one + ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack + ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) + ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open + ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node + ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. + ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). + ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow + //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 10, // FIXME: TODO: Extend hit box horizontally even if not framed + //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 11, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible + ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoAutoOpenOnLog +}; + +// Flags for ImGui::Selectable() +enum ImGuiSelectableFlags_ +{ + // Default: 0 + ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window + ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) + ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too +}; + +// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array +enum ImGuiKey_ +{ + ImGuiKey_Tab, // for tabbing through fields + ImGuiKey_LeftArrow, // for text edit + ImGuiKey_RightArrow,// for text edit + ImGuiKey_UpArrow, // for text edit + ImGuiKey_DownArrow, // for text edit + ImGuiKey_PageUp, + ImGuiKey_PageDown, + ImGuiKey_Home, // for text edit + ImGuiKey_End, // for text edit + ImGuiKey_Delete, // for text edit + ImGuiKey_Backspace, // for text edit + ImGuiKey_Enter, // for text edit + ImGuiKey_Escape, // for text edit + ImGuiKey_A, // for text edit CTRL+A: select all + ImGuiKey_C, // for text edit CTRL+C: copy + ImGuiKey_V, // for text edit CTRL+V: paste + ImGuiKey_X, // for text edit CTRL+X: cut + ImGuiKey_Y, // for text edit CTRL+Y: redo + ImGuiKey_Z, // for text edit CTRL+Z: undo + ImGuiKey_COUNT +}; + +// Enumeration for PushStyleColor() / PopStyleColor() +enum ImGuiCol_ +{ + ImGuiCol_Text, + ImGuiCol_TextDisabled, + ImGuiCol_WindowBg, // Background of normal windows + ImGuiCol_ChildWindowBg, // Background of child windows + ImGuiCol_PopupBg, // Background of popups, menus, tooltips windows + ImGuiCol_Border, + ImGuiCol_BorderShadow, + ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input + ImGuiCol_FrameBgHovered, + ImGuiCol_FrameBgActive, + ImGuiCol_TitleBg, + ImGuiCol_TitleBgCollapsed, + ImGuiCol_TitleBgActive, + ImGuiCol_MenuBarBg, + ImGuiCol_ScrollbarBg, + ImGuiCol_ScrollbarGrab, + ImGuiCol_ScrollbarGrabHovered, + ImGuiCol_ScrollbarGrabActive, + ImGuiCol_ComboBg, + ImGuiCol_CheckMark, + ImGuiCol_SliderGrab, + ImGuiCol_SliderGrabActive, + ImGuiCol_Button, + ImGuiCol_ButtonHovered, + ImGuiCol_ButtonActive, + ImGuiCol_Header, + ImGuiCol_HeaderHovered, + ImGuiCol_HeaderActive, + ImGuiCol_Column, + ImGuiCol_ColumnHovered, + ImGuiCol_ColumnActive, + ImGuiCol_ResizeGrip, + ImGuiCol_ResizeGripHovered, + ImGuiCol_ResizeGripActive, + ImGuiCol_CloseButton, + ImGuiCol_CloseButtonHovered, + ImGuiCol_CloseButtonActive, + ImGuiCol_PlotLines, + ImGuiCol_PlotLinesHovered, + ImGuiCol_PlotHistogram, + ImGuiCol_PlotHistogramHovered, + ImGuiCol_TextSelectedBg, + ImGuiCol_ModalWindowDarkening, // darken entire screen when a modal window is active + ImGuiCol_COUNT +}; + +// Enumeration for PushStyleVar() / PopStyleVar() +// NB: the enum only refers to fields of ImGuiStyle() which makes sense to be pushed/poped in UI code. Feel free to add others. +enum ImGuiStyleVar_ +{ + ImGuiStyleVar_Alpha, // float + ImGuiStyleVar_WindowPadding, // ImVec2 + ImGuiStyleVar_WindowRounding, // float + ImGuiStyleVar_WindowMinSize, // ImVec2 + ImGuiStyleVar_ChildWindowRounding, // float + ImGuiStyleVar_FramePadding, // ImVec2 + ImGuiStyleVar_FrameRounding, // float + ImGuiStyleVar_ItemSpacing, // ImVec2 + ImGuiStyleVar_ItemInnerSpacing, // ImVec2 + ImGuiStyleVar_IndentSpacing, // float + ImGuiStyleVar_GrabMinSize, // float + ImGuiStyleVar_ButtonTextAlign, // flags ImGuiAlign_* + ImGuiStyleVar_Count_ +}; + +// Enumeration for ColorEditMode() +// FIXME-OBSOLETE: Will be replaced by future color/picker api +enum ImGuiColorEditMode_ +{ + ImGuiColorEditMode_UserSelect = -2, + ImGuiColorEditMode_UserSelectShowButton = -1, + ImGuiColorEditMode_RGB = 0, + ImGuiColorEditMode_HSV = 1, + ImGuiColorEditMode_HEX = 2 +}; + +// Enumeration for GetMouseCursor() +enum ImGuiMouseCursor_ +{ + ImGuiMouseCursor_None = -1, + ImGuiMouseCursor_Arrow = 0, + ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. + ImGuiMouseCursor_Move, // Unused + ImGuiMouseCursor_ResizeNS, // Unused + ImGuiMouseCursor_ResizeEW, // When hovering over a column + ImGuiMouseCursor_ResizeNESW, // Unused + ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window + ImGuiMouseCursor_Count_ +}; + +// Condition flags for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions +// All those functions treat 0 as a shortcut to ImGuiSetCond_Always +enum ImGuiSetCond_ +{ + ImGuiSetCond_Always = 1 << 0, // Set the variable + ImGuiSetCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call with succeed) + ImGuiSetCond_FirstUseEver = 1 << 2, // Set the variable if the window has no saved data (if doesn't exist in the .ini file) + ImGuiSetCond_Appearing = 1 << 3 // Set the variable if the window is appearing after being hidden/inactive (or the first time) +}; + +struct ImGuiStyle +{ + float Alpha; // Global alpha applies to everything in ImGui + ImVec2 WindowPadding; // Padding within a window + ImVec2 WindowMinSize; // Minimum window size + float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows + ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. + float ChildWindowRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows + ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets) + float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). + ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines + ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) + ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! + float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). + float ColumnsMinSpacing; // Minimum horizontal spacing between two columns + float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar + float ScrollbarRounding; // Radius of grab corners for scrollbar + float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. + float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. + ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. + ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. + ImVec2 DisplaySafeAreaPadding; // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. + bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. + bool AntiAliasedShapes; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) + float CurveTessellationTol; // Tessellation tolerance. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. + ImVec4 Colors[ImGuiCol_COUNT]; + + IMGUI_API ImGuiStyle(); +}; + +// This is where your app communicate with ImGui. Access via ImGui::GetIO(). +// Read 'Programmer guide' section in .cpp file for general usage. +struct ImGuiIO +{ + //------------------------------------------------------------------ + // Settings (fill once) // Default value: + //------------------------------------------------------------------ + + ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. + float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. + float IniSavingRate; // = 5.0f // Maximum time between saving positions/sizes to .ini file, in seconds. + const char* IniFilename; // = "imgui.ini" // Path to .ini file. NULL to disable .ini saving. + const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging + int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array + float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.020f // When holding a key/button, rate at which it repeats, in seconds. + void* UserData; // = NULL // Store your own data for retrieval by callbacks. + + ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. + float FontGlobalScale; // = 1.0f // Global scale all fonts + bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. + ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. + ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. + ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize + + // Advanced/subtle behaviors + bool OSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl + + //------------------------------------------------------------------ + // User Functions + //------------------------------------------------------------------ + + // Rendering function, will be called in Render(). + // Alternatively you can keep this to NULL and call GetDrawData() after Render() to get the same pointer. + // See example applications if you are unsure of how to implement this. + void (*RenderDrawListsFn)(ImDrawData* data); + + // Optional: access OS clipboard + // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) + const char* (*GetClipboardTextFn)(void* user_data); + void (*SetClipboardTextFn)(void* user_data, const char* text); + void* ClipboardUserData; + + // Optional: override memory allocations. MemFreeFn() may be called with a NULL pointer. + // (default to posix malloc/free) + void* (*MemAllocFn)(size_t sz); + void (*MemFreeFn)(void* ptr); + + // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) + // (default to use native imm32 api on Windows) + void (*ImeSetInputScreenPosFn)(int x, int y); + void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. + + //------------------------------------------------------------------ + // Input - Fill before calling NewFrame() + //------------------------------------------------------------------ + + ImVec2 MousePos; // Mouse position, in pixels (set to -1,-1 if no mouse / on another screen, etc.) + bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + float MouseWheel; // Mouse wheel: 1 unit scrolls about 5 lines text. + bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). + bool KeyCtrl; // Keyboard modifier pressed: Control + bool KeyShift; // Keyboard modifier pressed: Shift + bool KeyAlt; // Keyboard modifier pressed: Alt + bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows + bool KeysDown[512]; // Keyboard keys that are pressed (in whatever storage order you naturally have access to keyboard data) + ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. + + // Functions + IMGUI_API void AddInputCharacter(ImWchar c); // Helper to add a new character into InputCharacters[] + IMGUI_API void AddInputCharactersUTF8(const char* utf8_chars); // Helper to add new characters into InputCharacters[] from an UTF-8 string + inline void ClearInputCharacters() { InputCharacters[0] = 0; } // Helper to clear the text input buffer + + //------------------------------------------------------------------ + // Output - Retrieve after calling NewFrame(), you can use them to discard inputs or hide them from the rest of your application + //------------------------------------------------------------------ + + bool WantCaptureMouse; // Mouse is hovering a window or widget is active (= ImGui will use your mouse input) + bool WantCaptureKeyboard; // Widget is active (= ImGui will use your keyboard input) + bool WantTextInput; // Some text input widget is active, which will read input characters from the InputCharacters array. + float Framerate; // Framerate estimation, in frame per second. Rolling average estimation based on IO.DeltaTime over 120 frames + int MetricsAllocs; // Number of active memory allocations + int MetricsRenderVertices; // Vertices output during last call to Render() + int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 + int MetricsActiveWindows; // Number of visible windows (exclude child windows) + + //------------------------------------------------------------------ + // [Private] ImGui will maintain those fields. Forward compatibility not guaranteed! + //------------------------------------------------------------------ + + ImVec2 MousePosPrev; // Previous mouse position + ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are negative to allow mouse enabling/disabling. + bool MouseClicked[5]; // Mouse button went from !Down to Down + ImVec2 MouseClickedPos[5]; // Position at time of clicking + float MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? + bool MouseReleased[5]; // Mouse button went from Down to !Down + bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. + float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) + float MouseDownDurationPrev[5]; // Previous time the mouse button has been down + float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the click point + float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) + float KeysDownDurationPrev[512]; // Previous duration the key has been down + + IMGUI_API ImGuiIO(); +}; + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +// Lightweight std::vector<> like class to avoid dragging dependencies (also: windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). +// Our implementation does NOT call c++ constructors because we don't use them in ImGui. Don't use this class as a straight std::vector replacement in your code! +template +class ImVector +{ +public: + int Size; + int Capacity; + T* Data; + + typedef T value_type; + typedef value_type* iterator; + typedef const value_type* const_iterator; + + ImVector() { Size = Capacity = 0; Data = NULL; } + ~ImVector() { if (Data) ImGui::MemFree(Data); } + + inline bool empty() const { return Size == 0; } + inline int size() const { return Size; } + inline int capacity() const { return Capacity; } + + inline value_type& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } + inline const value_type& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } + + inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } + inline iterator begin() { return Data; } + inline const_iterator begin() const { return Data; } + inline iterator end() { return Data + Size; } + inline const_iterator end() const { return Data + Size; } + inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } + inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } + inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size-1]; } + inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size-1]; } + inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } + + inline int _grow_capacity(int new_size) { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > new_size ? new_capacity : new_size; } + + inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } + inline void reserve(int new_capacity) + { + if (new_capacity <= Capacity) return; + T* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(value_type)); + if (Data) + memcpy(new_data, Data, (size_t)Size * sizeof(value_type)); + ImGui::MemFree(Data); + Data = new_data; + Capacity = new_capacity; + } + + inline void push_back(const value_type& v) { if (Size == Capacity) reserve(_grow_capacity(Size+1)); Data[Size++] = v; } + inline void pop_back() { IM_ASSERT(Size > 0); Size--; } + + inline iterator erase(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } + inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(Capacity ? Capacity * 2 : 4); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); Data[off] = v; Size++; return Data + off; } +}; + +// Helper: execute a block of code at maximum once a frame +// Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. +// Usage: +// IMGUI_ONCE_UPON_A_FRAME +// { +// // code block will be executed one per frame +// } +// Attention! the macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. +#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf##__LINE__; if (imgui_oaf##__LINE__) +struct ImGuiOnceUponAFrame +{ + ImGuiOnceUponAFrame() { RefFrame = -1; } + mutable int RefFrame; + operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } +}; + +// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" +struct ImGuiTextFilter +{ + struct TextRange + { + const char* b; + const char* e; + + TextRange() { b = e = NULL; } + TextRange(const char* _b, const char* _e) { b = _b; e = _e; } + const char* begin() const { return b; } + const char* end() const { return e; } + bool empty() const { return b == e; } + char front() const { return *b; } + static bool is_blank(char c) { return c == ' ' || c == '\t'; } + void trim_blanks() { while (b < e && is_blank(*b)) b++; while (e > b && is_blank(*(e-1))) e--; } + IMGUI_API void split(char separator, ImVector& out); + }; + + char InputBuf[256]; + ImVector Filters; + int CountGrep; + + ImGuiTextFilter(const char* default_filter = ""); + ~ImGuiTextFilter() {} + void Clear() { InputBuf[0] = 0; Build(); } + bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build + bool PassFilter(const char* text, const char* text_end = NULL) const; + bool IsActive() const { return !Filters.empty(); } + IMGUI_API void Build(); +}; + +// Helper: Text buffer for logging/accumulating text +struct ImGuiTextBuffer +{ + ImVector Buf; + + ImGuiTextBuffer() { Buf.push_back(0); } + inline char operator[](int i) { return Buf.Data[i]; } + const char* begin() const { return &Buf.front(); } + const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator + int size() const { return Buf.Size - 1; } + bool empty() { return Buf.Size <= 1; } + void clear() { Buf.clear(); Buf.push_back(0); } + const char* c_str() const { return Buf.Data; } + IMGUI_API void append(const char* fmt, ...) IM_PRINTFARGS(2); + IMGUI_API void appendv(const char* fmt, va_list args); +}; + +// Helper: Simple Key->value storage +// Typically you don't have to worry about this since a storage is held within each Window. +// We use it to e.g. store collapse state for a tree (Int 0/1), store color edit options. +// You can use it as custom user storage for temporary values. +// Declare your own storage if: +// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). +// - You want to store custom debug data easily without adding or editing structures in your code. +// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types. +struct ImGuiStorage +{ + struct Pair + { + ImGuiID key; + union { int val_i; float val_f; void* val_p; }; + Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } + Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } + Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } + }; + ImVector Data; + + // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) + // - Set***() functions find pair, insertion on demand if missing. + // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair. + IMGUI_API void Clear(); + IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; + IMGUI_API void SetInt(ImGuiID key, int val); + IMGUI_API bool GetBool(ImGuiID key, bool default_val = false) const; + IMGUI_API void SetBool(ImGuiID key, bool val); + IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; + IMGUI_API void SetFloat(ImGuiID key, float val); + IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL + IMGUI_API void SetVoidPtr(ImGuiID key, void* val); + + // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. + // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. + // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct) + // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; + IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); + IMGUI_API bool* GetBoolRef(ImGuiID key, bool default_val = false); + IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); + IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); + + // Use on your own storage if you know only integer are being stored (open/close all tree nodes) + IMGUI_API void SetAllInt(int val); +}; + +// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. +struct ImGuiTextEditCallbackData +{ + ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only + ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only + void* UserData; // What user passed to InputText() // Read-only + bool ReadOnly; // Read-only mode // Read-only + + // CharFilter event: + ImWchar EventChar; // Character input // Read-write (replace character or set to zero) + + // Completion,History,Always events: + // If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. + ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only + char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) + int BufTextLen; // Current text length in bytes // Read-write + int BufSize; // Maximum text length in bytes // Read-only + bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write + int CursorPos; // // Read-write + int SelectionStart; // // Read-write (== to SelectionEnd when no selection) + int SelectionEnd; // // Read-write + + // NB: Helper functions for text manipulation. Calling those function loses selection. + void DeleteChars(int pos, int bytes_count); + void InsertChars(int pos, const char* text, const char* text_end = NULL); + bool HasSelection() const { return SelectionStart != SelectionEnd; } +}; + +// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). +// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. +struct ImGuiSizeConstraintCallbackData +{ + void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() + ImVec2 Pos; // Read-only. Window position, for reference. + ImVec2 CurrentSize; // Read-only. Current window size. + ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. +}; + +// Helpers macros to generate 32-bits encoded colors +#ifdef IMGUI_USE_BGRA_PACKED_COLOR +#define IM_COL32_R_SHIFT 16 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 0 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#else +#define IM_COL32_R_SHIFT 0 +#define IM_COL32_G_SHIFT 8 +#define IM_COL32_B_SHIFT 16 +#define IM_COL32_A_SHIFT 24 +#define IM_COL32_A_MASK 0xFF000000 +#endif +#define IM_COL32(R,G,B,A) (((ImU32)(A)<>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; } + ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } + ImColor(const ImVec4& col) { Value = col; } + inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } + inline operator ImVec4() const { return Value; } + + inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } + + static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } +}; + +// Helper: Manually clip large list of items. +// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. +// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. +// ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. +// Usage: +// ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. +// while (clipper.Step()) +// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) +// ImGui::Text("line number %d", i); +// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). +// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. +// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) +// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. +struct ImGuiListClipper +{ + float StartPosY; + float ItemsHeight; + int ItemsCount, StepNo, DisplayStart, DisplayEnd; + + // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). + // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetItemsLineHeightWithSpacing(). + // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). + ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). + ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. + + IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. + IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. + IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. +}; + +//----------------------------------------------------------------------------- +// Draw List +// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. +//----------------------------------------------------------------------------- + +// Draw callbacks for advanced uses. +// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) +// Draw callback may be useful for example, A) Change your GPU render state, B) render a complex 3D scene inside a UI element (without an intermediate texture/render target), etc. +// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) cmd.UserCallback(parent_list, cmd); else RenderTriangles()' +typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); + +// Typically, 1 command = 1 gpu draw call (unless command is a callback) +struct ImDrawCmd +{ + unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. + ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2) + ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. + ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. + void* UserCallbackData; // The draw callback code can access this. + + ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = -8192.0f; ClipRect.z = ClipRect.w = +8192.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; } +}; + +// Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) +#ifndef ImDrawIdx +typedef unsigned short ImDrawIdx; +#endif + +// Vertex layout +#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT +struct ImDrawVert +{ + ImVec2 pos; + ImVec2 uv; + ImU32 col; +}; +#else +// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h +// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. +// The type has to be described within the macro (you can either declare the struct or use a typedef) +IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; +#endif + +// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. +// You can also use them to simulate drawing layers and submit primitives in a different order than how they will be rendered. +struct ImDrawChannel +{ + ImVector CmdBuffer; + ImVector IdxBuffer; +}; + +// Draw command list +// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. +// At the moment, each ImGui window contains its own ImDrawList but they could potentially be merged in the future. +// If you want to add custom rendering within a window, you can use ImGui::GetWindowDrawList() to access the current draw list and add your own primitives. +// You can interleave normal ImGui:: calls and adding primitives to the current draw list. +// All positions are in screen coordinates (0,0=top-left, 1 pixel per unit). Primitives are always added to the list and not culled (culling is done at render time and at a higher-level by ImGui:: functions). +struct ImDrawList +{ + // This is what you have to render + ImVector CmdBuffer; // Commands. Typically 1 command = 1 gpu draw call. + ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those + ImVector VtxBuffer; // Vertex buffer. + + // [Internal, used while building lists] + const char* _OwnerName; // Pointer to owner window's name for debugging + unsigned int _VtxCurrentIdx; // [Internal] == VtxBuffer.Size + ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) + ImVector _ClipRectStack; // [Internal] + ImVector _TextureIdStack; // [Internal] + ImVector _Path; // [Internal] current path building + int _ChannelsCurrent; // [Internal] current channel number (0) + int _ChannelsCount; // [Internal] number of active channels (1+) + ImVector _Channels; // [Internal] draw channels for columns API (not resized down so _ChannelsCount may be smaller than _Channels.Size) + + ImDrawList() { _OwnerName = NULL; Clear(); } + ~ImDrawList() { ClearFreeMemory(); } + IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) + IMGUI_API void PushClipRectFullScreen(); + IMGUI_API void PopClipRect(); + IMGUI_API void PushTextureID(const ImTextureID& texture_id); + IMGUI_API void PopTextureID(); + + // Primitives + IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0, float thickness = 1.0f); // a: upper-left, b: lower-right, rounding_corners_flags: 4-bits corresponding to which corner to round + IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ~0); // a: upper-left, b: lower-right + IMGUI_API void AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); + IMGUI_API void AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); + IMGUI_API void AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness = 1.0f); + IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); + IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f); + IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); + IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); + IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); + IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); + IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness, bool anti_aliased); + IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col, bool anti_aliased); + IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); + + // Stateful path API, add points then finish with PathFill() or PathStroke() + inline void PathClear() { _Path.resize(0); } + inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } + inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } + inline void PathFill(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col, true); PathClear(); } + inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness, true); PathClear(); } + IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); + IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle + IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0); + IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners_flags = ~0); // rounding_corners_flags: 4-bits corresponding to which corner to round + + // Channels + // - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) + // - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end) + IMGUI_API void ChannelsSplit(int channels_count); + IMGUI_API void ChannelsMerge(); + IMGUI_API void ChannelsSetCurrent(int channel_index); + + // Advanced + IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. + IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible + + // Internal helpers + // NB: all primitives needs to be reserved via PrimReserve() beforehand! + IMGUI_API void Clear(); + IMGUI_API void ClearFreeMemory(); + IMGUI_API void PrimReserve(int idx_count, int vtx_count); + IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) + IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); + IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); + inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } + inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } + inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } + IMGUI_API void UpdateClipRect(); + IMGUI_API void UpdateTextureID(); +}; + +// All draw data to render an ImGui frame +struct ImDrawData +{ + bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. + ImDrawList** CmdLists; + int CmdListsCount; + int TotalVtxCount; // For convenience, sum of all cmd_lists vtx_buffer.Size + int TotalIdxCount; // For convenience, sum of all cmd_lists idx_buffer.Size + + // Functions + ImDrawData() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; } + IMGUI_API void DeIndexAllBuffers(); // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! + IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +}; + +struct ImFontConfig +{ + void* FontData; // // TTF data + int FontDataSize; // // TTF data size + bool FontDataOwnedByAtlas; // true // TTF data ownership taken by the container ImFontAtlas (will delete memory itself). Set to true + int FontNo; // 0 // Index of font within TTF file + float SizePixels; // // Size in pixels for rasterizer + int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. + bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. + ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs + const ImWchar* GlyphRanges; // // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. + bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). + bool MergeGlyphCenterV; // false // When merging (multiple ImFontInput for one ImFont), vertically center new glyphs instead of aligning their baseline + + // [Internal] + char Name[32]; // Name (strictly for debugging) + ImFont* DstFont; + + IMGUI_API ImFontConfig(); +}; + +// Load and rasterize multiple TTF fonts into a same texture. +// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. +// We also add custom graphic data into the texture that serves for ImGui. +// 1. (Optional) Call AddFont*** functions. If you don't call any, the default font will be loaded for you. +// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. +// 3. Upload the pixels data into a texture within your graphics system. +// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. +// 5. Call ClearTexData() to free textures memory on the heap. +// NB: If you use a 'glyph_ranges' array you need to make sure that your array persist up until the ImFont is cleared. We only copy the pointer, not the data. +struct ImFontAtlas +{ + IMGUI_API ImFontAtlas(); + IMGUI_API ~ImFontAtlas(); + IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); + IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); + IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); + IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build() + IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data' still owned by caller. Compress with binary_to_compressed_c.cpp + IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 paramaeter + IMGUI_API void ClearTexData(); // Clear the CPU-side texture data. Saves RAM once the texture has been copied to graphics memory. + IMGUI_API void ClearInputData(); // Clear the input TTF data (inc sizes, glyph ranges) + IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates) + IMGUI_API void Clear(); // Clear all + + // Retrieve texture data + // User is in charge of copying the pixels into graphics memory, then call SetTextureUserID() + // After loading the texture into your graphic system, store your texture handle in 'TexID' (ignore if you aren't using multiple fonts nor images) + // RGBA32 format is provided for convenience and high compatibility, but note that all RGB pixels are white, so 75% of the memory is wasted. + // Pitch = Width * BytesPerPixels + IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel + IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel + void SetTexID(void* id) { TexID = id; } + + // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) + // NB: Make sure that your string are UTF-8 and NOT in your local code page. See FAQ for details. + IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin + IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters + IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs + IMGUI_API const ImWchar* GetGlyphRangesChinese(); // Japanese + full set of about 21000 CJK Unified Ideographs + IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters + IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters + + // Members + // (Access texture data via GetTexData*() calls which will setup a default font for you.) + void* TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It ia passed back to you during rendering. + unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight + unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 + int TexWidth; // Texture width calculated during Build(). + int TexHeight; // Texture height calculated during Build(). + int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. + ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel + ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. + + // Private + ImVector ConfigData; // Internal data + IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions. + IMGUI_API void RenderCustomTexData(int pass, void* rects); +}; + +// Font runtime data and rendering +// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). +struct ImFont +{ + struct Glyph + { + ImWchar Codepoint; + float XAdvance; + float X0, Y0, X1, Y1; + float U0, V0, U1, V1; // Texture coordinates + }; + + // Members: Hot ~62/78 bytes + float FontSize; // // Height of characters, set during loading (don't change after loading) + float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() + ImVec2 DisplayOffset; // = (0.f,1.f) // Offset font rendering by xx pixels + ImVector Glyphs; // // All glyphs. + ImVector IndexXAdvance; // // Sparse. Glyphs->XAdvance in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). + ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. + const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) + float FallbackXAdvance; // == FallbackGlyph->XAdvance + ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() + + // Members: Cold ~18/26 bytes + short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. + ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData + ImFontAtlas* ContainerAtlas; // // What we has been loaded into + float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] + + // Methods + IMGUI_API ImFont(); + IMGUI_API ~ImFont(); + IMGUI_API void Clear(); + IMGUI_API void BuildLookupTable(); + IMGUI_API const Glyph* FindGlyph(ImWchar c) const; + IMGUI_API void SetFallbackChar(ImWchar c); + float GetCharAdvance(ImWchar c) const { return ((int)c < IndexXAdvance.Size) ? IndexXAdvance[(int)c] : FallbackXAdvance; } + bool IsLoaded() const { return ContainerAtlas != NULL; } + + // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. + // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. + IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 + IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; + IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; + IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; + + // Private + IMGUI_API void GrowIndex(int new_size); + IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. +}; + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) +#ifdef IMGUI_INCLUDE_IMGUI_USER_H +#include "imgui_user.h" +#endif diff --git a/apps/exampleViewer/common/imgui/imgui_demo.cpp b/apps/exampleViewer/common/imgui/imgui_demo.cpp new file mode 100644 index 0000000000..b840f05c2f --- /dev/null +++ b/apps/exampleViewer/common/imgui/imgui_demo.cpp @@ -0,0 +1,2630 @@ +// dear imgui, v1.50 WIP +// (demo code) + +// Don't remove this file from your project! It is useful reference code that you can execute. +// You can call ImGui::ShowTestWindow() in your code to learn about various features of ImGui. +// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowTestWindow(). + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#include // toupper, isprint +#include // sqrtf, powf, cosf, sinf, floorf, ceilf +#include // vsnprintf, sscanf, printf +#include // NULL, malloc, free, qsort, atoi +#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier +#include // intptr_t +#else +#include // intptr_t +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#define snprintf _snprintf +#endif +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code) +#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' +#pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal +#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size +#pragma GCC diagnostic ignored "-Wformat-security" // warning : format string is not a string literal (potentially insecure) +#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value +#endif + +// Play it nice with Windows users. Notepad in 2015 still doesn't display text data with Unix-style \n. +#ifdef _WIN32 +#define IM_NEWLINE "\r\n" +#else +#define IM_NEWLINE "\n" +#endif + +#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) +#define IM_MAX(_A,_B) (((_A) >= (_B)) ? (_A) : (_B)) + +//----------------------------------------------------------------------------- +// DEMO CODE +//----------------------------------------------------------------------------- + +#ifndef IMGUI_DISABLE_TEST_WINDOWS + +static void ShowExampleAppConsole(bool* p_open); +static void ShowExampleAppLog(bool* p_open); +static void ShowExampleAppLayout(bool* p_open); +static void ShowExampleAppPropertyEditor(bool* p_open); +static void ShowExampleAppLongText(bool* p_open); +static void ShowExampleAppAutoResize(bool* p_open); +static void ShowExampleAppConstrainedResize(bool* p_open); +static void ShowExampleAppFixedOverlay(bool* p_open); +static void ShowExampleAppManipulatingWindowTitle(bool* p_open); +static void ShowExampleAppCustomRendering(bool* p_open); +static void ShowExampleAppMainMenuBar(); +static void ShowExampleMenuFile(); + +static void ShowHelpMarker(const char* desc) +{ + ImGui::TextDisabled("(?)"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::PushTextWrapPos(450.0f); + ImGui::TextUnformatted(desc); + ImGui::PopTextWrapPos(); + ImGui::EndTooltip(); + } +} + +void ImGui::ShowUserGuide() +{ + ImGui::BulletText("Double-click on title bar to collapse window."); + ImGui::BulletText("Click and drag on lower right corner to resize window."); + ImGui::BulletText("Click and drag on any empty space to move window."); + ImGui::BulletText("Mouse Wheel to scroll."); + if (ImGui::GetIO().FontAllowUserScaling) + ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); + ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); + ImGui::BulletText("CTRL+Click on a slider or drag box to input text."); + ImGui::BulletText( + "While editing text:\n" + "- Hold SHIFT or use mouse to select text\n" + "- CTRL+Left/Right to word jump\n" + "- CTRL+A or double-click to select all\n" + "- CTRL+X,CTRL+C,CTRL+V clipboard\n" + "- CTRL+Z,CTRL+Y undo/redo\n" + "- ESCAPE to revert\n" + "- You can apply arithmetic operators +,*,/ on numerical values.\n" + " Use +- to subtract.\n"); +} + +// Demonstrate most ImGui features (big function!) +void ImGui::ShowTestWindow(bool* p_open) +{ + // Examples apps + static bool show_app_main_menu_bar = false; + static bool show_app_console = false; + static bool show_app_log = false; + static bool show_app_layout = false; + static bool show_app_property_editor = false; + static bool show_app_long_text = false; + static bool show_app_auto_resize = false; + static bool show_app_constrained_resize = false; + static bool show_app_fixed_overlay = false; + static bool show_app_manipulating_window_title = false; + static bool show_app_custom_rendering = false; + static bool show_app_style_editor = false; + + static bool show_app_metrics = false; + static bool show_app_about = false; + + if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); + if (show_app_console) ShowExampleAppConsole(&show_app_console); + if (show_app_log) ShowExampleAppLog(&show_app_log); + if (show_app_layout) ShowExampleAppLayout(&show_app_layout); + if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); + if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); + if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); + if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); + if (show_app_fixed_overlay) ShowExampleAppFixedOverlay(&show_app_fixed_overlay); + if (show_app_manipulating_window_title) ShowExampleAppManipulatingWindowTitle(&show_app_manipulating_window_title); + if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); + + if (show_app_metrics) ImGui::ShowMetricsWindow(&show_app_metrics); + if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } + if (show_app_about) + { + ImGui::Begin("About ImGui", &show_app_about, ImGuiWindowFlags_AlwaysAutoResize); + ImGui::Text("dear imgui, %s", ImGui::GetVersion()); + ImGui::Separator(); + ImGui::Text("By Omar Cornut and all github contributors."); + ImGui::Text("ImGui is licensed under the MIT License, see LICENSE for more information."); + ImGui::End(); + } + + static bool no_titlebar = false; + static bool no_border = true; + static bool no_resize = false; + static bool no_move = false; + static bool no_scrollbar = false; + static bool no_collapse = false; + static bool no_menu = false; + + // Demonstrate the various window flags. Typically you would just use the default. + ImGuiWindowFlags window_flags = 0; + if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; + if (!no_border) window_flags |= ImGuiWindowFlags_ShowBorders; + if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; + if (no_move) window_flags |= ImGuiWindowFlags_NoMove; + if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; + if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; + if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; + ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("ImGui Demo", p_open, window_flags)) + { + // Early out if the window is collapsed, as an optimization. + ImGui::End(); + return; + } + + //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // 2/3 of the space for widget and 1/3 for labels + ImGui::PushItemWidth(-140); // Right align, keep 140 pixels for labels + + ImGui::Text("Dear ImGui says hello."); + + // Menu + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Menu")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Examples")) + { + ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); + ImGui::MenuItem("Console", NULL, &show_app_console); + ImGui::MenuItem("Log", NULL, &show_app_log); + ImGui::MenuItem("Simple layout", NULL, &show_app_layout); + ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); + ImGui::MenuItem("Long text display", NULL, &show_app_long_text); + ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); + ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); + ImGui::MenuItem("Simple overlay", NULL, &show_app_fixed_overlay); + ImGui::MenuItem("Manipulating window title", NULL, &show_app_manipulating_window_title); + ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Help")) + { + ImGui::MenuItem("Metrics", NULL, &show_app_metrics); + ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); + ImGui::MenuItem("About ImGui", NULL, &show_app_about); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + ImGui::Spacing(); + if (ImGui::CollapsingHeader("Help")) + { + ImGui::TextWrapped("This window is being created by the ShowTestWindow() function. Please refer to the code for programming reference.\n\nUser Guide:"); + ImGui::ShowUserGuide(); + } + + if (ImGui::CollapsingHeader("Window options")) + { + ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::SameLine(150); + ImGui::Checkbox("No border", &no_border); ImGui::SameLine(300); + ImGui::Checkbox("No resize", &no_resize); + ImGui::Checkbox("No move", &no_move); ImGui::SameLine(150); + ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::SameLine(300); + ImGui::Checkbox("No collapse", &no_collapse); + ImGui::Checkbox("No menu", &no_menu); + + if (ImGui::TreeNode("Style")) + { + ImGui::ShowStyleEditor(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Logging")) + { + ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded. You can also call ImGui::LogText() to output directly to the log without a visual output."); + ImGui::LogButtons(); + ImGui::TreePop(); + } + } + + if (ImGui::CollapsingHeader("Widgets")) + { + if (ImGui::TreeNode("Trees")) + { + if (ImGui::TreeNode("Basic trees")) + { + for (int i = 0; i < 5; i++) + if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) + { + ImGui::Text("blah blah"); + ImGui::SameLine(); + if (ImGui::SmallButton("print")) printf("Child %d pressed", i); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Advanced, with Selectable nodes")) + { + ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); + static bool align_label_with_current_x_position = false; + ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); + ImGui::Text("Hello!"); + if (align_label_with_current_x_position) + ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); + + static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. + int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. + ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. + for (int i = 0; i < 6; i++) + { + // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. + ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); + if (i < 3) + { + // Node + bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; + if (node_open) + { + ImGui::Text("Blah blah\nBlah Blah"); + ImGui::TreePop(); + } + } + else + { + // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). + ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags | ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen, "Selectable Leaf %d", i); + if (ImGui::IsItemClicked()) + node_clicked = i; + } + } + if (node_clicked != -1) + { + // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. + if (ImGui::GetIO().KeyCtrl) + selection_mask ^= (1 << node_clicked); // CTRL+click to toggle + else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection + selection_mask = (1 << node_clicked); // Click to single-select + } + ImGui::PopStyleVar(); + if (align_label_with_current_x_position) + ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Collapsing Headers")) + { + static bool closable_group = true; + if (ImGui::CollapsingHeader("Header")) + { + ImGui::Checkbox("Enable extra group", &closable_group); + for (int i = 0; i < 5; i++) + ImGui::Text("Some content %d", i); + } + if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) + { + for (int i = 0; i < 5; i++) + ImGui::Text("More content %d", i); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Bullets")) + { + ImGui::BulletText("Bullet point 1"); + ImGui::BulletText("Bullet point 2\nOn multiple lines"); + ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); + ImGui::Bullet(); ImGui::SmallButton("Button"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Colored Text")) + { + // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. + ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); + ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); + ImGui::TextDisabled("Disabled"); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Word Wrapping")) + { + // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. + ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); + ImGui::Spacing(); + + static float wrap_width = 200.0f; + ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); + + ImGui::Text("Test paragraph 1:"); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("lazy dog. This paragraph is made to fit within %.0f pixels. The quick brown fox jumps over the lazy dog.", wrap_width); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::Text("Test paragraph 2:"); + pos = ImGui::GetCursorScreenPos(); + ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); + ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); + ImGui::Text("aaaaaaaa bbbbbbbb, cccccccc,dddddddd. eeeeeeee ffffffff. gggggggg!hhhhhhhh"); + ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); + ImGui::PopTextWrapPos(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("UTF-8 Text")) + { + // UTF-8 test with Japanese characters + // (needs a suitable font, try Arial Unicode or M+ fonts http://mplus-fonts.sourceforge.jp/mplus-outline-fonts/index-en.html) + // Most compiler appears to support UTF-8 in source code (with Visual Studio you need to save your file as 'UTF-8 without signature') + // However for the sake for maximum portability here we are *not* including raw UTF-8 character in this source file, instead we encode the string with hexadecimal constants. + // In your own application be reasonable and use UTF-8 in source or retrieve the data from file system! + // Note that characters values are preserved even if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. + ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges."); + ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); + ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); + static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; + ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Images")) + { + ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); + ImVec2 tex_screen_pos = ImGui::GetCursorScreenPos(); + float tex_w = (float)ImGui::GetIO().Fonts->TexWidth; + float tex_h = (float)ImGui::GetIO().Fonts->TexHeight; + ImTextureID tex_id = ImGui::GetIO().Fonts->TexID; + ImGui::Text("%.0fx%.0f", tex_w, tex_h); + ImGui::Image(tex_id, ImVec2(tex_w, tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + float focus_sz = 32.0f; + float focus_x = ImGui::GetMousePos().x - tex_screen_pos.x - focus_sz * 0.5f; if (focus_x < 0.0f) focus_x = 0.0f; else if (focus_x > tex_w - focus_sz) focus_x = tex_w - focus_sz; + float focus_y = ImGui::GetMousePos().y - tex_screen_pos.y - focus_sz * 0.5f; if (focus_y < 0.0f) focus_y = 0.0f; else if (focus_y > tex_h - focus_sz) focus_y = tex_h - focus_sz; + ImGui::Text("Min: (%.2f, %.2f)", focus_x, focus_y); + ImGui::Text("Max: (%.2f, %.2f)", focus_x + focus_sz, focus_y + focus_sz); + ImVec2 uv0 = ImVec2((focus_x) / tex_w, (focus_y) / tex_h); + ImVec2 uv1 = ImVec2((focus_x + focus_sz) / tex_w, (focus_y + focus_sz) / tex_h); + ImGui::Image(tex_id, ImVec2(128,128), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::EndTooltip(); + } + ImGui::TextWrapped("And now some textured buttons.."); + static int pressed_count = 0; + for (int i = 0; i < 8; i++) + { + ImGui::PushID(i); + int frame_padding = -1 + i; // -1 = uses default padding + if (ImGui::ImageButton(tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/tex_w,32/tex_h), frame_padding, ImColor(0,0,0,255))) + pressed_count += 1; + ImGui::PopID(); + ImGui::SameLine(); + } + ImGui::NewLine(); + ImGui::Text("Pressed %d times.", pressed_count); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Selectables")) + { + if (ImGui::TreeNode("Basic")) + { + static bool selected[4] = { false, true, false, false }; + ImGui::Selectable("1. I am selectable", &selected[0]); + ImGui::Selectable("2. I am selectable", &selected[1]); + ImGui::Text("3. I am not selectable"); + ImGui::Selectable("4. I am selectable", &selected[2]); + if (ImGui::Selectable("5. I am double clickable", selected[3], ImGuiSelectableFlags_AllowDoubleClick)) + if (ImGui::IsMouseDoubleClicked(0)) + selected[3] = !selected[3]; + ImGui::TreePop(); + } + if (ImGui::TreeNode("Rendering more text into the same block")) + { + static bool selected[3] = { false, false, false }; + ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); + ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); + ImGui::TreePop(); + } + if (ImGui::TreeNode("In columns")) + { + ImGui::Columns(3, NULL, false); + static bool selected[16] = { 0 }; + for (int i = 0; i < 16; i++) + { + char label[32]; sprintf(label, "Item %d", i); + if (ImGui::Selectable(label, &selected[i])) {} + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Grid")) + { + static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; + for (int i = 0; i < 16; i++) + { + ImGui::PushID(i); + if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) + { + int x = i % 4, y = i / 4; + if (x > 0) selected[i - 1] ^= 1; + if (x < 3) selected[i + 1] ^= 1; + if (y > 0) selected[i - 4] ^= 1; + if (y < 3) selected[i + 4] ^= 1; + } + if ((i % 4) < 3) ImGui::SameLine(); + ImGui::PopID(); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Filtered Text Input")) + { + static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); + static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); + static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); + static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); + static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); + struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; + static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); + + ImGui::Text("Password input"); + static char bufpass[64] = "password123"; + ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); + ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); + ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Multi-line Text Input")) + { + static bool read_only = false; + static char text[1024*16] = + "/*\n" + " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" + " the hexadecimal encoding of one offending instruction,\n" + " more formally, the invalid operand with locked CMPXCHG8B\n" + " instruction bug, is a design flaw in the majority of\n" + " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" + " processors (all in the P5 microarchitecture).\n" + "*/\n\n" + "label:\n" + "\tlock cmpxchg8b eax\n"; + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + ImGui::Checkbox("Read-only", &read_only); + ImGui::PopStyleVar(); + ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0)); + ImGui::TreePop(); + } + + static bool a=false; + if (ImGui::Button("Button")) { printf("Clicked\n"); a ^= 1; } + if (a) + { + ImGui::SameLine(); + ImGui::Text("Thanks for clicking me!"); + } + + static bool check = true; + ImGui::Checkbox("checkbox", &check); + + static int e = 0; + ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); + ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); + ImGui::RadioButton("radio c", &e, 2); + + // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_Button, ImColor::HSV(i/7.0f, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor::HSV(i/7.0f, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor::HSV(i/7.0f, 0.8f, 0.8f)); + ImGui::Button("Click"); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } + + ImGui::Text("Hover over me"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip"); + + ImGui::SameLine(); + ImGui::Text("- or me"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::EndTooltip(); + } + + // Testing IMGUI_ONCE_UPON_A_FRAME macro + //for (int i = 0; i < 5; i++) + //{ + // IMGUI_ONCE_UPON_A_FRAME + // { + // ImGui::Text("This will be displayed only once."); + // } + //} + + ImGui::Separator(); + + ImGui::LabelText("label", "Value"); + + static int item = 1; + ImGui::Combo("combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); // Combo using values packed in a single constant string (for really quick combo) + + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK" }; + static int item2 = -1; + ImGui::Combo("combo scroll", &item2, items, IM_ARRAYSIZE(items)); // Combo using proper array. You can also pass a callback to retrieve array value, no need to create/copy an array just for that. + + { + static char str0[128] = "Hello, world!"; + static int i0=123; + static float f0=0.001f; + ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); + ImGui::SameLine(); ShowHelpMarker("Hold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n"); + + ImGui::InputInt("input int", &i0); + ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); + + ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); + + static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + ImGui::InputFloat3("input float3", vec4a); + } + + { + static int i1=50, i2=42; + ImGui::DragInt("drag int", &i1, 1); + ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); + + ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%.0f%%"); + + static float f1=1.00f, f2=0.0067f; + ImGui::DragFloat("drag float", &f1, 0.005f); + ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); + } + + { + static int i1=0; + ImGui::SliderInt("slider int", &i1, -1, 3); + ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); + + static float f1=0.123f, f2=0.0f; + ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); + ImGui::SliderFloat("slider log float", &f2, -10.0f, 10.0f, "%.4f", 3.0f); + static float angle = 0.0f; + ImGui::SliderAngle("slider angle", &angle); + } + + static float col1[3] = { 1.0f,0.0f,0.2f }; + static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; + ImGui::ColorEdit3("color 1", col1); + ImGui::SameLine(); ShowHelpMarker("Click on the colored square to change edit mode.\nCTRL+click on individual component to input value.\n"); + + ImGui::ColorEdit4("color 2", col2); + + const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; + static int listbox_item_current = 1; + ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + + //static int listbox_item_current2 = 2; + //ImGui::PushItemWidth(-1); + //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); + //ImGui::PopItemWidth(); + + if (ImGui::TreeNode("Range Widgets")) + { + ImGui::Unindent(); + + static float begin = 10, end = 90; + static int begin_i = 100, end_i = 1000; + ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); + ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %.0f units", "Max: %.0f units"); + + ImGui::Indent(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Multi-component Widgets")) + { + ImGui::Unindent(); + + static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; + static int vec4i[4] = { 1, 5, 100, 255 }; + + ImGui::InputFloat2("input float2", vec4f); + ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); + ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); + ImGui::InputInt2("input int2", vec4i); + ImGui::SliderInt2("slider int2", vec4i, 0, 255); + ImGui::Spacing(); + + if (ImGui::InputFloat3("input float3", vec4f)) {} + if (ImGui::InputFloat3("input float3 1", vec4f)){} + if (ImGui::InputFloat3("input float3 2", vec4f)){} + ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); + ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); + ImGui::InputInt3("input int3", vec4i); + ImGui::SliderInt3("slider int3", vec4i, 0, 255); + ImGui::Spacing(); + + ImGui::InputFloat4("input float4", vec4f); + ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); + ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); + ImGui::InputInt4("input int4", vec4i); + ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); + ImGui::SliderInt4("slider int4", vec4i, 0, 255); + + ImGui::Indent(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Vertical Sliders")) + { + ImGui::Unindent(); + const float spacing = 4; + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); + + static int int_value = 0; + ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); + ImGui::SameLine(); + + static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; + ImGui::PushID("set1"); + for (int i = 0; i < 7; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleColor(ImGuiCol_FrameBg, ImColor::HSV(i/7.0f, 0.5f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, ImColor::HSV(i/7.0f, 0.6f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_FrameBgActive, ImColor::HSV(i/7.0f, 0.7f, 0.5f)); + ImGui::PushStyleColor(ImGuiCol_SliderGrab, ImColor::HSV(i/7.0f, 0.9f, 0.9f)); + ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values[i]); + ImGui::PopStyleColor(4); + ImGui::PopID(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set2"); + static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; + const int rows = 3; + const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); + for (int nx = 0; nx < 4; nx++) + { + if (nx > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + for (int ny = 0; ny < rows; ny++) + { + ImGui::PushID(nx*rows+ny); + ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); + if (ImGui::IsItemActive() || ImGui::IsItemHovered()) + ImGui::SetTooltip("%.3f", values2[nx]); + ImGui::PopID(); + } + ImGui::EndGroup(); + } + ImGui::PopID(); + + ImGui::SameLine(); + ImGui::PushID("set3"); + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); + ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); + ImGui::PopStyleVar(); + ImGui::PopID(); + } + ImGui::PopID(); + ImGui::PopStyleVar(); + + ImGui::Indent(); + ImGui::TreePop(); + } + } + + if (ImGui::CollapsingHeader("Graphs widgets")) + { + static bool animate = true; + ImGui::Checkbox("Animate", &animate); + + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); + + // Create a dummy array of contiguous float values to plot + // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. + static float values[90] = { 0 }; + static int values_offset = 0; + if (animate) + { + static float refresh_time = ImGui::GetTime(); // Create dummy data at fixed 60 hz rate for the demo + for (; ImGui::GetTime() > refresh_time + 1.0f/60.0f; refresh_time += 1.0f/60.0f) + { + static float phase = 0.0f; + values[values_offset] = cosf(phase); + values_offset = (values_offset+1) % IM_ARRAYSIZE(values); + phase += 0.10f*values_offset; + } + } + ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); + + // Use functions to generate output + // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. + struct Funcs + { + static float Sin(void*, int i) { return sinf(i * 0.1f); } + static float Saw(void*, int i) { return (i & 1) ? 1.0f : 0.0f; } + }; + static int func_type = 0, display_count = 70; + ImGui::Separator(); + ImGui::PushItemWidth(100); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::PopItemWidth(); + ImGui::SameLine(); + ImGui::SliderInt("Sample count", &display_count, 1, 400); + float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; + ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); + ImGui::Separator(); + + // Animate a simple progress bar + static float progress = 0.0f, progress_dir = 1.0f; + if (animate) + { + progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; + if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } + if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } + } + + // Typically we would use ImVec2(-1.0f,0.0f) to use all available width, or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. + ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); + ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); + ImGui::Text("Progress Bar"); + + float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; + char buf[32]; + sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); + ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); + } + + if (ImGui::CollapsingHeader("Layout")) + { + if (ImGui::TreeNode("Child regions")) + { + ImGui::Text("Without border"); + static int line = 50; + bool goto_line = ImGui::Button("Goto"); + ImGui::SameLine(); + ImGui::PushItemWidth(100); + goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); + ImGui::PopItemWidth(); + ImGui::BeginChild("Sub1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f,300), false, ImGuiWindowFlags_HorizontalScrollbar); + for (int i = 0; i < 100; i++) + { + ImGui::Text("%04d: scrollable region", i); + if (goto_line && line == i) + ImGui::SetScrollHere(); + } + if (goto_line && line >= 100) + ImGui::SetScrollHere(); + ImGui::EndChild(); + + ImGui::SameLine(); + + ImGui::PushStyleVar(ImGuiStyleVar_ChildWindowRounding, 5.0f); + ImGui::BeginChild("Sub2", ImVec2(0,300), true); + ImGui::Text("With border"); + ImGui::Columns(2); + for (int i = 0; i < 100; i++) + { + if (i == 50) + ImGui::NextColumn(); + char buf[32]; + sprintf(buf, "%08x", i*5731); + ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); + } + ImGui::EndChild(); + ImGui::PopStyleVar(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Widgets Width")) + { + static float f = 0.0f; + ImGui::Text("PushItemWidth(100)"); + ImGui::SameLine(); ShowHelpMarker("Fixed width."); + ImGui::PushItemWidth(100); + ImGui::DragFloat("float##1", &f); + ImGui::PopItemWidth(); + + ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); + ImGui::SameLine(); ShowHelpMarker("Half of window width."); + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); + ImGui::DragFloat("float##2", &f); + ImGui::PopItemWidth(); + + ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); + ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); + ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); + ImGui::DragFloat("float##3", &f); + ImGui::PopItemWidth(); + + ImGui::Text("PushItemWidth(-100)"); + ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); + ImGui::PushItemWidth(-100); + ImGui::DragFloat("float##4", &f); + ImGui::PopItemWidth(); + + ImGui::Text("PushItemWidth(-1)"); + ImGui::SameLine(); ShowHelpMarker("Align to right edge"); + ImGui::PushItemWidth(-1); + ImGui::DragFloat("float##5", &f); + ImGui::PopItemWidth(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Basic Horizontal Layout")) + { + ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); + + // Text + ImGui::Text("Two items: Hello"); ImGui::SameLine(); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Adjust spacing + ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); + ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); + + // Button + ImGui::AlignFirstTextHeightToWidgets(); + ImGui::Text("Normal buttons"); ImGui::SameLine(); + ImGui::Button("Banana"); ImGui::SameLine(); + ImGui::Button("Apple"); ImGui::SameLine(); + ImGui::Button("Corniflower"); + + // Button + ImGui::Text("Small buttons"); ImGui::SameLine(); + ImGui::SmallButton("Like this one"); ImGui::SameLine(); + ImGui::Text("can fit within a text block."); + + // Aligned to arbitrary position. Easy/cheap column. + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::Text("x=150"); + ImGui::SameLine(300); ImGui::Text("x=300"); + ImGui::Text("Aligned"); + ImGui::SameLine(150); ImGui::SmallButton("x=150"); + ImGui::SameLine(300); ImGui::SmallButton("x=300"); + + // Checkbox + static bool c1=false,c2=false,c3=false,c4=false; + ImGui::Checkbox("My", &c1); ImGui::SameLine(); + ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); + ImGui::Checkbox("Is", &c3); ImGui::SameLine(); + ImGui::Checkbox("Rich", &c4); + + // Various + static float f0=1.0f, f1=2.0f, f2=3.0f; + ImGui::PushItemWidth(80); + const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; + static int item = -1; + ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); + ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); + ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); + ImGui::PopItemWidth(); + + ImGui::PushItemWidth(80); + ImGui::Text("Lists:"); + static int selection[4] = { 0, 1, 2, 3 }; + for (int i = 0; i < 4; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::PushID(i); + ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); + ImGui::PopID(); + //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + } + ImGui::PopItemWidth(); + + // Dummy + ImVec2 sz(30,30); + ImGui::Button("A", sz); ImGui::SameLine(); + ImGui::Dummy(sz); ImGui::SameLine(); + ImGui::Button("B", sz); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Groups")) + { + ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); + ImGui::BeginGroup(); + { + ImGui::BeginGroup(); + ImGui::Button("AAA"); + ImGui::SameLine(); + ImGui::Button("BBB"); + ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Button("CCC"); + ImGui::Button("DDD"); + ImGui::EndGroup(); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Group hovered"); + ImGui::SameLine(); + ImGui::Button("EEE"); + ImGui::EndGroup(); + } + // Capture the group size and create widgets using the same size + ImVec2 size = ImGui::GetItemRectSize(); + const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; + ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); + + ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::SameLine(); + ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); + ImGui::EndGroup(); + ImGui::SameLine(); + + ImGui::Button("LEVERAGE\nBUZZWORD", size); + ImGui::SameLine(); + + ImGui::ListBoxHeader("List", size); + ImGui::Selectable("Selected", true); + ImGui::Selectable("Not Selected", false); + ImGui::ListBoxFooter(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Text Baseline Alignment")) + { + ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); + + ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("One\nTwo\nThree"); + + ImGui::Button("HOP##1"); ImGui::SameLine(); + ImGui::Text("Banana"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("HOP##2"); ImGui::SameLine(); + ImGui::Text("Hello\nWorld"); ImGui::SameLine(); + ImGui::Text("Banana"); + + ImGui::Button("TEST##1"); ImGui::SameLine(); + ImGui::Text("TEST"); ImGui::SameLine(); + ImGui::SmallButton("TEST##2"); + + ImGui::AlignFirstTextHeightToWidgets(); // If your line starts with text, call this to align it to upcoming widgets. + ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); + ImGui::Button("Widget##1"); ImGui::SameLine(); + ImGui::Text("Widget"); ImGui::SameLine(); + ImGui::SmallButton("Widget##2"); + + // Tree + const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; + ImGui::Button("Button##1"); + ImGui::SameLine(0.0f, spacing); + if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + ImGui::AlignFirstTextHeightToWidgets(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). + bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); + if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data + + // Bullet + ImGui::Button("Button##3"); + ImGui::SameLine(0.0f, spacing); + ImGui::BulletText("Bullet text"); + + ImGui::AlignFirstTextHeightToWidgets(); + ImGui::BulletText("Node"); + ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Scrolling")) + { + ImGui::TextWrapped("(Use SetScrollHere() or SetScrollFromPosY() to scroll to a given position.)"); + static bool track = true; + static int track_line = 50, scroll_to_px = 200; + ImGui::Checkbox("Track", &track); + ImGui::PushItemWidth(100); + ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line %.0f"); + bool scroll_to = ImGui::Button("Scroll To"); + ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "y = %.0f px"); + ImGui::PopItemWidth(); + if (scroll_to) track = false; + + for (int i = 0; i < 5; i++) + { + if (i > 0) ImGui::SameLine(); + ImGui::BeginGroup(); + ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom"); + ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(ImGui::GetWindowWidth() * 0.17f, 200.0f), true); + if (scroll_to) + ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_px, i * 0.25f); + for (int line = 0; line < 100; line++) + { + if (track && line == track_line) + { + ImGui::TextColored(ImColor(255,255,0), "Line %d", line); + ImGui::SetScrollHere(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom + } + else + { + ImGui::Text("Line %d", line); + } + } + ImGui::EndChild(); + ImGui::EndGroup(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Horizontal Scrolling")) + { + ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); + ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); + static int lines = 7; + ImGui::SliderInt("Lines", &lines, 1, 15); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); + ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetItemsLineHeightWithSpacing()*7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); + for (int line = 0; line < lines; line++) + { + // Display random stuff + int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); + for (int n = 0; n < num_buttons; n++) + { + if (n > 0) ImGui::SameLine(); + ImGui::PushID(n + line * 1000); + char num_buf[16]; + const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : (sprintf(num_buf, "%d", n), num_buf); + float hue = n*0.05f; + ImGui::PushStyleColor(ImGuiCol_Button, ImColor::HSV(hue, 0.6f, 0.6f)); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImColor::HSV(hue, 0.7f, 0.7f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImColor::HSV(hue, 0.8f, 0.8f)); + ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); + ImGui::PopStyleColor(3); + ImGui::PopID(); + } + } + ImGui::EndChild(); + ImGui::PopStyleVar(2); + float scroll_x_delta = 0.0f; + ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; + ImGui::SameLine(); ImGui::Text("Scroll from code"); ImGui::SameLine(); + ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; + if (scroll_x_delta != 0.0f) + { + ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) + ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); + ImGui::End(); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Clipping")) + { + static ImVec2 size(100, 100), offset(50, 20); + ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); + ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); + ImGui::TextWrapped("(Click and drag)"); + ImVec2 pos = ImGui::GetCursorScreenPos(); + ImVec4 clip_rect(pos.x, pos.y, pos.x+size.x, pos.y+size.y); + ImGui::InvisibleButton("##dummy", size); + if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } + ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x+size.x,pos.y+size.y), ImColor(90,90,120,255)); + ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x+offset.x,pos.y+offset.y), ImColor(255,255,255,255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); + ImGui::TreePop(); + } + } + + if (ImGui::CollapsingHeader("Popups & Modal windows")) + { + if (ImGui::TreeNode("Popups")) + { + ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); + + static int selected_fish = -1; + const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; + static bool toggles[] = { true, false, false, false, false }; + + if (ImGui::Button("Select..")) + ImGui::OpenPopup("select"); + ImGui::SameLine(); + ImGui::Text(selected_fish == -1 ? "" : names[selected_fish]); + if (ImGui::BeginPopup("select")) + { + ImGui::Text("Aquarium"); + ImGui::Separator(); + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + if (ImGui::Selectable(names[i])) + selected_fish = i; + ImGui::EndPopup(); + } + + if (ImGui::Button("Toggle..")) + ImGui::OpenPopup("toggle"); + if (ImGui::BeginPopup("toggle")) + { + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + ImGui::MenuItem(names[i], "", &toggles[i]); + if (ImGui::BeginMenu("Sub-menu")) + { + ImGui::MenuItem("Click me"); + ImGui::EndMenu(); + } + + ImGui::Separator(); + ImGui::Text("Tooltip here"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("I am a tooltip over a popup"); + + if (ImGui::Button("Stacked Popup")) + ImGui::OpenPopup("another popup"); + if (ImGui::BeginPopup("another popup")) + { + for (int i = 0; i < IM_ARRAYSIZE(names); i++) + ImGui::MenuItem(names[i], "", &toggles[i]); + if (ImGui::BeginMenu("Sub-menu")) + { + ImGui::MenuItem("Click me"); + ImGui::EndMenu(); + } + ImGui::EndPopup(); + } + ImGui::EndPopup(); + } + + if (ImGui::Button("Popup Menu..")) + ImGui::OpenPopup("popup from button"); + if (ImGui::BeginPopup("popup from button")) + { + ShowExampleMenuFile(); + ImGui::EndPopup(); + } + + ImGui::Spacing(); + ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); + ImGui::Separator(); + // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. + // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here + // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. + ImGui::PushID("foo"); + ImGui::MenuItem("Menu item", "CTRL+M"); + if (ImGui::BeginMenu("Menu inside a regular window")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::PopID(); + ImGui::Separator(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Context menus")) + { + static float value = 0.5f; + ImGui::Text("Value = %.3f (<-- right-click here)", value); + if (ImGui::BeginPopupContextItem("item context menu")) + { + if (ImGui::Selectable("Set to zero")) value = 0.0f; + if (ImGui::Selectable("Set to PI")) value = 3.1415f; + ImGui::EndPopup(); + } + + static ImVec4 color = ImColor(0.8f, 0.5f, 1.0f, 1.0f); + ImGui::ColorButton(color); + if (ImGui::BeginPopupContextItem("color context menu")) + { + ImGui::Text("Edit color"); + ImGui::ColorEdit3("##edit", (float*)&color); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Modals")) + { + ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); + + if (ImGui::Button("Delete..")) + ImGui::OpenPopup("Delete?"); + if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); + ImGui::Separator(); + + //static int dummy_i = 0; + //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); + + static bool dont_ask_me_next_time = false; + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); + ImGui::PopStyleVar(); + + if (ImGui::Button("OK", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } + ImGui::SameLine(); + if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } + ImGui::EndPopup(); + } + + if (ImGui::Button("Stacked modals..")) + ImGui::OpenPopup("Stacked 1"); + if (ImGui::BeginPopupModal("Stacked 1")) + { + ImGui::Text("Hello from Stacked The First"); + + if (ImGui::Button("Another one..")) + ImGui::OpenPopup("Stacked 2"); + if (ImGui::BeginPopupModal("Stacked 2")) + { + ImGui::Text("Hello from Stacked The Second"); + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + + if (ImGui::Button("Close")) + ImGui::CloseCurrentPopup(); + ImGui::EndPopup(); + } + + ImGui::TreePop(); + } + } + + if (ImGui::CollapsingHeader("Columns")) + { + // Basic columns + if (ImGui::TreeNode("Basic")) + { + ImGui::Text("Without border:"); + ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border + ImGui::Separator(); + for (int n = 0; n < 14; n++) + { + char label[32]; + sprintf(label, "Item %d", n); + if (ImGui::Selectable(label)) {} + //if (ImGui::Button(label, ImVec2(-1,0))) {} + ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + + ImGui::Text("With border:"); + ImGui::Columns(4, "mycolumns"); // 4-ways, with border + ImGui::Separator(); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Text("Flags"); ImGui::NextColumn(); + ImGui::Separator(); + const char* names[3] = { "One", "Two", "Three" }; + const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; + static int selected = -1; + for (int i = 0; i < 3; i++) + { + char label[32]; + sprintf(label, "%04d", i); + if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) + selected = i; + ImGui::NextColumn(); + ImGui::Text(names[i]); ImGui::NextColumn(); + ImGui::Text(paths[i]); ImGui::NextColumn(); + ImGui::Text("...."); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + // Scrolling columns + /* + if (ImGui::TreeNode("Scrolling")) + { + ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); + ImGui::Columns(3); + ImGui::Text("ID"); ImGui::NextColumn(); + ImGui::Text("Name"); ImGui::NextColumn(); + ImGui::Text("Path"); ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::EndChild(); + ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); + ImGui::Columns(3); + for (int i = 0; i < 10; i++) + { + ImGui::Text("%04d", i); ImGui::NextColumn(); + ImGui::Text("Foobar"); ImGui::NextColumn(); + ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); + } + ImGui::Columns(1); + ImGui::EndChild(); + ImGui::TreePop(); + } + */ + + // Create multiple items in a same cell before switching to next column + if (ImGui::TreeNode("Mixed items")) + { + ImGui::Columns(3, "mixed"); + ImGui::Separator(); + + ImGui::Text("Hello"); + ImGui::Button("Banana"); + ImGui::NextColumn(); + + ImGui::Text("ImGui"); + ImGui::Button("Apple"); + static float foo = 1.0f; + ImGui::InputFloat("red", &foo, 0.05f, 0, 3); + ImGui::Text("An extra line here."); + ImGui::NextColumn(); + + ImGui::Text("Sailor"); + ImGui::Button("Corniflower"); + static float bar = 1.0f; + ImGui::InputFloat("blue", &bar, 0.05f, 0, 3); + ImGui::NextColumn(); + + if (ImGui::CollapsingHeader("Category A")) ImGui::Text("Blah blah blah"); + ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category B")) ImGui::Text("Blah blah blah"); + ImGui::NextColumn(); + if (ImGui::CollapsingHeader("Category C")) ImGui::Text("Blah blah blah"); + ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + // Word wrapping + if (ImGui::TreeNode("Word-wrapping")) + { + ImGui::Columns(2, "word-wrapping"); + ImGui::Separator(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Left"); + ImGui::NextColumn(); + ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); + ImGui::TextWrapped("Hello Right"); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Borders")) + { + static bool h_borders = true; + static bool v_borders = true; + ImGui::Checkbox("horizontal", &h_borders); + ImGui::SameLine(); + ImGui::Checkbox("vertical", &v_borders); + ImGui::Columns(4, NULL, v_borders); + if (h_borders) ImGui::Separator(); + for (int i = 0; i < 8; i++) + { + ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); + ImGui::NextColumn(); + } + ImGui::Columns(1); + if (h_borders) ImGui::Separator(); + ImGui::TreePop(); + } + + bool node_open = ImGui::TreeNode("Tree within single cell"); + ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell.\nThere's no storage of state per-cell."); + if (node_open) + { + ImGui::Columns(2, "tree items"); + ImGui::Separator(); + if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); + if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); + ImGui::Columns(1); + ImGui::Separator(); + ImGui::TreePop(); + } + } + + if (ImGui::CollapsingHeader("Filtering")) + { + static ImGuiTextFilter filter; + ImGui::Text("Filter usage:\n" + " \"\" display all lines\n" + " \"xxx\" display lines containing \"xxx\"\n" + " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" + " \"-xxx\" hide lines containing \"xxx\""); + filter.Draw(); + const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; + for (int i = 0; i < IM_ARRAYSIZE(lines); i++) + if (filter.PassFilter(lines[i])) + ImGui::BulletText("%s", lines[i]); + } + + if (ImGui::CollapsingHeader("Keyboard, Mouse & Focus")) + { + if (ImGui::TreeNode("Tabbing")) + { + ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); + static char buf[32] = "dummy"; + ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); + ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); + ImGui::PushAllowKeyboardFocus(false); + ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); + //ImGui::SameLine(); ShowHelperMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); + ImGui::PopAllowKeyboardFocus(); + ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Focus from code")) + { + bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); + bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine(); + bool focus_3 = ImGui::Button("Focus on 3"); + int has_focus = 0; + static char buf[128] = "click on a button to set focus"; + + if (focus_1) ImGui::SetKeyboardFocusHere(); + ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); + if (ImGui::IsItemActive()) has_focus = 1; + + if (focus_2) ImGui::SetKeyboardFocusHere(); + ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); + if (ImGui::IsItemActive()) has_focus = 2; + + ImGui::PushAllowKeyboardFocus(false); + if (focus_3) ImGui::SetKeyboardFocusHere(); + ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); + if (ImGui::IsItemActive()) has_focus = 3; + ImGui::PopAllowKeyboardFocus(); + if (has_focus) + ImGui::Text("Item with focus: %d", has_focus); + else + ImGui::Text("Item with focus: "); + ImGui::TextWrapped("Cursor & selection are preserved when refocusing last used item in code."); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Dragging")) + { + ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); + ImGui::Button("Drag Me"); + if (ImGui::IsItemActive()) + { + // Draw a line between the button and the mouse cursor + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->PushClipRectFullScreen(); + draw_list->AddLine(ImGui::CalcItemRectClosestPoint(ImGui::GetIO().MousePos, true, -2.0f), ImGui::GetIO().MousePos, ImColor(ImGui::GetStyle().Colors[ImGuiCol_Button]), 4.0f); + draw_list->PopClipRect(); + ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); + ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); + ImVec2 mouse_delta = ImGui::GetIO().MouseDelta; + ImGui::SameLine(); ImGui::Text("Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f), MouseDelta (%.1f, %.1f)", value_raw.x, value_raw.y, value_with_lock_threshold.x, value_with_lock_threshold.y, mouse_delta.x, mouse_delta.y); + } + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Keyboard & Mouse State")) + { + ImGuiIO& io = ImGui::GetIO(); + + ImGui::Text("MousePos: (%g, %g)", io.MousePos.x, io.MousePos.y); + ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } + ImGui::Text("MouseWheel: %.1f", io.MouseWheel); + + ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } + ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } + ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } + ImGui::Text("KeyMods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + + ImGui::Text("WantCaptureMouse: %s", io.WantCaptureMouse ? "true" : "false"); + ImGui::Text("WantCaptureKeyboard: %s", io.WantCaptureKeyboard ? "true" : "false"); + ImGui::Text("WantTextInput: %s", io.WantTextInput ? "true" : "false"); + + ImGui::Button("Hovering me sets the\nkeyboard capture flag"); + if (ImGui::IsItemHovered()) + ImGui::CaptureKeyboardFromApp(true); + ImGui::SameLine(); + ImGui::Button("Holding me clears the\nthe keyboard capture flag"); + if (ImGui::IsItemActive()) + ImGui::CaptureKeyboardFromApp(false); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Mouse cursors")) + { + ImGui::TextWrapped("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. You can also set io.MouseDrawCursor to ask ImGui to render the cursor for you in software."); + ImGui::Checkbox("io.MouseDrawCursor", &ImGui::GetIO().MouseDrawCursor); + ImGui::Text("Hover to see mouse cursors:"); + for (int i = 0; i < ImGuiMouseCursor_Count_; i++) + { + char label[32]; + sprintf(label, "Mouse cursor %d", i); + ImGui::Bullet(); ImGui::Selectable(label, false); + if (ImGui::IsItemHovered()) + ImGui::SetMouseCursor(i); + } + ImGui::TreePop(); + } + } + + ImGui::End(); +} + +void ImGui::ShowStyleEditor(ImGuiStyle* ref) +{ + ImGuiStyle& style = ImGui::GetStyle(); + + // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it compares to the default style) + const ImGuiStyle default_style; // Default style + if (ImGui::Button("Revert Style")) + style = ref ? *ref : default_style; + + if (ref) + { + ImGui::SameLine(); + if (ImGui::Button("Save Style")) + *ref = style; + } + + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.55f); + + if (ImGui::TreeNode("Rendering")) + { + ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); + ImGui::Checkbox("Anti-aliased shapes", &style.AntiAliasedShapes); + ImGui::PushItemWidth(100); + ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, NULL, 2.0f); + if (style.CurveTessellationTol < 0.0f) style.CurveTessellationTol = 0.10f; + ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. + ImGui::PopItemWidth(); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Settings")) + { + ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat("ChildWindowRounding", &style.ChildWindowRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); + ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); + ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 16.0f, "%.0f"); + ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); + ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 16.0f, "%.0f"); + ImGui::Text("Alignment"); + ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); + ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Colors")) + { + static int output_dest = 0; + static bool output_only_modified = false; + if (ImGui::Button("Copy Colors")) + { + if (output_dest == 0) + ImGui::LogToClipboard(); + else + ImGui::LogToTTY(); + ImGui::LogText("ImGuiStyle& style = ImGui::GetStyle();" IM_NEWLINE); + for (int i = 0; i < ImGuiCol_COUNT; i++) + { + const ImVec4& col = style.Colors[i]; + const char* name = ImGui::GetStyleColName(i); + if (!output_only_modified || memcmp(&col, (ref ? &ref->Colors[i] : &default_style.Colors[i]), sizeof(ImVec4)) != 0) + ImGui::LogText("style.Colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 22 - (int)strlen(name), "", col.x, col.y, col.z, col.w); + } + ImGui::LogFinish(); + } + ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); + ImGui::SameLine(); ImGui::Checkbox("Only Modified Fields", &output_only_modified); + + static ImGuiColorEditMode edit_mode = ImGuiColorEditMode_RGB; + ImGui::RadioButton("RGB", &edit_mode, ImGuiColorEditMode_RGB); + ImGui::SameLine(); + ImGui::RadioButton("HSV", &edit_mode, ImGuiColorEditMode_HSV); + ImGui::SameLine(); + ImGui::RadioButton("HEX", &edit_mode, ImGuiColorEditMode_HEX); + //ImGui::Text("Tip: Click on colored square to change edit mode."); + + static ImGuiTextFilter filter; + filter.Draw("Filter colors", 200); + + ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar); + ImGui::PushItemWidth(-160); + ImGui::ColorEditMode(edit_mode); + for (int i = 0; i < ImGuiCol_COUNT; i++) + { + const char* name = ImGui::GetStyleColName(i); + if (!filter.PassFilter(name)) + continue; + ImGui::PushID(i); + ImGui::ColorEdit4(name, (float*)&style.Colors[i], true); + if (memcmp(&style.Colors[i], (ref ? &ref->Colors[i] : &default_style.Colors[i]), sizeof(ImVec4)) != 0) + { + ImGui::SameLine(); if (ImGui::Button("Revert")) style.Colors[i] = ref ? ref->Colors[i] : default_style.Colors[i]; + if (ref) { ImGui::SameLine(); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; } + } + ImGui::PopID(); + } + ImGui::PopItemWidth(); + ImGui::EndChild(); + + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size)) + { + ImGui::SameLine(); ShowHelpMarker("Tip: Load fonts with io.Fonts->AddFontFromFileTTF()\nbefore calling io.Fonts->GetTex* functions."); + ImFontAtlas* atlas = ImGui::GetIO().Fonts; + if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) + { + ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); + ImGui::TreePop(); + } + ImGui::PushItemWidth(100); + for (int i = 0; i < atlas->Fonts.Size; i++) + { + ImFont* font = atlas->Fonts[i]; + ImGui::BulletText("Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); + ImGui::TreePush((void*)(intptr_t)i); + if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { atlas->Fonts[i] = atlas->Fonts[0]; atlas->Fonts[0] = font; } } + ImGui::PushFont(font); + ImGui::Text("The quick brown fox jumps over the lazy dog"); + ImGui::PopFont(); + if (ImGui::TreeNode("Details")) + { + ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font + ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); + ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); + ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); + for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) + { + ImFontConfig* cfg = &font->ConfigData[config_i]; + ImGui::BulletText("Input %d: \'%s\'\nOversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); + } + if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) + { + // Display all glyphs of the fonts in separate pages of 256 characters + const ImFont::Glyph* glyph_fallback = font->FallbackGlyph; // Forcefully/dodgily make FindGlyph() return NULL on fallback, which isn't the default behavior. + font->FallbackGlyph = NULL; + for (int base = 0; base < 0x10000; base += 256) + { + int count = 0; + for (int n = 0; n < 256; n++) + count += font->FindGlyph((ImWchar)(base + n)) ? 1 : 0; + if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base+255, count, count > 1 ? "glyphs" : "glyph")) + { + float cell_spacing = style.ItemSpacing.y; + ImVec2 cell_size(font->FontSize * 1, font->FontSize * 1); + ImVec2 base_pos = ImGui::GetCursorScreenPos(); + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + for (int n = 0; n < 256; n++) + { + ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size.x + cell_spacing), base_pos.y + (n / 16) * (cell_size.y + cell_spacing)); + ImVec2 cell_p2(cell_p1.x + cell_size.x, cell_p1.y + cell_size.y); + const ImFont::Glyph* glyph = font->FindGlyph((ImWchar)(base+n));; + draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255,255,255,100) : IM_COL32(255,255,255,50)); + font->RenderChar(draw_list, cell_size.x, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base+n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. + if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) + { + ImGui::BeginTooltip(); + ImGui::Text("Codepoint: U+%04X", base+n); + ImGui::Separator(); + ImGui::Text("XAdvance+1: %.1f", glyph->XAdvance); + ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); + ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); + ImGui::EndTooltip(); + } + } + ImGui::Dummy(ImVec2((cell_size.x + cell_spacing) * 16, (cell_size.y + cell_spacing) * 16)); + ImGui::TreePop(); + } + } + font->FallbackGlyph = glyph_fallback; + ImGui::TreePop(); + } + ImGui::TreePop(); + } + ImGui::TreePop(); + } + static float window_scale = 1.0f; + ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window + ImGui::DragFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale everything + ImGui::PopItemWidth(); + ImGui::SetWindowFontScale(window_scale); + ImGui::TreePop(); + } + + ImGui::PopItemWidth(); +} + +static void ShowExampleAppMainMenuBar() +{ + if (ImGui::BeginMainMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Edit")) + { + if (ImGui::MenuItem("Undo", "CTRL+Z")) {} + if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item + ImGui::Separator(); + if (ImGui::MenuItem("Cut", "CTRL+X")) {} + if (ImGui::MenuItem("Copy", "CTRL+C")) {} + if (ImGui::MenuItem("Paste", "CTRL+V")) {} + ImGui::EndMenu(); + } + ImGui::EndMainMenuBar(); + } +} + +static void ShowExampleMenuFile() +{ + ImGui::MenuItem("(dummy menu)", NULL, false, false); + if (ImGui::MenuItem("New")) {} + if (ImGui::MenuItem("Open", "Ctrl+O")) {} + if (ImGui::BeginMenu("Open Recent")) + { + ImGui::MenuItem("fish_hat.c"); + ImGui::MenuItem("fish_hat.inl"); + ImGui::MenuItem("fish_hat.h"); + if (ImGui::BeginMenu("More..")) + { + ImGui::MenuItem("Hello"); + ImGui::MenuItem("Sailor"); + if (ImGui::BeginMenu("Recurse..")) + { + ShowExampleMenuFile(); + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + ImGui::EndMenu(); + } + if (ImGui::MenuItem("Save", "Ctrl+S")) {} + if (ImGui::MenuItem("Save As..")) {} + ImGui::Separator(); + if (ImGui::BeginMenu("Options")) + { + static bool enabled = true; + ImGui::MenuItem("Enabled", "", &enabled); + ImGui::BeginChild("child", ImVec2(0, 60), true); + for (int i = 0; i < 10; i++) + ImGui::Text("Scrolling Text %d", i); + ImGui::EndChild(); + static float f = 0.5f; + static int n = 0; + ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); + ImGui::InputFloat("Input", &f, 0.1f); + ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Colors")) + { + for (int i = 0; i < ImGuiCol_COUNT; i++) + ImGui::MenuItem(ImGui::GetStyleColName((ImGuiCol)i)); + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Disabled", false)) // Disabled + { + IM_ASSERT(0); + } + if (ImGui::MenuItem("Checked", NULL, true)) {} + if (ImGui::MenuItem("Quit", "Alt+F4")) {} +} + +static void ShowExampleAppAutoResize(bool* p_open) +{ + if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::End(); + return; + } + + static int lines = 10; + ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop."); + ImGui::SliderInt("Number of lines", &lines, 1, 20); + for (int i = 0; i < lines; i++) + ImGui::Text("%*sThis is line %d", i*4, "", i); // Pad with space to extend size horizontally + ImGui::End(); +} + +static void ShowExampleAppConstrainedResize(bool* p_open) +{ + struct CustomConstraints // Helper functions to demonstrate programmatic constraints + { + static void Square(ImGuiSizeConstraintCallbackData* data) { data->DesiredSize = ImVec2(IM_MAX(data->DesiredSize.x, data->DesiredSize.y), IM_MAX(data->DesiredSize.x, data->DesiredSize.y)); } + static void Step(ImGuiSizeConstraintCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } + }; + + static int type = 0; + if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only + if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only + if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 + if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(300, 0), ImVec2(400, FLT_MAX)); // Width 300-400 + if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square + if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step + + if (ImGui::Begin("Example: Constrained Resize", p_open)) + { + const char* desc[] = + { + "Resize vertical only", + "Resize horizontal only", + "Width > 100, Height > 100", + "Width 300-400", + "Custom: Always Square", + "Custom: Fixed Steps (100)", + }; + ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); + if (ImGui::Button("200x200")) ImGui::SetWindowSize(ImVec2(200,200)); + ImGui::SameLine(); + if (ImGui::Button("500x500")) ImGui::SetWindowSize(ImVec2(500,500)); + ImGui::SameLine(); + if (ImGui::Button("800x200")) ImGui::SetWindowSize(ImVec2(800,200)); + for (int i = 0; i < 10; i++) + ImGui::Text("Hello, sailor! Making this line long enough for the example."); + } + ImGui::End(); +} + +static void ShowExampleAppFixedOverlay(bool* p_open) +{ + ImGui::SetNextWindowPos(ImVec2(10,10)); + if (!ImGui::Begin("Example: Fixed Overlay", p_open, ImVec2(0,0), 0.3f, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoSavedSettings)) + { + ImGui::End(); + return; + } + ImGui::Text("Simple overlay\non the top-left side of the screen."); + ImGui::Separator(); + ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); + ImGui::End(); +} + +static void ShowExampleAppManipulatingWindowTitle(bool*) +{ + // By default, Windows are uniquely identified by their title. + // You can use the "##" and "###" markers to manipulate the display/ID. Read FAQ at the top of this file! + + // Using "##" to display same title but have unique identifier. + ImGui::SetNextWindowPos(ImVec2(100,100), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##1"); + ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); + ImGui::End(); + + ImGui::SetNextWindowPos(ImVec2(100,200), ImGuiSetCond_FirstUseEver); + ImGui::Begin("Same title as another window##2"); + ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); + ImGui::End(); + + // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" + char buf[128]; + sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime()/0.25f)&3], rand()); + ImGui::SetNextWindowPos(ImVec2(100,300), ImGuiSetCond_FirstUseEver); + ImGui::Begin(buf); + ImGui::Text("This window has a changing title."); + ImGui::End(); +} + +static void ShowExampleAppCustomRendering(bool* p_open) +{ + ImGui::SetNextWindowSize(ImVec2(350,560), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("Example: Custom rendering", p_open)) + { + ImGui::End(); + return; + } + + // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc. + // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4. + // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types) + // In this example we are not using the maths operators! + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + + // Primitives + ImGui::Text("Primitives"); + static float sz = 36.0f; + static ImVec4 col = ImVec4(1.0f,1.0f,0.4f,1.0f); + ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); + ImGui::ColorEdit3("Color", &col.x); + { + const ImVec2 p = ImGui::GetCursorScreenPos(); + const ImU32 col32 = ImColor(col); + float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; + for (int n = 0; n < 2; n++) + { + float thickness = (n == 0) ? 1.0f : 4.0f; + draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, thickness); x += sz+spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ~0, thickness); x += sz+spacing; + draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ~0, thickness); x += sz+spacing; + draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, thickness); x += sz+spacing; + draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, thickness); x += sz+spacing; + draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, thickness); x += sz+spacing; + draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, thickness); x += spacing; + draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, thickness); + x = p.x + 4; + y += sz+spacing; + } + draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 32); x += sz+spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32); x += sz+spacing; + draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing; + draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing; + draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), ImColor(0,0,0), ImColor(255,0,0), ImColor(255,255,0), ImColor(0,255,0)); + ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3)); + } + ImGui::Separator(); + { + static ImVector points; + static bool adding_line = false; + ImGui::Text("Canvas example"); + if (ImGui::Button("Clear")) points.clear(); + if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } + ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); + + // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() + // However you can draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). + // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). + ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! + ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available + if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; + if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; + draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), ImColor(50,50,50), ImColor(50,50,60), ImColor(60,60,70), ImColor(50,50,60)); + draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), ImColor(255,255,255)); + + bool adding_preview = false; + ImGui::InvisibleButton("canvas", canvas_size); + if (ImGui::IsItemHovered()) + { + ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); + if (!adding_line && ImGui::IsMouseClicked(0)) + { + points.push_back(mouse_pos_in_canvas); + adding_line = true; + } + if (adding_line) + { + adding_preview = true; + points.push_back(mouse_pos_in_canvas); + if (!ImGui::GetIO().MouseDown[0]) + adding_line = adding_preview = false; + } + if (ImGui::IsMouseClicked(1) && !points.empty()) + { + adding_line = adding_preview = false; + points.pop_back(); + points.pop_back(); + } + } + draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y)); // clip lines within the canvas (if we resize it, etc.) + for (int i = 0; i < points.Size - 1; i += 2) + draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), IM_COL32(255,255,0,255), 2.0f); + draw_list->PopClipRect(); + if (adding_preview) + points.pop_back(); + } + ImGui::End(); +} + +// For the console example, here we are using a more C++ like approach of declaring a class to hold the data and the functions. +struct ExampleAppConsole +{ + char InputBuf[256]; + ImVector Items; + bool ScrollToBottom; + ImVector History; + int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. + ImVector Commands; + + ExampleAppConsole() + { + ClearLog(); + memset(InputBuf, 0, sizeof(InputBuf)); + HistoryPos = -1; + Commands.push_back("HELP"); + Commands.push_back("HISTORY"); + Commands.push_back("CLEAR"); + Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches. + AddLog("Welcome to ImGui!"); + } + ~ExampleAppConsole() + { + ClearLog(); + for (int i = 0; i < History.Size; i++) + free(History[i]); + } + + // Portable helpers + static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } + static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; } + static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buff = malloc(len); return (char*)memcpy(buff, (const void*)str, len); } + + void ClearLog() + { + for (int i = 0; i < Items.Size; i++) + free(Items[i]); + Items.clear(); + ScrollToBottom = true; + } + + void AddLog(const char* fmt, ...) IM_PRINTFARGS(2) + { + char buf[1024]; + va_list args; + va_start(args, fmt); + vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); + buf[IM_ARRAYSIZE(buf)-1] = 0; + va_end(args); + Items.push_back(Strdup(buf)); + ScrollToBottom = true; + } + + void Draw(const char* title, bool* p_open) + { + ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin(title, p_open)) + { + ImGui::End(); + return; + } + + ImGui::TextWrapped("This example implements a console with basic coloring, completion and history. A more elaborate implementation may want to store entries along with extra data such as timestamp, emitter, etc."); + ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion."); + + // TODO: display items starting from the bottom + + if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } + ImGui::SameLine(); + if (ImGui::SmallButton("Add Dummy Error")) AddLog("[error] something went wrong"); + ImGui::SameLine(); + if (ImGui::SmallButton("Clear")) ClearLog(); + ImGui::SameLine(); + if (ImGui::SmallButton("Scroll to bottom")) ScrollToBottom = true; + //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } + + ImGui::Separator(); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); + static ImGuiTextFilter filter; + filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); + ImGui::PopStyleVar(); + ImGui::Separator(); + + ImGui::BeginChild("ScrollingRegion", ImVec2(0,-ImGui::GetItemsLineHeightWithSpacing()), false, ImGuiWindowFlags_HorizontalScrollbar); + if (ImGui::BeginPopupContextWindow()) + { + if (ImGui::Selectable("Clear")) ClearLog(); + ImGui::EndPopup(); + } + + // Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); + // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping to only process visible items. + // You can seek and display only the lines that are visible using the ImGuiListClipper helper, if your elements are evenly spaced and you have cheap random access to the elements. + // To use the clipper we could replace the 'for (int i = 0; i < Items.Size; i++)' loop with: + // ImGuiListClipper clipper(Items.Size); + // while (clipper.Step()) + // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + // However take note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list. + // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices that passed the filtering test, recomputing this array when user changes the filter, + // and appending newly elements as they are inserted. This is left as a task to the user until we can manage to improve this example code! + // If your items are of variable size you may want to implement code similar to what ImGuiListClipper does. Or split your data into fixed height items to allow random-seeking into your list. + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing + for (int i = 0; i < Items.Size; i++) + { + const char* item = Items[i]; + if (!filter.PassFilter(item)) + continue; + ImVec4 col = ImVec4(1.0f,1.0f,1.0f,1.0f); // A better implementation may store a type per-item. For the sample let's just parse the text. + if (strstr(item, "[error]")) col = ImColor(1.0f,0.4f,0.4f,1.0f); + else if (strncmp(item, "# ", 2) == 0) col = ImColor(1.0f,0.78f,0.58f,1.0f); + ImGui::PushStyleColor(ImGuiCol_Text, col); + ImGui::TextUnformatted(item); + ImGui::PopStyleColor(); + } + if (ScrollToBottom) + ImGui::SetScrollHere(); + ScrollToBottom = false; + ImGui::PopStyleVar(); + ImGui::EndChild(); + ImGui::Separator(); + + // Command-line + if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) + { + char* input_end = InputBuf+strlen(InputBuf); + while (input_end > InputBuf && input_end[-1] == ' ') input_end--; + *input_end = 0; + if (InputBuf[0]) + ExecCommand(InputBuf); + strcpy(InputBuf, ""); + } + + // Demonstrate keeping auto focus on the input box + if (ImGui::IsItemHovered() || (ImGui::IsRootWindowOrAnyChildFocused() && !ImGui::IsAnyItemActive() && !ImGui::IsMouseClicked(0))) + ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget + + ImGui::End(); + } + + void ExecCommand(const char* command_line) + { + AddLog("# %s\n", command_line); + + // Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal. + HistoryPos = -1; + for (int i = History.Size-1; i >= 0; i--) + if (Stricmp(History[i], command_line) == 0) + { + free(History[i]); + History.erase(History.begin() + i); + break; + } + History.push_back(Strdup(command_line)); + + // Process command + if (Stricmp(command_line, "CLEAR") == 0) + { + ClearLog(); + } + else if (Stricmp(command_line, "HELP") == 0) + { + AddLog("Commands:"); + for (int i = 0; i < Commands.Size; i++) + AddLog("- %s", Commands[i]); + } + else if (Stricmp(command_line, "HISTORY") == 0) + { + for (int i = History.Size >= 10 ? History.Size - 10 : 0; i < History.Size; i++) + AddLog("%3d: %s\n", i, History[i]); + } + else + { + AddLog("Unknown command: '%s'\n", command_line); + } + } + + static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks + { + ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; + return console->TextEditCallback(data); + } + + int TextEditCallback(ImGuiTextEditCallbackData* data) + { + //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); + switch (data->EventFlag) + { + case ImGuiInputTextFlags_CallbackCompletion: + { + // Example of TEXT COMPLETION + + // Locate beginning of current word + const char* word_end = data->Buf + data->CursorPos; + const char* word_start = word_end; + while (word_start > data->Buf) + { + const char c = word_start[-1]; + if (c == ' ' || c == '\t' || c == ',' || c == ';') + break; + word_start--; + } + + // Build a list of candidates + ImVector candidates; + for (int i = 0; i < Commands.Size; i++) + if (Strnicmp(Commands[i], word_start, (int)(word_end-word_start)) == 0) + candidates.push_back(Commands[i]); + + if (candidates.Size == 0) + { + // No match + AddLog("No match for \"%.*s\"!\n", (int)(word_end-word_start), word_start); + } + else if (candidates.Size == 1) + { + // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing + data->DeleteChars((int)(word_start-data->Buf), (int)(word_end-word_start)); + data->InsertChars(data->CursorPos, candidates[0]); + data->InsertChars(data->CursorPos, " "); + } + else + { + // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY" + int match_len = (int)(word_end - word_start); + for (;;) + { + int c = 0; + bool all_candidates_matches = true; + for (int i = 0; i < candidates.Size && all_candidates_matches; i++) + if (i == 0) + c = toupper(candidates[i][match_len]); + else if (c != toupper(candidates[i][match_len])) + all_candidates_matches = false; + if (!all_candidates_matches) + break; + match_len++; + } + + if (match_len > 0) + { + data->DeleteChars((int)(word_start - data->Buf), (int)(word_end-word_start)); + data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); + } + + // List matches + AddLog("Possible matches:\n"); + for (int i = 0; i < candidates.Size; i++) + AddLog("- %s\n", candidates[i]); + } + + break; + } + case ImGuiInputTextFlags_CallbackHistory: + { + // Example of HISTORY + const int prev_history_pos = HistoryPos; + if (data->EventKey == ImGuiKey_UpArrow) + { + if (HistoryPos == -1) + HistoryPos = History.Size - 1; + else if (HistoryPos > 0) + HistoryPos--; + } + else if (data->EventKey == ImGuiKey_DownArrow) + { + if (HistoryPos != -1) + if (++HistoryPos >= History.Size) + HistoryPos = -1; + } + + // A better implementation would preserve the data on the current input line along with cursor position. + if (prev_history_pos != HistoryPos) + { + data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); + data->BufDirty = true; + } + } + } + return 0; + } +}; + +static void ShowExampleAppConsole(bool* p_open) +{ + static ExampleAppConsole console; + console.Draw("Example: Console", p_open); +} + +// Usage: +// static ExampleAppLog my_log; +// my_log.AddLog("Hello %d world\n", 123); +// my_log.Draw("title"); +struct ExampleAppLog +{ + ImGuiTextBuffer Buf; + ImGuiTextFilter Filter; + ImVector LineOffsets; // Index to lines offset + bool ScrollToBottom; + + void Clear() { Buf.clear(); LineOffsets.clear(); } + + void AddLog(const char* fmt, ...) IM_PRINTFARGS(2) + { + int old_size = Buf.size(); + va_list args; + va_start(args, fmt); + Buf.appendv(fmt, args); + va_end(args); + for (int new_size = Buf.size(); old_size < new_size; old_size++) + if (Buf[old_size] == '\n') + LineOffsets.push_back(old_size); + ScrollToBottom = true; + } + + void Draw(const char* title, bool* p_open = NULL) + { + ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiSetCond_FirstUseEver); + ImGui::Begin(title, p_open); + if (ImGui::Button("Clear")) Clear(); + ImGui::SameLine(); + bool copy = ImGui::Button("Copy"); + ImGui::SameLine(); + Filter.Draw("Filter", -100.0f); + ImGui::Separator(); + ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); + if (copy) ImGui::LogToClipboard(); + + if (Filter.IsActive()) + { + const char* buf_begin = Buf.begin(); + const char* line = buf_begin; + for (int line_no = 0; line != NULL; line_no++) + { + const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL; + if (Filter.PassFilter(line, line_end)) + ImGui::TextUnformatted(line, line_end); + line = line_end && line_end[1] ? line_end + 1 : NULL; + } + } + else + { + ImGui::TextUnformatted(Buf.begin()); + } + + if (ScrollToBottom) + ImGui::SetScrollHere(1.0f); + ScrollToBottom = false; + ImGui::EndChild(); + ImGui::End(); + } +}; + +static void ShowExampleAppLog(bool* p_open) +{ + static ExampleAppLog log; + + // Demo fill + static float last_time = -1.0f; + float time = ImGui::GetTime(); + if (time - last_time >= 0.3f) + { + const char* random_words[] = { "system", "info", "warning", "error", "fatal", "notice", "log" }; + log.AddLog("[%s] Hello, time is %.1f, rand() %d\n", random_words[rand() % IM_ARRAYSIZE(random_words)], time, (int)rand()); + last_time = time; + } + + log.Draw("Example: Log", p_open); +} + +static void ShowExampleAppLayout(bool* p_open) +{ + ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiSetCond_FirstUseEver); + if (ImGui::Begin("Example: Layout", p_open, ImGuiWindowFlags_MenuBar)) + { + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("File")) + { + if (ImGui::MenuItem("Close")) *p_open = false; + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + // left + static int selected = 0; + ImGui::BeginChild("left pane", ImVec2(150, 0), true); + for (int i = 0; i < 100; i++) + { + char label[128]; + sprintf(label, "MyObject %d", i); + if (ImGui::Selectable(label, selected == i)) + selected = i; + } + ImGui::EndChild(); + ImGui::SameLine(); + + // right + ImGui::BeginGroup(); + ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetItemsLineHeightWithSpacing())); // Leave room for 1 line below us + ImGui::Text("MyObject: %d", selected); + ImGui::Separator(); + ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); + ImGui::EndChild(); + ImGui::BeginChild("buttons"); + if (ImGui::Button("Revert")) {} + ImGui::SameLine(); + if (ImGui::Button("Save")) {} + ImGui::EndChild(); + ImGui::EndGroup(); + } + ImGui::End(); +} + +static void ShowExampleAppPropertyEditor(bool* p_open) +{ + ImGui::SetNextWindowSize(ImVec2(430,450), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("Example: Property editor", p_open)) + { + ImGui::End(); + return; + } + + ShowHelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2)); + ImGui::Columns(2); + ImGui::Separator(); + + struct funcs + { + static void ShowDummyObject(const char* prefix, int uid) + { + ImGui::PushID(uid); // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. + ImGui::AlignFirstTextHeightToWidgets(); // Text and Tree nodes are less high than regular widgets, here we add vertical spacing to make the tree lines equal high. + bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid); + ImGui::NextColumn(); + ImGui::AlignFirstTextHeightToWidgets(); + ImGui::Text("my sailor is rich"); + ImGui::NextColumn(); + if (node_open) + { + static float dummy_members[8] = { 0.0f,0.0f,1.0f,3.1416f,100.0f,999.0f }; + for (int i = 0; i < 8; i++) + { + ImGui::PushID(i); // Use field index as identifier. + if (i < 2) + { + ShowDummyObject("Child", 424242); + } + else + { + ImGui::AlignFirstTextHeightToWidgets(); + // Here we use a Selectable (instead of Text) to highlight on hover + //ImGui::Text("Field_%d", i); + char label[32]; + sprintf(label, "Field_%d", i); + ImGui::Bullet(); + ImGui::Selectable(label); + ImGui::NextColumn(); + ImGui::PushItemWidth(-1); + if (i >= 5) + ImGui::InputFloat("##value", &dummy_members[i], 1.0f); + else + ImGui::DragFloat("##value", &dummy_members[i], 0.01f); + ImGui::PopItemWidth(); + ImGui::NextColumn(); + } + ImGui::PopID(); + } + ImGui::TreePop(); + } + ImGui::PopID(); + } + }; + + // Iterate dummy objects with dummy members (all the same data) + for (int obj_i = 0; obj_i < 3; obj_i++) + funcs::ShowDummyObject("Object", obj_i); + + ImGui::Columns(1); + ImGui::Separator(); + ImGui::PopStyleVar(); + ImGui::End(); +} + +static void ShowExampleAppLongText(bool* p_open) +{ + ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiSetCond_FirstUseEver); + if (!ImGui::Begin("Example: Long text display", p_open)) + { + ImGui::End(); + return; + } + + static int test_type = 0; + static ImGuiTextBuffer log; + static int lines = 0; + ImGui::Text("Printing unusually long amount of text."); + ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped manually\0Multiple calls to Text(), not clipped\0"); + ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); + if (ImGui::Button("Clear")) { log.clear(); lines = 0; } + ImGui::SameLine(); + if (ImGui::Button("Add 1000 lines")) + { + for (int i = 0; i < 1000; i++) + log.append("%i The quick brown fox jumps over the lazy dog\n", lines+i); + lines += 1000; + } + ImGui::BeginChild("Log"); + switch (test_type) + { + case 0: + // Single call to TextUnformatted() with a big buffer + ImGui::TextUnformatted(log.begin(), log.end()); + break; + case 1: + { + // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); + ImGuiListClipper clipper(lines); + while (clipper.Step()) + for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) + ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); + ImGui::PopStyleVar(); + break; + } + case 2: + // Multiple calls to Text(), not clipped (slow) + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); + for (int i = 0; i < lines; i++) + ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); + ImGui::PopStyleVar(); + break; + } + ImGui::EndChild(); + ImGui::End(); +} + +// End of Demo code +#else + +void ImGui::ShowTestWindow(bool*) {} +void ImGui::ShowUserGuide() {} +void ImGui::ShowStyleEditor(ImGuiStyle*) {} + +#endif diff --git a/apps/exampleViewer/common/imgui/imgui_draw.cpp b/apps/exampleViewer/common/imgui/imgui_draw.cpp new file mode 100644 index 0000000000..ade417771d --- /dev/null +++ b/apps/exampleViewer/common/imgui/imgui_draw.cpp @@ -0,0 +1,2392 @@ +// dear imgui, v1.50 WIP +// (drawing and font code) + +// Contains implementation for +// - ImDrawList +// - ImDrawData +// - ImFontAtlas +// - ImFont +// - Default font data + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include "imgui.h" +#define IMGUI_DEFINE_MATH_OPERATORS +#define IMGUI_DEFINE_PLACEMENT_NEW +#include "imgui_internal.h" + +#include // vsnprintf, sscanf, printf +#if !defined(alloca) +#ifdef _WIN32 +#include // alloca +#elif (defined(__FreeBSD__) || defined(FreeBSD_kernel) || defined(__DragonFly__)) && !defined(__GLIBC__) +#include // alloca. FreeBSD uses stdlib.h unless GLIBC +#else +#include // alloca +#endif +#endif + +#ifdef _MSC_VER +#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) +#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen +#define snprintf _snprintf +#endif + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. +#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. +#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // +#endif +#elif defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used +#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function +#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value +#endif + +//------------------------------------------------------------------------- +// STB libraries implementation +//------------------------------------------------------------------------- + +//#define IMGUI_STB_NAMESPACE ImGuiStb +//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION + +#ifdef IMGUI_STB_NAMESPACE +namespace IMGUI_STB_NAMESPACE +{ +#endif + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. +#pragma clang diagnostic ignored "-Wunused-function" +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] +#endif + +#define STBRP_ASSERT(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION +#define STBRP_STATIC +#define STB_RECT_PACK_IMPLEMENTATION +#endif +#include "stb_rect_pack.h" + +#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x)) +#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x)) +#define STBTT_assert(x) IM_ASSERT(x) +#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION +#define STBTT_STATIC +#define STB_TRUETYPE_IMPLEMENTATION +#else +#define STBTT_DEF extern +#endif +#include "stb_truetype.h" + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif + +#ifdef IMGUI_STB_NAMESPACE +} // namespace ImGuiStb +using namespace IMGUI_STB_NAMESPACE; +#endif + +//----------------------------------------------------------------------------- +// ImDrawList +//----------------------------------------------------------------------------- + +static const ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift + +void ImDrawList::Clear() +{ + CmdBuffer.resize(0); + IdxBuffer.resize(0); + VtxBuffer.resize(0); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.resize(0); + _TextureIdStack.resize(0); + _Path.resize(0); + _ChannelsCurrent = 0; + _ChannelsCount = 1; + // NB: Do not clear channels so our allocations are re-used after the first frame. +} + +void ImDrawList::ClearFreeMemory() +{ + CmdBuffer.clear(); + IdxBuffer.clear(); + VtxBuffer.clear(); + _VtxCurrentIdx = 0; + _VtxWritePtr = NULL; + _IdxWritePtr = NULL; + _ClipRectStack.clear(); + _TextureIdStack.clear(); + _Path.clear(); + _ChannelsCurrent = 0; + _ChannelsCount = 1; + for (int i = 0; i < _Channels.Size; i++) + { + if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again + _Channels[i].CmdBuffer.clear(); + _Channels[i].IdxBuffer.clear(); + } + _Channels.clear(); +} + +// Use macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug mode +#define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : GNullClipRect) +#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) + +void ImDrawList::AddDrawCmd() +{ + ImDrawCmd draw_cmd; + draw_cmd.ClipRect = GetCurrentClipRect(); + draw_cmd.TextureId = GetCurrentTextureId(); + + IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); + CmdBuffer.push_back(draw_cmd); +} + +void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) +{ + ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL) + { + AddDrawCmd(); + current_cmd = &CmdBuffer.back(); + } + current_cmd->UserCallback = callback; + current_cmd->UserCallbackData = callback_data; + + AddDrawCmd(); // Force a new command after us (see comment below) +} + +// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. +// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. +void ImDrawList::UpdateClipRect() +{ + // If current command is used with different settings we need to add a new command + const ImVec4 curr_clip_rect = GetCurrentClipRect(); + ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) + { + AddDrawCmd(); + return; + } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; + if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL) + CmdBuffer.pop_back(); + else + curr_cmd->ClipRect = curr_clip_rect; +} + +void ImDrawList::UpdateTextureID() +{ + // If current command is used with different settings we need to add a new command + const ImTextureID curr_texture_id = GetCurrentTextureId(); + ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; + if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL) + { + AddDrawCmd(); + return; + } + + // Try to merge with previous command if it matches, else use current command + ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; + if (prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL) + CmdBuffer.pop_back(); + else + curr_cmd->TextureId = curr_texture_id; +} + +#undef GetCurrentClipRect +#undef GetCurrentTextureId + +// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) +void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) +{ + ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); + if (intersect_with_current_clip_rect && _ClipRectStack.Size) + { + ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1]; + if (cr.x < current.x) cr.x = current.x; + if (cr.y < current.y) cr.y = current.y; + if (cr.z > current.z) cr.z = current.z; + if (cr.w > current.w) cr.w = current.w; + } + cr.z = ImMax(cr.x, cr.z); + cr.w = ImMax(cr.y, cr.w); + + _ClipRectStack.push_back(cr); + UpdateClipRect(); +} + +void ImDrawList::PushClipRectFullScreen() +{ + PushClipRect(ImVec2(GNullClipRect.x, GNullClipRect.y), ImVec2(GNullClipRect.z, GNullClipRect.w)); + //PushClipRect(GetVisibleRect()); // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiContext from here? +} + +void ImDrawList::PopClipRect() +{ + IM_ASSERT(_ClipRectStack.Size > 0); + _ClipRectStack.pop_back(); + UpdateClipRect(); +} + +void ImDrawList::PushTextureID(const ImTextureID& texture_id) +{ + _TextureIdStack.push_back(texture_id); + UpdateTextureID(); +} + +void ImDrawList::PopTextureID() +{ + IM_ASSERT(_TextureIdStack.Size > 0); + _TextureIdStack.pop_back(); + UpdateTextureID(); +} + +void ImDrawList::ChannelsSplit(int channels_count) +{ + IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1); + int old_channels_count = _Channels.Size; + if (old_channels_count < channels_count) + _Channels.resize(channels_count); + _ChannelsCount = channels_count; + + // _Channels[] (24 bytes each) hold storage that we'll swap with this->_CmdBuffer/_IdxBuffer + // The content of _Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. + // When we switch to the next channel, we'll copy _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into _CmdBuffer/_IdxBuffer + memset(&_Channels[0], 0, sizeof(ImDrawChannel)); + for (int i = 1; i < channels_count; i++) + { + if (i >= old_channels_count) + { + IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); + } + else + { + _Channels[i].CmdBuffer.resize(0); + _Channels[i].IdxBuffer.resize(0); + } + if (_Channels[i].CmdBuffer.Size == 0) + { + ImDrawCmd draw_cmd; + draw_cmd.ClipRect = _ClipRectStack.back(); + draw_cmd.TextureId = _TextureIdStack.back(); + _Channels[i].CmdBuffer.push_back(draw_cmd); + } + } +} + +void ImDrawList::ChannelsMerge() +{ + // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. + if (_ChannelsCount <= 1) + return; + + ChannelsSetCurrent(0); + if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0) + CmdBuffer.pop_back(); + + int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; + for (int i = 1; i < _ChannelsCount; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) + ch.CmdBuffer.pop_back(); + new_cmd_buffer_count += ch.CmdBuffer.Size; + new_idx_buffer_count += ch.IdxBuffer.Size; + } + CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count); + IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count); + + ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; + for (int i = 1; i < _ChannelsCount; i++) + { + ImDrawChannel& ch = _Channels[i]; + if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } + if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } + } + AddDrawCmd(); + _ChannelsCount = 1; +} + +void ImDrawList::ChannelsSetCurrent(int idx) +{ + IM_ASSERT(idx < _ChannelsCount); + if (_ChannelsCurrent == idx) return; + memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times + memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); + _ChannelsCurrent = idx; + memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer)); + memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer)); + _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; +} + +// NB: this can be called with negative count for removing primitives (as long as the result does not underflow) +void ImDrawList::PrimReserve(int idx_count, int vtx_count) +{ + ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1]; + draw_cmd.ElemCount += idx_count; + + int vtx_buffer_size = VtxBuffer.Size; + VtxBuffer.resize(vtx_buffer_size + vtx_count); + _VtxWritePtr = VtxBuffer.Data + vtx_buffer_size; + + int idx_buffer_size = IdxBuffer.Size; + IdxBuffer.resize(idx_buffer_size + idx_count); + _IdxWritePtr = IdxBuffer.Data + idx_buffer_size; +} + +// Fully unrolled with inline call to keep our debug builds decently fast. +void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) +{ + ImVec2 b(c.x, a.y), d(a.x, c.y), uv(GImGui->FontTexUvWhitePixel); + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) +{ + ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y); + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) +{ + ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; + _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); + _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); + _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + _VtxCurrentIdx += 4; + _IdxWritePtr += 6; +} + +// TODO: Thickness anti-aliased lines cap are missing their AA fringe. +void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness, bool anti_aliased) +{ + if (points_count < 2) + return; + + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + anti_aliased &= GImGui->Style.AntiAliasedLines; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug + + int count = points_count; + if (!closed) + count = points_count-1; + + const bool thick_line = thickness > 1.0f; + if (anti_aliased) + { + // Anti-aliased stroke + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & IM_COL32(255,255,255,0); + + const int idx_count = thick_line ? count*18 : count*12; + const int vtx_count = thick_line ? points_count*4 : points_count*3; + PrimReserve(idx_count, vtx_count); + + // Temporary buffer + ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); + ImVec2* temp_points = temp_normals + points_count; + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + ImVec2 diff = points[i2] - points[i1]; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i1].x = diff.y; + temp_normals[i1].y = -diff.x; + } + if (!closed) + temp_normals[points_count-1] = temp_normals[points_count-2]; + + if (!thick_line) + { + if (!closed) + { + temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; + temp_points[1] = points[0] - temp_normals[0] * AA_SIZE; + temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; + temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; + } + + // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. + unsigned int idx1 = _VtxCurrentIdx; + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; + + // Average normals + ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE; + temp_points[i2*2+0] = points[i2] + dm; + temp_points[i2*2+1] = points[i2] - dm; + + // Add indexes + _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); + _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); + _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); + _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); + _IdxWritePtr += 12; + + idx1 = idx2; + } + + // Add vertexes + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; + _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans; + _VtxWritePtr += 3; + } + } + else + { + const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; + if (!closed) + { + temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE); + temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness); + temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness); + temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE); + temp_points[(points_count-1)*4+0] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); + temp_points[(points_count-1)*4+1] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness); + temp_points[(points_count-1)*4+2] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness); + temp_points[(points_count-1)*4+3] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); + } + + // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. + unsigned int idx1 = _VtxCurrentIdx; + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; + + // Average normals + ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); + ImVec2 dm_in = dm * half_inner_thickness; + temp_points[i2*4+0] = points[i2] + dm_out; + temp_points[i2*4+1] = points[i2] + dm_in; + temp_points[i2*4+2] = points[i2] - dm_in; + temp_points[i2*4+3] = points[i2] - dm_out; + + // Add indexes + _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); + _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+1); + _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); + _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10] = (ImDrawIdx)(idx2+0); _IdxWritePtr[11] = (ImDrawIdx)(idx2+1); + _IdxWritePtr[12] = (ImDrawIdx)(idx2+2); _IdxWritePtr[13] = (ImDrawIdx)(idx1+2); _IdxWritePtr[14] = (ImDrawIdx)(idx1+3); + _IdxWritePtr[15] = (ImDrawIdx)(idx1+3); _IdxWritePtr[16] = (ImDrawIdx)(idx2+3); _IdxWritePtr[17] = (ImDrawIdx)(idx2+2); + _IdxWritePtr += 18; + + idx1 = idx2; + } + + // Add vertexes + for (int i = 0; i < points_count; i++) + { + _VtxWritePtr[0].pos = temp_points[i*4+0]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col_trans; + _VtxWritePtr[1].pos = temp_points[i*4+1]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos = temp_points[i*4+2]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos = temp_points[i*4+3]; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col_trans; + _VtxWritePtr += 4; + } + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Stroke + const int idx_count = count*6; + const int vtx_count = count*4; // FIXME-OPT: Not sharing edges + PrimReserve(idx_count, vtx_count); + + for (int i1 = 0; i1 < count; i1++) + { + const int i2 = (i1+1) == points_count ? 0 : i1+1; + const ImVec2& p1 = points[i1]; + const ImVec2& p2 = points[i2]; + ImVec2 diff = p2 - p1; + diff *= ImInvLength(diff, 1.0f); + + const float dx = diff.x * (thickness * 0.5f); + const float dy = diff.y * (thickness * 0.5f); + _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; + _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; + _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; + _VtxWritePtr += 4; + + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); + _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); + _IdxWritePtr += 6; + _VtxCurrentIdx += 4; + } + } +} + +void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col, bool anti_aliased) +{ + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + anti_aliased &= GImGui->Style.AntiAliasedShapes; + //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug + + if (anti_aliased) + { + // Anti-aliased Fill + const float AA_SIZE = 1.0f; + const ImU32 col_trans = col & IM_COL32(255,255,255,0); + const int idx_count = (points_count-2)*3 + points_count*6; + const int vtx_count = (points_count*2); + PrimReserve(idx_count, vtx_count); + + // Add indexes for fill + unsigned int vtx_inner_idx = _VtxCurrentIdx; + unsigned int vtx_outer_idx = _VtxCurrentIdx+1; + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1)); + _IdxWritePtr += 3; + } + + // Compute normals + ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + const ImVec2& p0 = points[i0]; + const ImVec2& p1 = points[i1]; + ImVec2 diff = p1 - p0; + diff *= ImInvLength(diff, 1.0f); + temp_normals[i0].x = diff.y; + temp_normals[i0].y = -diff.x; + } + + for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) + { + // Average normals + const ImVec2& n0 = temp_normals[i0]; + const ImVec2& n1 = temp_normals[i1]; + ImVec2 dm = (n0 + n1) * 0.5f; + float dmr2 = dm.x*dm.x + dm.y*dm.y; + if (dmr2 > 0.000001f) + { + float scale = 1.0f / dmr2; + if (scale > 100.0f) scale = 100.0f; + dm *= scale; + } + dm *= AA_SIZE * 0.5f; + + // Add vertices + _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner + _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer + _VtxWritePtr += 2; + + // Add indexes for fringes + _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); + _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); + _IdxWritePtr += 6; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } + else + { + // Non Anti-aliased Fill + const int idx_count = (points_count-2)*3; + const int vtx_count = points_count; + PrimReserve(idx_count, vtx_count); + for (int i = 0; i < vtx_count; i++) + { + _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; + _VtxWritePtr++; + } + for (int i = 2; i < points_count; i++) + { + _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i); + _IdxWritePtr += 3; + } + _VtxCurrentIdx += (ImDrawIdx)vtx_count; + } +} + +void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int amin, int amax) +{ + static ImVec2 circle_vtx[12]; + static bool circle_vtx_builds = false; + const int circle_vtx_count = IM_ARRAYSIZE(circle_vtx); + if (!circle_vtx_builds) + { + for (int i = 0; i < circle_vtx_count; i++) + { + const float a = ((float)i / (float)circle_vtx_count) * 2*IM_PI; + circle_vtx[i].x = cosf(a); + circle_vtx[i].y = sinf(a); + } + circle_vtx_builds = true; + } + + if (amin > amax) return; + if (radius == 0.0f) + { + _Path.push_back(centre); + } + else + { + _Path.reserve(_Path.Size + (amax - amin + 1)); + for (int a = amin; a <= amax; a++) + { + const ImVec2& c = circle_vtx[a % circle_vtx_count]; + _Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius)); + } + } +} + +void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float amax, int num_segments) +{ + if (radius == 0.0f) + _Path.push_back(centre); + _Path.reserve(_Path.Size + (num_segments + 1)); + for (int i = 0; i <= num_segments; i++) + { + const float a = amin + ((float)i / (float)num_segments) * (amax - amin); + _Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius)); + } +} + +static void PathBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) +{ + float dx = x4 - x1; + float dy = y4 - y1; + float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); + float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); + d2 = (d2 >= 0) ? d2 : -d2; + d3 = (d3 >= 0) ? d3 : -d3; + if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy)) + { + path->push_back(ImVec2(x4, y4)); + } + else if (level < 10) + { + float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f; + float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f; + float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f; + float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f; + float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f; + float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f; + + PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1); + PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1); + } +} + +void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) +{ + ImVec2 p1 = _Path.back(); + if (num_segments == 0) + { + // Auto-tessellated + PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, GImGui->Style.CurveTessellationTol, 0); + } + else + { + float t_step = 1.0f / (float)num_segments; + for (int i_step = 1; i_step <= num_segments; i_step++) + { + float t = t_step * i_step; + float u = 1.0f - t; + float w1 = u*u*u; + float w2 = 3*u*u*t; + float w3 = 3*u*t*t; + float w4 = t*t*t; + _Path.push_back(ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y)); + } + } +} + +void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) +{ + float r = rounding; + r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f ) - 1.0f); + r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f ) - 1.0f); + + if (r <= 0.0f || rounding_corners == 0) + { + PathLineTo(a); + PathLineTo(ImVec2(b.x,a.y)); + PathLineTo(b); + PathLineTo(ImVec2(a.x,b.y)); + } + else + { + const float r0 = (rounding_corners & 1) ? r : 0.0f; + const float r1 = (rounding_corners & 2) ? r : 0.0f; + const float r2 = (rounding_corners & 4) ? r : 0.0f; + const float r3 = (rounding_corners & 8) ? r : 0.0f; + PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 6, 9); + PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 9, 12); + PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 0, 3); + PathArcToFast(ImVec2(a.x+r3,b.y-r3), r3, 3, 6); + } +} + +void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathLineTo(a + ImVec2(0.5f,0.5f)); + PathLineTo(b + ImVec2(0.5f,0.5f)); + PathStroke(col, false, thickness); +} + +// a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly. +void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.5f,0.5f), rounding, rounding_corners_flags); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + if (rounding > 0.0f) + { + PathRect(a, b, rounding, rounding_corners_flags); + PathFill(col); + } + else + { + PrimReserve(6, 4); + PrimRect(a, b, col); + } +} + +void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) +{ + if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0) + return; + + const ImVec2 uv = GImGui->FontTexUvWhitePixel; + PrimReserve(6, 4); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); + PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3)); + PrimWriteVtx(a, uv, col_upr_left); + PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right); + PrimWriteVtx(c, uv, col_bot_right); + PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left); +} + +void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathLineTo(d); + PathFill(col); +} + +void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(a); + PathLineTo(b); + PathLineTo(c); + PathFill(col); +} + +void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments); + PathStroke(col, true, thickness); +} + +void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; + PathArcTo(centre, radius, 0.0f, a_max, num_segments); + PathFill(col); +} + +void ImDrawList::AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + PathLineTo(pos0); + PathBezierCurveTo(cp0, cp1, pos1, num_segments); + PathStroke(col, false, thickness); +} + +void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + if (text_end == NULL) + text_end = text_begin + strlen(text_begin); + if (text_begin == text_end) + return; + + // Note: This is one of the few instance of breaking the encapsulation of ImDrawList, as we pull this from ImGui state, but it is just SO useful. + // Might just move Font/FontSize to ImDrawList? + if (font == NULL) + font = GImGui->Font; + if (font_size == 0.0f) + font_size = GImGui->FontSize; + + IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. + + ImVec4 clip_rect = _ClipRectStack.back(); + if (cpu_fine_clip_rect) + { + clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); + clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); + clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); + clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); + } + font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); +} + +void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) +{ + AddText(GImGui->Font, GImGui->FontSize, pos, col, text_begin, text_end); +} + +void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col) +{ + if ((col & IM_COL32_A_MASK) == 0) + return; + + // FIXME-OPT: This is wasting draw calls. + const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); + if (push_texture_id) + PushTextureID(user_texture_id); + + PrimReserve(6, 4); + PrimRectUV(a, b, uv0, uv1, col); + + if (push_texture_id) + PopTextureID(); +} + +//----------------------------------------------------------------------------- +// ImDrawData +//----------------------------------------------------------------------------- + +// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! +void ImDrawData::DeIndexAllBuffers() +{ + ImVector new_vtx_buffer; + TotalVtxCount = TotalIdxCount = 0; + for (int i = 0; i < CmdListsCount; i++) + { + ImDrawList* cmd_list = CmdLists[i]; + if (cmd_list->IdxBuffer.empty()) + continue; + new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); + for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) + new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; + cmd_list->VtxBuffer.swap(new_vtx_buffer); + cmd_list->IdxBuffer.resize(0); + TotalVtxCount += cmd_list->VtxBuffer.Size; + } +} + +// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. +void ImDrawData::ScaleClipRects(const ImVec2& scale) +{ + for (int i = 0; i < CmdListsCount; i++) + { + ImDrawList* cmd_list = CmdLists[i]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; + cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y); + } + } +} + +//----------------------------------------------------------------------------- +// ImFontAtlas +//----------------------------------------------------------------------------- + +ImFontConfig::ImFontConfig() +{ + FontData = NULL; + FontDataSize = 0; + FontDataOwnedByAtlas = true; + FontNo = 0; + SizePixels = 0.0f; + OversampleH = 3; + OversampleV = 1; + PixelSnapH = false; + GlyphExtraSpacing = ImVec2(0.0f, 0.0f); + GlyphRanges = NULL; + MergeMode = false; + MergeGlyphCenterV = false; + DstFont = NULL; + memset(Name, 0, sizeof(Name)); +} + +ImFontAtlas::ImFontAtlas() +{ + TexID = NULL; + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; + TexWidth = TexHeight = TexDesiredWidth = 0; + TexUvWhitePixel = ImVec2(0, 0); +} + +ImFontAtlas::~ImFontAtlas() +{ + Clear(); +} + +void ImFontAtlas::ClearInputData() +{ + for (int i = 0; i < ConfigData.Size; i++) + if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) + { + ImGui::MemFree(ConfigData[i].FontData); + ConfigData[i].FontData = NULL; + } + + // When clearing this we lose access to the font name and other information used to build the font. + for (int i = 0; i < Fonts.Size; i++) + if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) + { + Fonts[i]->ConfigData = NULL; + Fonts[i]->ConfigDataCount = 0; + } + ConfigData.clear(); +} + +void ImFontAtlas::ClearTexData() +{ + if (TexPixelsAlpha8) + ImGui::MemFree(TexPixelsAlpha8); + if (TexPixelsRGBA32) + ImGui::MemFree(TexPixelsRGBA32); + TexPixelsAlpha8 = NULL; + TexPixelsRGBA32 = NULL; +} + +void ImFontAtlas::ClearFonts() +{ + for (int i = 0; i < Fonts.Size; i++) + { + Fonts[i]->~ImFont(); + ImGui::MemFree(Fonts[i]); + } + Fonts.clear(); +} + +void ImFontAtlas::Clear() +{ + ClearInputData(); + ClearTexData(); + ClearFonts(); +} + +void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Build atlas on demand + if (TexPixelsAlpha8 == NULL) + { + if (ConfigData.empty()) + AddFontDefault(); + Build(); + } + + *out_pixels = TexPixelsAlpha8; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; +} + +void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) +{ + // Convert to RGBA32 format on demand + // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp + if (!TexPixelsRGBA32) + { + unsigned char* pixels; + GetTexDataAsAlpha8(&pixels, NULL, NULL); + TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); + const unsigned char* src = pixels; + unsigned int* dst = TexPixelsRGBA32; + for (int n = TexWidth * TexHeight; n > 0; n--) + *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); + } + + *out_pixels = (unsigned char*)TexPixelsRGBA32; + if (out_width) *out_width = TexWidth; + if (out_height) *out_height = TexHeight; + if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; +} + +ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) +{ + IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); + IM_ASSERT(font_cfg->SizePixels > 0.0f); + + // Create new font + if (!font_cfg->MergeMode) + { + ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont)); + IM_PLACEMENT_NEW(font) ImFont(); + Fonts.push_back(font); + } + + ConfigData.push_back(*font_cfg); + ImFontConfig& new_font_cfg = ConfigData.back(); + if (!new_font_cfg.DstFont) + new_font_cfg.DstFont = Fonts.back(); + if (!new_font_cfg.FontDataOwnedByAtlas) + { + new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize); + new_font_cfg.FontDataOwnedByAtlas = true; + memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); + } + + // Invalidate texture + ClearTexData(); + return new_font_cfg.DstFont; +} + +// Default font TTF is compressed with stb_compress then base85 encoded (see extra_fonts/binary_to_compressed_c.cpp for encoder) +static unsigned int stb_decompress_length(unsigned char *input); +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length); +static const char* GetDefaultCompressedFontDataTTFBase85(); +static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } +static void Decode85(const unsigned char* src, unsigned char* dst) +{ + while (*src) + { + unsigned int tmp = Decode85Byte(src[0]) + 85*(Decode85Byte(src[1]) + 85*(Decode85Byte(src[2]) + 85*(Decode85Byte(src[3]) + 85*Decode85Byte(src[4])))); + dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness. + src += 5; + dst += 4; + } +} + +// Load embedded ProggyClean.ttf at size 13, disable oversampling +ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) +{ + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (!font_cfg_template) + { + font_cfg.OversampleH = font_cfg.OversampleV = 1; + font_cfg.PixelSnapH = true; + } + if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, ""); + + const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); + ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, 13.0f, &font_cfg, GetGlyphRangesDefault()); + return font; +} + +ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + int data_size = 0; + void* data = ImLoadFileToMemory(filename, "rb", &data_size, 0); + if (!data) + { + IM_ASSERT(0); // Could not load file. + return NULL; + } + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + if (font_cfg.Name[0] == '\0') + { + // Store a short copy of filename into into the font name for convenience + const char* p; + for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} + snprintf(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p); + } + return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges); +} + +// NBM Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). +ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontData = ttf_data; + font_cfg.FontDataSize = ttf_size; + font_cfg.SizePixels = size_pixels; + if (glyph_ranges) + font_cfg.GlyphRanges = glyph_ranges; + return AddFont(&font_cfg); +} + +ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) +{ + const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data); + unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); + stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); + + ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); + IM_ASSERT(font_cfg.FontData == NULL); + font_cfg.FontDataOwnedByAtlas = true; + return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); +} + +ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) +{ + int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4; + void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size); + Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); + ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); + ImGui::MemFree(compressed_ttf); + return font; +} + +bool ImFontAtlas::Build() +{ + IM_ASSERT(ConfigData.Size > 0); + + TexID = NULL; + TexWidth = TexHeight = 0; + TexUvWhitePixel = ImVec2(0, 0); + ClearTexData(); + + struct ImFontTempBuildData + { + stbtt_fontinfo FontInfo; + stbrp_rect* Rects; + stbtt_pack_range* Ranges; + int RangesCount; + }; + ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData)); + + // Initialize font information early (so we can error without any cleanup) + count glyphs + int total_glyph_count = 0; + int total_glyph_range_count = 0; + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + + IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this)); + const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); + IM_ASSERT(font_offset >= 0); + if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) + return false; + + // Count glyphs + if (!cfg.GlyphRanges) + cfg.GlyphRanges = GetGlyphRangesDefault(); + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + total_glyph_count += (in_range[1] - in_range[0]) + 1; + total_glyph_range_count++; + } + } + + // Start packing. We need a known width for the skyline algorithm. Using a cheap heuristic here to decide of width. User can override TexDesiredWidth if they wish. + // After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height. + TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyph_count > 4000) ? 4096 : (total_glyph_count > 2000) ? 2048 : (total_glyph_count > 1000) ? 1024 : 512; + TexHeight = 0; + const int max_tex_height = 1024*32; + stbtt_pack_context spc; + stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL); + + // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). + ImVector extra_rects; + RenderCustomTexData(0, &extra_rects); + stbtt_PackSetOversampling(&spc, 1, 1); + stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size); + for (int i = 0; i < extra_rects.Size; i++) + if (extra_rects[i].was_packed) + TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h); + + // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) + int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; + stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyph_count * sizeof(stbtt_packedchar)); + stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyph_count * sizeof(stbrp_rect)); + stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_glyph_range_count * sizeof(stbtt_pack_range)); + memset(buf_packedchars, 0, total_glyph_count * sizeof(stbtt_packedchar)); + memset(buf_rects, 0, total_glyph_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. + memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range)); + + // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + + // Setup ranges + int glyph_count = 0; + int glyph_ranges_count = 0; + for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2) + { + glyph_count += (in_range[1] - in_range[0]) + 1; + glyph_ranges_count++; + } + tmp.Ranges = buf_ranges + buf_ranges_n; + tmp.RangesCount = glyph_ranges_count; + buf_ranges_n += glyph_ranges_count; + for (int i = 0; i < glyph_ranges_count; i++) + { + const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; + stbtt_pack_range& range = tmp.Ranges[i]; + range.font_size = cfg.SizePixels; + range.first_unicode_codepoint_in_range = in_range[0]; + range.num_chars = (in_range[1] - in_range[0]) + 1; + range.chardata_for_range = buf_packedchars + buf_packedchars_n; + buf_packedchars_n += range.num_chars; + } + + // Pack + tmp.Rects = buf_rects + buf_rects_n; + buf_rects_n += glyph_count; + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); + + // Extend texture height + for (int i = 0; i < n; i++) + if (tmp.Rects[i].was_packed) + TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); + } + IM_ASSERT(buf_rects_n == total_glyph_count); + IM_ASSERT(buf_packedchars_n == total_glyph_count); + IM_ASSERT(buf_ranges_n == total_glyph_range_count); + + // Create texture + TexHeight = ImUpperPowerOfTwo(TexHeight); + TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight); + memset(TexPixelsAlpha8, 0, TexWidth * TexHeight); + spc.pixels = TexPixelsAlpha8; + spc.height = TexHeight; + + // Second pass: render characters + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); + stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); + tmp.Rects = NULL; + } + + // End packing + stbtt_PackEnd(&spc); + ImGui::MemFree(buf_rects); + buf_rects = NULL; + + // Third pass: setup ImFont and glyphs for runtime + for (int input_i = 0; input_i < ConfigData.Size; input_i++) + { + ImFontConfig& cfg = ConfigData[input_i]; + ImFontTempBuildData& tmp = tmp_array[input_i]; + ImFont* dst_font = cfg.DstFont; + + float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); + int unscaled_ascent, unscaled_descent, unscaled_line_gap; + stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); + + float ascent = unscaled_ascent * font_scale; + float descent = unscaled_descent * font_scale; + if (!cfg.MergeMode) + { + dst_font->ContainerAtlas = this; + dst_font->ConfigData = &cfg; + dst_font->ConfigDataCount = 0; + dst_font->FontSize = cfg.SizePixels; + dst_font->Ascent = ascent; + dst_font->Descent = descent; + dst_font->Glyphs.resize(0); + } + dst_font->ConfigDataCount++; + float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f; + + dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable() + for (int i = 0; i < tmp.RangesCount; i++) + { + stbtt_pack_range& range = tmp.Ranges[i]; + for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) + { + const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; + if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) + continue; + + const int codepoint = range.first_unicode_codepoint_in_range + char_idx; + if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint)) + continue; + + stbtt_aligned_quad q; + float dummy_x = 0.0f, dummy_y = 0.0f; + stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); + + dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1); + ImFont::Glyph& glyph = dst_font->Glyphs.back(); + glyph.Codepoint = (ImWchar)codepoint; + glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1; + glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1; + glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f); + glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance + if (cfg.PixelSnapH) + glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f); + } + } + cfg.DstFont->BuildLookupTable(); + } + + // Cleanup temporaries + ImGui::MemFree(buf_packedchars); + ImGui::MemFree(buf_ranges); + ImGui::MemFree(tmp_array); + + // Render into our custom data block + RenderCustomTexData(1, &extra_rects); + + return true; +} + +void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects) +{ + // A work of art lies ahead! (. = white layer, X = black layer, others are blank) + // The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. + const int TEX_DATA_W = 90; + const int TEX_DATA_H = 27; + const char texture_data[TEX_DATA_W*TEX_DATA_H+1] = + { + "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" + "..- -X.....X- X.X - X.X -X.....X - X.....X" + "--- -XXX.XXX- X...X - X...X -X....X - X....X" + "X - X.X - X.....X - X.....X -X...X - X...X" + "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" + "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" + "X..X - X.X - X.X - X.X -XX X.X - X.X XX" + "X...X - X.X - X.X - XX X.X XX - X.X - X.X " + "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " + "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " + "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " + "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " + "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " + "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " + "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " + "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " + "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " + "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" + "X.X X..X - -X.......X- X.......X - XX XX - " + "XX X..X - - X.....X - X.....X - X.X X.X - " + " X..X - X...X - X...X - X..X X..X - " + " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " + "------------ - X - X -X.....................X- " + " ----------------------------------- X...XXXXXXXXXXXXX...X - " + " - X..X X..X - " + " - X.X X.X - " + " - XX XX - " + }; + + ImVector& rects = *(ImVector*)p_rects; + if (pass == 0) + { + // Request rectangles + stbrp_rect r; + memset(&r, 0, sizeof(r)); + r.w = (TEX_DATA_W*2)+1; + r.h = TEX_DATA_H+1; + rects.push_back(r); + } + else if (pass == 1) + { + // Render/copy pixels + const stbrp_rect& r = rects[0]; + for (int y = 0, n = 0; y < TEX_DATA_H; y++) + for (int x = 0; x < TEX_DATA_W; x++, n++) + { + const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth; + const int offset1 = offset0 + 1 + TEX_DATA_W; + TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00; + TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00; + } + const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight); + TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y); + + // Setup mouse cursors + const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] = + { + // Pos ........ Size ......... Offset ...... + { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow + { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput + { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move + { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS + { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW + { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW + { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE + }; + + for (int type = 0; type < ImGuiMouseCursor_Count_; type++) + { + ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type]; + ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y); + const ImVec2 size = cursor_datas[type][1]; + cursor_data.Type = type; + cursor_data.Size = size; + cursor_data.HotOffset = cursor_datas[type][2]; + cursor_data.TexUvMin[0] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale; + pos.x += TEX_DATA_W+1; + cursor_data.TexUvMin[1] = (pos) * tex_uv_scale; + cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale; + } + } +} + +// Retrieve list of range (2 int per range, values are inclusive) +const ImWchar* ImFontAtlas::GetGlyphRangesDefault() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesKorean() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3131, 0x3163, // Korean alphabets + 0xAC00, 0xD79D, // Korean characters + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesChinese() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + 0x4e00, 0x9FAF, // CJK Ideograms + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() +{ + // Store the 1946 ideograms code points as successive offsets from the initial unicode codepoint 0x4E00. Each offset has an implicit +1. + // This encoding helps us reduce the source code size. + static const short offsets_from_0x4E00[] = + { + -1,0,1,3,0,0,0,0,1,0,5,1,1,0,7,4,6,10,0,1,9,9,7,1,3,19,1,10,7,1,0,1,0,5,1,0,6,4,2,6,0,0,12,6,8,0,3,5,0,1,0,9,0,0,8,1,1,3,4,5,13,0,0,8,2,17, + 4,3,1,1,9,6,0,0,0,2,1,3,2,22,1,9,11,1,13,1,3,12,0,5,9,2,0,6,12,5,3,12,4,1,2,16,1,1,4,6,5,3,0,6,13,15,5,12,8,14,0,0,6,15,3,6,0,18,8,1,6,14,1, + 5,4,12,24,3,13,12,10,24,0,0,0,1,0,1,1,2,9,10,2,2,0,0,3,3,1,0,3,8,0,3,2,4,4,1,6,11,10,14,6,15,3,4,15,1,0,0,5,2,2,0,0,1,6,5,5,6,0,3,6,5,0,0,1,0, + 11,2,2,8,4,7,0,10,0,1,2,17,19,3,0,2,5,0,6,2,4,4,6,1,1,11,2,0,3,1,2,1,2,10,7,6,3,16,0,8,24,0,0,3,1,1,3,0,1,6,0,0,0,2,0,1,5,15,0,1,0,0,2,11,19, + 1,4,19,7,6,5,1,0,0,0,0,5,1,0,1,9,0,0,5,0,2,0,1,0,3,0,11,3,0,2,0,0,0,0,0,9,3,6,4,12,0,14,0,0,29,10,8,0,14,37,13,0,31,16,19,0,8,30,1,20,8,3,48, + 21,1,0,12,0,10,44,34,42,54,11,18,82,0,2,1,2,12,1,0,6,2,17,2,12,7,0,7,17,4,2,6,24,23,8,23,39,2,16,23,1,0,5,1,2,15,14,5,6,2,11,0,8,6,2,2,2,14, + 20,4,15,3,4,11,10,10,2,5,2,1,30,2,1,0,0,22,5,5,0,3,1,5,4,1,0,0,2,2,21,1,5,1,2,16,2,1,3,4,0,8,4,0,0,5,14,11,2,16,1,13,1,7,0,22,15,3,1,22,7,14, + 22,19,11,24,18,46,10,20,64,45,3,2,0,4,5,0,1,4,25,1,0,0,2,10,0,0,0,1,0,1,2,0,0,9,1,2,0,0,0,2,5,2,1,1,5,5,8,1,1,1,5,1,4,9,1,3,0,1,0,1,1,2,0,0, + 2,0,1,8,22,8,1,0,0,0,0,4,2,1,0,9,8,5,0,9,1,30,24,2,6,4,39,0,14,5,16,6,26,179,0,2,1,1,0,0,0,5,2,9,6,0,2,5,16,7,5,1,1,0,2,4,4,7,15,13,14,0,0, + 3,0,1,0,0,0,2,1,6,4,5,1,4,9,0,3,1,8,0,0,10,5,0,43,0,2,6,8,4,0,2,0,0,9,6,0,9,3,1,6,20,14,6,1,4,0,7,2,3,0,2,0,5,0,3,1,0,3,9,7,0,3,4,0,4,9,1,6,0, + 9,0,0,2,3,10,9,28,3,6,2,4,1,2,32,4,1,18,2,0,3,1,5,30,10,0,2,2,2,0,7,9,8,11,10,11,7,2,13,7,5,10,0,3,40,2,0,1,6,12,0,4,5,1,5,11,11,21,4,8,3,7, + 8,8,33,5,23,0,0,19,8,8,2,3,0,6,1,1,1,5,1,27,4,2,5,0,3,5,6,3,1,0,3,1,12,5,3,3,2,0,7,7,2,1,0,4,0,1,1,2,0,10,10,6,2,5,9,7,5,15,15,21,6,11,5,20, + 4,3,5,5,2,5,0,2,1,0,1,7,28,0,9,0,5,12,5,5,18,30,0,12,3,3,21,16,25,32,9,3,14,11,24,5,66,9,1,2,0,5,9,1,5,1,8,0,8,3,3,0,1,15,1,4,8,1,2,7,0,7,2, + 8,3,7,5,3,7,10,2,1,0,0,2,25,0,6,4,0,10,0,4,2,4,1,12,5,38,4,0,4,1,10,5,9,4,0,14,4,2,5,18,20,21,1,3,0,5,0,7,0,3,7,1,3,1,1,8,1,0,0,0,3,2,5,2,11, + 6,0,13,1,3,9,1,12,0,16,6,2,1,0,2,1,12,6,13,11,2,0,28,1,7,8,14,13,8,13,0,2,0,5,4,8,10,2,37,42,19,6,6,7,4,14,11,18,14,80,7,6,0,4,72,12,36,27, + 7,7,0,14,17,19,164,27,0,5,10,7,3,13,6,14,0,2,2,5,3,0,6,13,0,0,10,29,0,4,0,3,13,0,3,1,6,51,1,5,28,2,0,8,0,20,2,4,0,25,2,10,13,10,0,16,4,0,1,0, + 2,1,7,0,1,8,11,0,0,1,2,7,2,23,11,6,6,4,16,2,2,2,0,22,9,3,3,5,2,0,15,16,21,2,9,20,15,15,5,3,9,1,0,0,1,7,7,5,4,2,2,2,38,24,14,0,0,15,5,6,24,14, + 5,5,11,0,21,12,0,3,8,4,11,1,8,0,11,27,7,2,4,9,21,59,0,1,39,3,60,62,3,0,12,11,0,3,30,11,0,13,88,4,15,5,28,13,1,4,48,17,17,4,28,32,46,0,16,0, + 18,11,1,8,6,38,11,2,6,11,38,2,0,45,3,11,2,7,8,4,30,14,17,2,1,1,65,18,12,16,4,2,45,123,12,56,33,1,4,3,4,7,0,0,0,3,2,0,16,4,2,4,2,0,7,4,5,2,26, + 2,25,6,11,6,1,16,2,6,17,77,15,3,35,0,1,0,5,1,0,38,16,6,3,12,3,3,3,0,9,3,1,3,5,2,9,0,18,0,25,1,3,32,1,72,46,6,2,7,1,3,14,17,0,28,1,40,13,0,20, + 15,40,6,38,24,12,43,1,1,9,0,12,6,0,6,2,4,19,3,7,1,48,0,9,5,0,5,6,9,6,10,15,2,11,19,3,9,2,0,1,10,1,27,8,1,3,6,1,14,0,26,0,27,16,3,4,9,6,2,23, + 9,10,5,25,2,1,6,1,1,48,15,9,15,14,3,4,26,60,29,13,37,21,1,6,4,0,2,11,22,23,16,16,2,2,1,3,0,5,1,6,4,0,0,4,0,0,8,3,0,2,5,0,7,1,7,3,13,2,4,10, + 3,0,2,31,0,18,3,0,12,10,4,1,0,7,5,7,0,5,4,12,2,22,10,4,2,15,2,8,9,0,23,2,197,51,3,1,1,4,13,4,3,21,4,19,3,10,5,40,0,4,1,1,10,4,1,27,34,7,21, + 2,17,2,9,6,4,2,3,0,4,2,7,8,2,5,1,15,21,3,4,4,2,2,17,22,1,5,22,4,26,7,0,32,1,11,42,15,4,1,2,5,0,19,3,1,8,6,0,10,1,9,2,13,30,8,2,24,17,19,1,4, + 4,25,13,0,10,16,11,39,18,8,5,30,82,1,6,8,18,77,11,13,20,75,11,112,78,33,3,0,0,60,17,84,9,1,1,12,30,10,49,5,32,158,178,5,5,6,3,3,1,3,1,4,7,6, + 19,31,21,0,2,9,5,6,27,4,9,8,1,76,18,12,1,4,0,3,3,6,3,12,2,8,30,16,2,25,1,5,5,4,3,0,6,10,2,3,1,0,5,1,19,3,0,8,1,5,2,6,0,0,0,19,1,2,0,5,1,2,5, + 1,3,7,0,4,12,7,3,10,22,0,9,5,1,0,2,20,1,1,3,23,30,3,9,9,1,4,191,14,3,15,6,8,50,0,1,0,0,4,0,0,1,0,2,4,2,0,2,3,0,2,0,2,2,8,7,0,1,1,1,3,3,17,11, + 91,1,9,3,2,13,4,24,15,41,3,13,3,1,20,4,125,29,30,1,0,4,12,2,21,4,5,5,19,11,0,13,11,86,2,18,0,7,1,8,8,2,2,22,1,2,6,5,2,0,1,2,8,0,2,0,5,2,1,0, + 2,10,2,0,5,9,2,1,2,0,1,0,4,0,0,10,2,5,3,0,6,1,0,1,4,4,33,3,13,17,3,18,6,4,7,1,5,78,0,4,1,13,7,1,8,1,0,35,27,15,3,0,0,0,1,11,5,41,38,15,22,6, + 14,14,2,1,11,6,20,63,5,8,27,7,11,2,2,40,58,23,50,54,56,293,8,8,1,5,1,14,0,1,12,37,89,8,8,8,2,10,6,0,0,0,4,5,2,1,0,1,1,2,7,0,3,3,0,4,6,0,3,2, + 19,3,8,0,0,0,4,4,16,0,4,1,5,1,3,0,3,4,6,2,17,10,10,31,6,4,3,6,10,126,7,3,2,2,0,9,0,0,5,20,13,0,15,0,6,0,2,5,8,64,50,3,2,12,2,9,0,0,11,8,20, + 109,2,18,23,0,0,9,61,3,0,28,41,77,27,19,17,81,5,2,14,5,83,57,252,14,154,263,14,20,8,13,6,57,39,38, + }; + static ImWchar base_ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana + 0x31F0, 0x31FF, // Katakana Phonetic Extensions + 0xFF00, 0xFFEF, // Half-width characters + }; + static bool full_ranges_unpacked = false; + static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(offsets_from_0x4E00)*2 + 1]; + if (!full_ranges_unpacked) + { + // Unpack + int codepoint = 0x4e00; + memcpy(full_ranges, base_ranges, sizeof(base_ranges)); + ImWchar* dst = full_ranges + IM_ARRAYSIZE(base_ranges);; + for (int n = 0; n < IM_ARRAYSIZE(offsets_from_0x4E00); n++, dst += 2) + dst[0] = dst[1] = (ImWchar)(codepoint += (offsets_from_0x4E00[n] + 1)); + dst[0] = 0; + full_ranges_unpacked = true; + } + return &full_ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x0400, 0x052F, // Cyrillic + Cyrillic Supplement + 0x2DE0, 0x2DFF, // Cyrillic Extended-A + 0xA640, 0xA69F, // Cyrillic Extended-B + 0, + }; + return &ranges[0]; +} + +const ImWchar* ImFontAtlas::GetGlyphRangesThai() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + 0x0E00, 0x0E7F, // Thai + 0, + }; + return &ranges[0]; +} + +//----------------------------------------------------------------------------- +// ImFont +//----------------------------------------------------------------------------- + +ImFont::ImFont() +{ + Scale = 1.0f; + FallbackChar = (ImWchar)'?'; + Clear(); +} + +ImFont::~ImFont() +{ + // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. + // If you want to delete fonts you need to do it between Render() and NewFrame(). + // FIXME-CLEANUP + /* + ImGuiContext& g = *GImGui; + if (g.Font == this) + g.Font = NULL; + */ + Clear(); +} + +void ImFont::Clear() +{ + FontSize = 0.0f; + DisplayOffset = ImVec2(0.0f, 1.0f); + ConfigData = NULL; + ConfigDataCount = 0; + Ascent = Descent = 0.0f; + ContainerAtlas = NULL; + Glyphs.clear(); + FallbackGlyph = NULL; + FallbackXAdvance = 0.0f; + IndexXAdvance.clear(); + IndexLookup.clear(); +} + +void ImFont::BuildLookupTable() +{ + int max_codepoint = 0; + for (int i = 0; i != Glyphs.Size; i++) + max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); + + IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved + IndexXAdvance.clear(); + IndexLookup.clear(); + GrowIndex(max_codepoint + 1); + for (int i = 0; i < Glyphs.Size; i++) + { + int codepoint = (int)Glyphs[i].Codepoint; + IndexXAdvance[codepoint] = Glyphs[i].XAdvance; + IndexLookup[codepoint] = (unsigned short)i; + } + + // Create a glyph to handle TAB + // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) + if (FindGlyph((unsigned short)' ')) + { + if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times + Glyphs.resize(Glyphs.Size + 1); + ImFont::Glyph& tab_glyph = Glyphs.back(); + tab_glyph = *FindGlyph((unsigned short)' '); + tab_glyph.Codepoint = '\t'; + tab_glyph.XAdvance *= 4; + IndexXAdvance[(int)tab_glyph.Codepoint] = (float)tab_glyph.XAdvance; + IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); + } + + FallbackGlyph = NULL; + FallbackGlyph = FindGlyph(FallbackChar); + FallbackXAdvance = FallbackGlyph ? FallbackGlyph->XAdvance : 0.0f; + for (int i = 0; i < max_codepoint + 1; i++) + if (IndexXAdvance[i] < 0.0f) + IndexXAdvance[i] = FallbackXAdvance; +} + +void ImFont::SetFallbackChar(ImWchar c) +{ + FallbackChar = c; + BuildLookupTable(); +} + +void ImFont::GrowIndex(int new_size) +{ + IM_ASSERT(IndexXAdvance.Size == IndexLookup.Size); + int old_size = IndexLookup.Size; + if (new_size <= old_size) + return; + IndexXAdvance.resize(new_size); + IndexLookup.resize(new_size); + for (int i = old_size; i < new_size; i++) + { + IndexXAdvance[i] = -1.0f; + IndexLookup[i] = (unsigned short)-1; + } +} + +void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) +{ + IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. + int index_size = IndexLookup.Size; + + if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists + return; + if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op + return; + + GrowIndex(dst + 1); + IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; + IndexXAdvance[dst] = (src < index_size) ? IndexXAdvance.Data[src] : 1.0f; +} + +const ImFont::Glyph* ImFont::FindGlyph(unsigned short c) const +{ + if (c < IndexLookup.Size) + { + const unsigned short i = IndexLookup[c]; + if (i != (unsigned short)-1) + return &Glyphs.Data[i]; + } + return FallbackGlyph; +} + +const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const +{ + // Simple word-wrapping for English, not full-featured. Please submit failing cases! + // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) + + // For references, possible wrap point marked with ^ + // "aaa bbb, ccc,ddd. eee fff. ggg!" + // ^ ^ ^ ^ ^__ ^ ^ + + // List of hardcoded separators: .,;!?'" + + // Skip extra blanks after a line returns (that includes not counting them in width computation) + // e.g. "Hello world" --> "Hello" "World" + + // Cut words that cannot possibly fit within one line. + // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" + + float line_width = 0.0f; + float word_width = 0.0f; + float blank_width = 0.0f; + + const char* word_end = text; + const char* prev_word_end = NULL; + bool inside_word = true; + + const char* s = text; + while (s < text_end) + { + unsigned int c = (unsigned int)*s; + const char* next_s; + if (c < 0x80) + next_s = s + 1; + else + next_s = s + ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + + if (c < 32) + { + if (c == '\n') + { + line_width = word_width = blank_width = 0.0f; + inside_word = true; + s = next_s; + continue; + } + if (c == '\r') + { + s = next_s; + continue; + } + } + + const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance) * scale; + if (ImCharIsSpace(c)) + { + if (inside_word) + { + line_width += blank_width; + blank_width = 0.0f; + } + blank_width += char_width; + inside_word = false; + } + else + { + word_width += char_width; + if (inside_word) + { + word_end = next_s; + } + else + { + prev_word_end = word_end; + line_width += word_width + blank_width; + word_width = blank_width = 0.0f; + } + + // Allow wrapping after punctuation. + inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' || c == '?' || c == '\"'); + } + + // We ignore blank width at the end of the line (they can be skipped) + if (line_width + word_width >= wrap_width) + { + // Words that cannot possibly fit within an entire line will be cut anywhere. + if (word_width < wrap_width) + s = prev_word_end ? prev_word_end : word_end; + break; + } + + s = next_s; + } + + return s; +} + +ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const +{ + if (!text_end) + text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this. + + const float line_height = size; + const float scale = size / FontSize; + + ImVec2 text_size = ImVec2(0,0); + float line_width = 0.0f; + + const bool word_wrap_enabled = (wrap_width > 0.0f); + const char* word_wrap_eol = NULL; + + const char* s = text_begin; + while (s < text_end) + { + if (word_wrap_enabled) + { + // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); + if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. + word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below + } + + if (s >= word_wrap_eol) + { + if (text_size.x < line_width) + text_size.x = line_width; + text_size.y += line_height; + line_width = 0.0f; + word_wrap_eol = NULL; + + // Wrapping skips upcoming blanks + while (s < text_end) + { + const char c = *s; + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + } + continue; + } + } + + // Decode and advance source + const char* prev_s = s; + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } + + if (c < 32) + { + if (c == '\n') + { + text_size.x = ImMax(text_size.x, line_width); + text_size.y += line_height; + line_width = 0.0f; + continue; + } + if (c == '\r') + continue; + } + + const float char_width = ((int)c < IndexXAdvance.Size ? IndexXAdvance[(int)c] : FallbackXAdvance) * scale; + if (line_width + char_width >= max_width) + { + s = prev_s; + break; + } + + line_width += char_width; + } + + if (text_size.x < line_width) + text_size.x = line_width; + + if (line_width > 0 || text_size.y == 0.0f) + text_size.y += line_height; + + if (remaining) + *remaining = s; + + return text_size; +} + +void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const +{ + if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. + return; + if (const Glyph* glyph = FindGlyph(c)) + { + float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; + pos.x = (float)(int)pos.x + DisplayOffset.x; + pos.y = (float)(int)pos.y + DisplayOffset.y; + ImVec2 pos_tl(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale); + ImVec2 pos_br(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale); + draw_list->PrimReserve(6, 4); + draw_list->PrimRectUV(pos_tl, pos_br, ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); + } +} + +void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const +{ + if (!text_end) + text_end = text_begin + strlen(text_begin); + + // Align to be pixel perfect + pos.x = (float)(int)pos.x + DisplayOffset.x; + pos.y = (float)(int)pos.y + DisplayOffset.y; + float x = pos.x; + float y = pos.y; + if (y > clip_rect.w) + return; + + const float scale = size / FontSize; + const float line_height = FontSize * scale; + const bool word_wrap_enabled = (wrap_width > 0.0f); + const char* word_wrap_eol = NULL; + + // Skip non-visible lines + const char* s = text_begin; + if (!word_wrap_enabled && y + line_height < clip_rect.y) + while (s < text_end && *s != '\n') // Fast-forward to next line + s++; + + // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) + const int vtx_count_max = (int)(text_end - s) * 4; + const int idx_count_max = (int)(text_end - s) * 6; + const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; + draw_list->PrimReserve(idx_count_max, vtx_count_max); + + ImDrawVert* vtx_write = draw_list->_VtxWritePtr; + ImDrawIdx* idx_write = draw_list->_IdxWritePtr; + unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; + + while (s < text_end) + { + if (word_wrap_enabled) + { + // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. + if (!word_wrap_eol) + { + word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x)); + if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. + word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below + } + + if (s >= word_wrap_eol) + { + x = pos.x; + y += line_height; + word_wrap_eol = NULL; + + // Wrapping skips upcoming blanks + while (s < text_end) + { + const char c = *s; + if (ImCharIsSpace(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } + } + continue; + } + } + + // Decode and advance source + unsigned int c = (unsigned int)*s; + if (c < 0x80) + { + s += 1; + } + else + { + s += ImTextCharFromUtf8(&c, s, text_end); + if (c == 0) + break; + } + + if (c < 32) + { + if (c == '\n') + { + x = pos.x; + y += line_height; + + if (y > clip_rect.w) + break; + if (!word_wrap_enabled && y + line_height < clip_rect.y) + while (s < text_end && *s != '\n') // Fast-forward to next line + s++; + continue; + } + if (c == '\r') + continue; + } + + float char_width = 0.0f; + if (const Glyph* glyph = FindGlyph((unsigned short)c)) + { + char_width = glyph->XAdvance * scale; + + // Arbitrarily assume that both space and tabs are empty glyphs as an optimization + if (c != ' ' && c != '\t') + { + // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w + float x1 = x + glyph->X0 * scale; + float x2 = x + glyph->X1 * scale; + float y1 = y + glyph->Y0 * scale; + float y2 = y + glyph->Y1 * scale; + if (x1 <= clip_rect.z && x2 >= clip_rect.x) + { + // Render a character + float u1 = glyph->U0; + float v1 = glyph->V0; + float u2 = glyph->U1; + float v2 = glyph->V1; + + // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. + if (cpu_fine_clip) + { + if (x1 < clip_rect.x) + { + u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1); + x1 = clip_rect.x; + } + if (y1 < clip_rect.y) + { + v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1); + y1 = clip_rect.y; + } + if (x2 > clip_rect.z) + { + u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1); + x2 = clip_rect.z; + } + if (y2 > clip_rect.w) + { + v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1); + y2 = clip_rect.w; + } + if (y1 >= y2) + { + x += char_width; + continue; + } + } + + // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug build. + // Inlined here: + { + idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); + idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); + vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; + vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; + vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; + vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; + vtx_write += 4; + vtx_current_idx += 4; + idx_write += 6; + } + } + } + } + + x += char_width; + } + + // Give back unused vertices + draw_list->VtxBuffer.resize((int)(vtx_write - draw_list->VtxBuffer.Data)); + draw_list->IdxBuffer.resize((int)(idx_write - draw_list->IdxBuffer.Data)); + draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); + draw_list->_VtxWritePtr = vtx_write; + draw_list->_IdxWritePtr = idx_write; + draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size; +} + +//----------------------------------------------------------------------------- +// DEFAULT FONT DATA +//----------------------------------------------------------------------------- +// Compressed with stb_compress() then converted to a C array. +// Use the program in extra_fonts/binary_to_compressed_c.cpp to create the array from a TTF file. +// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h +//----------------------------------------------------------------------------- + +static unsigned int stb_decompress_length(unsigned char *input) +{ + return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; +} + +static unsigned char *stb__barrier, *stb__barrier2, *stb__barrier3, *stb__barrier4; +static unsigned char *stb__dout; +static void stb__match(unsigned char *data, unsigned int length) +{ + // INVERSE of memmove... write each byte before copying the next... + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier4) { stb__dout = stb__barrier+1; return; } + while (length--) *stb__dout++ = *data++; +} + +static void stb__lit(unsigned char *data, unsigned int length) +{ + IM_ASSERT (stb__dout + length <= stb__barrier); + if (stb__dout + length > stb__barrier) { stb__dout += length; return; } + if (data < stb__barrier2) { stb__dout = stb__barrier+1; return; } + memcpy(stb__dout, data, length); + stb__dout += length; +} + +#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) +#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) +#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) + +static unsigned char *stb_decompress_token(unsigned char *i) +{ + if (*i >= 0x20) { // use fewer if's for cases that expand small + if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; + else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; + else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); + } else { // more ifs for cases that expand large, since overhead is amortized + if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; + else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; + else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); + else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); + else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; + else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; + } + return i; +} + +static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) +{ + const unsigned long ADLER_MOD = 65521; + unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; + unsigned long blocklen, i; + + blocklen = buflen % 5552; + while (buflen) { + for (i=0; i + 7 < blocklen; i += 8) { + s1 += buffer[0], s2 += s1; + s1 += buffer[1], s2 += s1; + s1 += buffer[2], s2 += s1; + s1 += buffer[3], s2 += s1; + s1 += buffer[4], s2 += s1; + s1 += buffer[5], s2 += s1; + s1 += buffer[6], s2 += s1; + s1 += buffer[7], s2 += s1; + + buffer += 8; + } + + for (; i < blocklen; ++i) + s1 += *buffer++, s2 += s1; + + s1 %= ADLER_MOD, s2 %= ADLER_MOD; + buflen -= blocklen; + blocklen = 5552; + } + return (unsigned int)(s2 << 16) + (unsigned int)s1; +} + +static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length) +{ + unsigned int olen; + if (stb__in4(0) != 0x57bC0000) return 0; + if (stb__in4(4) != 0) return 0; // error! stream is > 4GB + olen = stb_decompress_length(i); + stb__barrier2 = i; + stb__barrier3 = i+length; + stb__barrier = output + olen; + stb__barrier4 = output; + i += 16; + + stb__dout = output; + for (;;) { + unsigned char *old_i = i; + i = stb_decompress_token(i); + if (i == old_i) { + if (*i == 0x05 && i[1] == 0xfa) { + IM_ASSERT(stb__dout == output + olen); + if (stb__dout != output + olen) return 0; + if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) + return 0; + return olen; + } else { + IM_ASSERT(0); /* NOTREACHED */ + return 0; + } + } + IM_ASSERT(stb__dout <= output + olen); + if (stb__dout > output + olen) + return 0; + } +} + +//----------------------------------------------------------------------------- +// ProggyClean.ttf +// Copyright (c) 2004, 2005 Tristan Grimmer +// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) +// Download and more information at http://upperbounds.net +//----------------------------------------------------------------------------- +// File: 'ProggyClean.ttf' (41208 bytes) +// Exported using binary_to_compressed_c.cpp +//----------------------------------------------------------------------------- +static const char proggy_clean_ttf_compressed_data_base85[11980+1] = + "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" + "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" + "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." + "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" + "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" + "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" + "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" + "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" + "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" + "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" + "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" + "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" + "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" + "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" + "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" + "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" + "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" + "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" + "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" + "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" + "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" + ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" + "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" + "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" + "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" + "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" + "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" + "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" + "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" + "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" + "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" + "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" + "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" + "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" + "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" + "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" + "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" + ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" + "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" + "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" + "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" + "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" + "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" + "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" + ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" + "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" + "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" + "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" + "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" + "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; + +static const char* GetDefaultCompressedFontDataTTFBase85() +{ + return proggy_clean_ttf_compressed_data_base85; +} diff --git a/apps/exampleViewer/common/imgui/imgui_internal.h b/apps/exampleViewer/common/imgui/imgui_internal.h new file mode 100644 index 0000000000..08f206adc5 --- /dev/null +++ b/apps/exampleViewer/common/imgui/imgui_internal.h @@ -0,0 +1,769 @@ +// dear imgui, v1.50 WIP +// (internals) + +// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! +// Implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) +// #define IMGUI_DEFINE_MATH_OPERATORS + +#pragma once + +#ifndef IMGUI_VERSION +#error Must include imgui.h before imgui_internal.h +#endif + +#include // FILE* +#include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf + +#ifdef _MSC_VER +#pragma warning (push) +#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h +#pragma clang diagnostic ignored "-Wold-style-cast" +#endif + +//----------------------------------------------------------------------------- +// Forward Declarations +//----------------------------------------------------------------------------- + +struct ImRect; +struct ImGuiColMod; +struct ImGuiStyleMod; +struct ImGuiGroupData; +struct ImGuiSimpleColumns; +struct ImGuiDrawContext; +struct ImGuiTextEditState; +struct ImGuiIniData; +struct ImGuiMouseCursorData; +struct ImGuiPopupRef; +struct ImGuiWindow; + +typedef int ImGuiLayoutType; // enum ImGuiLayoutType_ +typedef int ImGuiButtonFlags; // enum ImGuiButtonFlags_ +typedef int ImGuiTreeNodeFlags; // enum ImGuiTreeNodeFlags_ +typedef int ImGuiSliderFlags; // enum ImGuiSliderFlags_ + +//------------------------------------------------------------------------- +// STB libraries +//------------------------------------------------------------------------- + +namespace ImGuiStb +{ + +#undef STB_TEXTEDIT_STRING +#undef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_STRING ImGuiTextEditState +#define STB_TEXTEDIT_CHARTYPE ImWchar +#define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f +#include "stb_textedit.h" + +} // namespace ImGuiStb + +//----------------------------------------------------------------------------- +// Context +//----------------------------------------------------------------------------- + +extern IMGUI_API ImGuiContext* GImGui; // current implicit ImGui context pointer + +//----------------------------------------------------------------------------- +// Helpers +//----------------------------------------------------------------------------- + +#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) +#define IM_PI 3.14159265358979323846f +#define IM_OFFSETOF(_TYPE,_ELM) ((size_t)&(((_TYPE*)0)->_ELM)) + +// Helpers: UTF-8 <> wchar +IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count +IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count +IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count +IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) +IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points + +// Helpers: Misc +IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings +IMGUI_API void* ImLoadFileToMemory(const char* filename, const char* file_open_mode, int* out_file_size = NULL, int padding_bytes = 0); +IMGUI_API bool ImIsPointInTriangle(const ImVec2& p, const ImVec2& a, const ImVec2& b, const ImVec2& c); +static inline bool ImCharIsSpace(int c) { return c == ' ' || c == '\t' || c == 0x3000; } +static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } + +// Helpers: String +IMGUI_API int ImStricmp(const char* str1, const char* str2); +IMGUI_API int ImStrnicmp(const char* str1, const char* str2, int count); +IMGUI_API char* ImStrdup(const char* str); +IMGUI_API int ImStrlenW(const ImWchar* str); +IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line +IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); +IMGUI_API int ImFormatString(char* buf, int buf_size, const char* fmt, ...) IM_PRINTFARGS(3); +IMGUI_API int ImFormatStringV(char* buf, int buf_size, const char* fmt, va_list args); + +// Helpers: Math +// We are keeping those not leaking to the user by default, in the case the user has implicit cast operators between ImVec2 and its own types (when IM_VEC2_CLASS_EXTRA is defined) +#ifdef IMGUI_DEFINE_MATH_OPERATORS +static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } +static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } +static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); } +static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); } +static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); } +static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); } +static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } +static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } +static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } +static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } +static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); } +#endif + +static inline int ImMin(int lhs, int rhs) { return lhs < rhs ? lhs : rhs; } +static inline int ImMax(int lhs, int rhs) { return lhs >= rhs ? lhs : rhs; } +static inline float ImMin(float lhs, float rhs) { return lhs < rhs ? lhs : rhs; } +static inline float ImMax(float lhs, float rhs) { return lhs >= rhs ? lhs : rhs; } +static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMin(lhs.x,rhs.x), ImMin(lhs.y,rhs.y)); } +static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(ImMax(lhs.x,rhs.x), ImMax(lhs.y,rhs.y)); } +static inline int ImClamp(int v, int mn, int mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +static inline float ImClamp(float v, float mn, float mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } +static inline ImVec2 ImClamp(const ImVec2& f, const ImVec2& mn, ImVec2 mx) { return ImVec2(ImClamp(f.x,mn.x,mx.x), ImClamp(f.y,mn.y,mx.y)); } +static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } +static inline float ImLerp(float a, float b, float t) { return a + (b - a) * t; } +static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } +static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } +static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } +static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / sqrtf(d); return fail_value; } +static inline float ImFloor(float f) { return (float)(int)f; } +static inline ImVec2 ImFloor(ImVec2 v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } + +// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. +// Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. +#ifdef IMGUI_DEFINE_PLACEMENT_NEW +struct ImPlacementNewDummy {}; +inline void* operator new(size_t, ImPlacementNewDummy, void* ptr) { return ptr; } +inline void operator delete(void*, ImPlacementNewDummy, void*) {} +#define IM_PLACEMENT_NEW(_PTR) new(ImPlacementNewDummy(), _PTR) +#endif + +//----------------------------------------------------------------------------- +// Types +//----------------------------------------------------------------------------- + +enum ImGuiButtonFlags_ +{ + ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat + ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // (default) return pressed on click+release on same item (default if no PressedOn** flag is set) + ImGuiButtonFlags_PressedOnClick = 1 << 2, // return pressed on click (default requires click+release) + ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return pressed on release (default requires click+release) + ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return pressed on double-click (default requires click+release) + ImGuiButtonFlags_FlattenChilds = 1 << 5, // allow interaction even if a child window is overlapping + ImGuiButtonFlags_DontClosePopups = 1 << 6, // disable automatically closing parent popup on press + ImGuiButtonFlags_Disabled = 1 << 7, // disable interaction + ImGuiButtonFlags_AlignTextBaseLine = 1 << 8, // vertically align button to match text baseline - ButtonEx() only + ImGuiButtonFlags_NoKeyModifiers = 1 << 9, // disable interaction if a key modifier is held + ImGuiButtonFlags_AllowOverlapMode = 1 << 10 // require previous frame HoveredId to either match id or be null before being usable +}; + +enum ImGuiSliderFlags_ +{ + ImGuiSliderFlags_Vertical = 1 << 0 +}; + +enum ImGuiSelectableFlagsPrivate_ +{ + // NB: need to be in sync with last value of ImGuiSelectableFlags_ + ImGuiSelectableFlags_Menu = 1 << 3, + ImGuiSelectableFlags_MenuItem = 1 << 4, + ImGuiSelectableFlags_Disabled = 1 << 5, + ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 6 +}; + +// FIXME: this is in development, not exposed/functional as a generic feature yet. +enum ImGuiLayoutType_ +{ + ImGuiLayoutType_Vertical, + ImGuiLayoutType_Horizontal +}; + +enum ImGuiPlotType +{ + ImGuiPlotType_Lines, + ImGuiPlotType_Histogram +}; + +enum ImGuiDataType +{ + ImGuiDataType_Int, + ImGuiDataType_Float, + ImGuiDataType_Float2, +}; + +enum ImGuiCorner +{ + ImGuiCorner_TopLeft = 1 << 0, // 1 + ImGuiCorner_TopRight = 1 << 1, // 2 + ImGuiCorner_BottomRight = 1 << 2, // 4 + ImGuiCorner_BottomLeft = 1 << 3, // 8 + ImGuiCorner_All = 0x0F +}; + +// 2D axis aligned bounding-box +// NB: we can't rely on ImVec2 math operators being available here +struct IMGUI_API ImRect +{ + ImVec2 Min; // Upper-left + ImVec2 Max; // Lower-right + + ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} + ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} + ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} + ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} + + ImVec2 GetCenter() const { return ImVec2((Min.x+Max.x)*0.5f, (Min.y+Max.y)*0.5f); } + ImVec2 GetSize() const { return ImVec2(Max.x-Min.x, Max.y-Min.y); } + float GetWidth() const { return Max.x-Min.x; } + float GetHeight() const { return Max.y-Min.y; } + ImVec2 GetTL() const { return Min; } // Top-left + ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right + ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left + ImVec2 GetBR() const { return Max; } // Bottom-right + bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } + bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x < Max.x && r.Max.y < Max.y; } + bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } + void Add(const ImVec2& rhs) { if (Min.x > rhs.x) Min.x = rhs.x; if (Min.y > rhs.y) Min.y = rhs.y; if (Max.x < rhs.x) Max.x = rhs.x; if (Max.y < rhs.y) Max.y = rhs.y; } + void Add(const ImRect& rhs) { if (Min.x > rhs.Min.x) Min.x = rhs.Min.x; if (Min.y > rhs.Min.y) Min.y = rhs.Min.y; if (Max.x < rhs.Max.x) Max.x = rhs.Max.x; if (Max.y < rhs.Max.y) Max.y = rhs.Max.y; } + void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } + void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } + void Reduce(const ImVec2& amount) { Min.x += amount.x; Min.y += amount.y; Max.x -= amount.x; Max.y -= amount.y; } + void Clip(const ImRect& clip) { if (Min.x < clip.Min.x) Min.x = clip.Min.x; if (Min.y < clip.Min.y) Min.y = clip.Min.y; if (Max.x > clip.Max.x) Max.x = clip.Max.x; if (Max.y > clip.Max.y) Max.y = clip.Max.y; } + void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } + ImVec2 GetClosestPoint(ImVec2 p, bool on_edge) const + { + if (!on_edge && Contains(p)) + return p; + if (p.x > Max.x) p.x = Max.x; + else if (p.x < Min.x) p.x = Min.x; + if (p.y > Max.y) p.y = Max.y; + else if (p.y < Min.y) p.y = Min.y; + return p; + } +}; + +// Stacked color modifier, backup of modified data so we can restore it +struct ImGuiColMod +{ + ImGuiCol Col; + ImVec4 BackupValue; +}; + +// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. +struct ImGuiStyleMod +{ + ImGuiStyleVar VarIdx; + union { int BackupInt[2]; float BackupFloat[2]; }; + ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } + ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } +}; + +// Stacked data for BeginGroup()/EndGroup() +struct ImGuiGroupData +{ + ImVec2 BackupCursorPos; + ImVec2 BackupCursorMaxPos; + float BackupIndentX; + float BackupGroupOffsetX; + float BackupCurrentLineHeight; + float BackupCurrentLineTextBaseOffset; + float BackupLogLinePosY; + bool BackupActiveIdIsAlive; + bool AdvanceCursor; +}; + +// Per column data for Columns() +struct ImGuiColumnData +{ + float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) + //float IndentX; +}; + +// Simple column measurement currently used for MenuItem() only. This is very short-sighted/throw-away code and NOT a generic helper. +struct IMGUI_API ImGuiSimpleColumns +{ + int Count; + float Spacing; + float Width, NextWidth; + float Pos[8], NextWidths[8]; + + ImGuiSimpleColumns(); + void Update(int count, float spacing, bool clear); + float DeclColumns(float w0, float w1, float w2); + float CalcExtraSpace(float avail_w); +}; + +// Internal state of the currently focused/edited text input box +struct IMGUI_API ImGuiTextEditState +{ + ImGuiID Id; // widget id owning the text state + ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. + ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) + ImVector TempTextBuffer; + int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. + int BufSizeA; // end-user buffer size + float ScrollX; + ImGuiStb::STB_TexteditState StbState; + float CursorAnim; + bool CursorFollow; + bool SelectedAllMouseLock; + + ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } + void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking + void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } + bool HasSelection() const { return StbState.select_start != StbState.select_end; } + void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } + void SelectAll() { StbState.select_start = 0; StbState.select_end = CurLenW; StbState.cursor = StbState.select_end; StbState.has_preferred_x = false; } + void OnKeyPressed(int key); +}; + +// Data saved in imgui.ini file +struct ImGuiIniData +{ + char* Name; + ImGuiID Id; + ImVec2 Pos; + ImVec2 Size; + bool Collapsed; +}; + +// Mouse cursor data (used when io.MouseDrawCursor is set) +struct ImGuiMouseCursorData +{ + ImGuiMouseCursor Type; + ImVec2 HotOffset; + ImVec2 Size; + ImVec2 TexUvMin[2]; + ImVec2 TexUvMax[2]; +}; + +// Storage for current popup stack +struct ImGuiPopupRef +{ + ImGuiID PopupId; // Set on OpenPopup() + ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() + ImGuiWindow* ParentWindow; // Set on OpenPopup() + ImGuiID ParentMenuSet; // Set on OpenPopup() + ImVec2 MousePosOnOpen; // Copy of mouse position at the time of opening popup + + ImGuiPopupRef(ImGuiID id, ImGuiWindow* parent_window, ImGuiID parent_menu_set, const ImVec2& mouse_pos) { PopupId = id; Window = NULL; ParentWindow = parent_window; ParentMenuSet = parent_menu_set; MousePosOnOpen = mouse_pos; } +}; + +// Main state for ImGui +struct ImGuiContext +{ + bool Initialized; + ImGuiIO IO; + ImGuiStyle Style; + ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() + float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize() + float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Size of characters. + ImVec2 FontTexUvWhitePixel; // (Shortcut) == Font->TexUvWhitePixel + + float Time; + int FrameCount; + int FrameCountEnded; + int FrameCountRendered; + ImVector Windows; + ImVector WindowsSortBuffer; + ImGuiWindow* CurrentWindow; // Being drawn into + ImVector CurrentWindowStack; + ImGuiWindow* FocusedWindow; // Will catch keyboard inputs + ImGuiWindow* HoveredWindow; // Will catch mouse inputs + ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) + ImGuiID HoveredId; // Hovered widget + bool HoveredIdAllowOverlap; + ImGuiID HoveredIdPreviousFrame; + ImGuiID ActiveId; // Active widget + ImGuiID ActiveIdPreviousFrame; + bool ActiveIdIsAlive; + bool ActiveIdIsJustActivated; // Set at the time of activation for one frame + bool ActiveIdAllowOverlap; // Set only by active widget + ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) + ImGuiWindow* ActiveIdWindow; + ImGuiWindow* MovedWindow; // Track the child window we clicked on to move a window. + ImGuiID MovedWindowMoveId; // == MovedWindow->RootWindow->MoveId + ImVector Settings; // .ini Settings + float SettingsDirtyTimer; // Save .ini Settings on disk when time reaches zero + ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() + ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() + ImVector FontStack; // Stack for PushFont()/PopFont() + ImVector OpenPopupStack; // Which popups are open (persistent) + ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) + + // Storage for SetNexWindow** and SetNextTreeNode*** functions + ImVec2 SetNextWindowPosVal; + ImVec2 SetNextWindowSizeVal; + ImVec2 SetNextWindowContentSizeVal; + bool SetNextWindowCollapsedVal; + ImGuiSetCond SetNextWindowPosCond; + ImGuiSetCond SetNextWindowSizeCond; + ImGuiSetCond SetNextWindowContentSizeCond; + ImGuiSetCond SetNextWindowCollapsedCond; + ImRect SetNextWindowSizeConstraintRect; // Valid if 'SetNextWindowSizeConstraint' is true + ImGuiSizeConstraintCallback SetNextWindowSizeConstraintCallback; + void* SetNextWindowSizeConstraintCallbackUserData; + bool SetNextWindowSizeConstraint; + bool SetNextWindowFocus; + bool SetNextTreeNodeOpenVal; + ImGuiSetCond SetNextTreeNodeOpenCond; + + // Render + ImDrawData RenderDrawData; // Main ImDrawData instance to pass render information to the user + ImVector RenderDrawLists[3]; + float ModalWindowDarkeningRatio; + ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays + ImGuiMouseCursor MouseCursor; + ImGuiMouseCursorData MouseCursorData[ImGuiMouseCursor_Count_]; + + // Widget state + ImGuiTextEditState InputTextState; + ImFont InputTextPasswordFont; + ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. + ImGuiStorage ColorEditModeStorage; // Store user selection of color edit mode + float DragCurrentValue; // Currently dragged value, always float, not rounded by end-user precision settings + ImVec2 DragLastMouseDelta; + float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio + float DragSpeedScaleSlow; + float DragSpeedScaleFast; + ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? + char Tooltip[1024]; + char* PrivateClipboard; // If no custom clipboard handler is defined + ImVec2 OsImePosRequest, OsImePosSet; // Cursor position request & last passed to the OS Input Method Editor + + // Logging + bool LogEnabled; + FILE* LogFile; // If != NULL log to stdout/ file + ImGuiTextBuffer* LogClipboard; // Else log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. + int LogStartDepth; + int LogAutoExpandMaxDepth; + + // Misc + float FramerateSecPerFrame[120]; // calculate estimate of framerate for user + int FramerateSecPerFrameIdx; + float FramerateSecPerFrameAccum; + int CaptureMouseNextFrame; // explicit capture via CaptureInputs() sets those flags + int CaptureKeyboardNextFrame; + char TempBuffer[1024*3+1]; // temporary text buffer + + ImGuiContext() + { + Initialized = false; + Font = NULL; + FontSize = FontBaseSize = 0.0f; + FontTexUvWhitePixel = ImVec2(0.0f, 0.0f); + + Time = 0.0f; + FrameCount = 0; + FrameCountEnded = FrameCountRendered = -1; + CurrentWindow = NULL; + FocusedWindow = NULL; + HoveredWindow = NULL; + HoveredRootWindow = NULL; + HoveredId = 0; + HoveredIdAllowOverlap = false; + HoveredIdPreviousFrame = 0; + ActiveId = 0; + ActiveIdPreviousFrame = 0; + ActiveIdIsAlive = false; + ActiveIdIsJustActivated = false; + ActiveIdAllowOverlap = false; + ActiveIdClickOffset = ImVec2(-1,-1); + ActiveIdWindow = NULL; + MovedWindow = NULL; + MovedWindowMoveId = 0; + SettingsDirtyTimer = 0.0f; + + SetNextWindowPosVal = ImVec2(0.0f, 0.0f); + SetNextWindowSizeVal = ImVec2(0.0f, 0.0f); + SetNextWindowCollapsedVal = false; + SetNextWindowPosCond = 0; + SetNextWindowSizeCond = 0; + SetNextWindowContentSizeCond = 0; + SetNextWindowCollapsedCond = 0; + SetNextWindowFocus = false; + SetNextWindowSizeConstraintCallback = NULL; + SetNextWindowSizeConstraintCallbackUserData = NULL; + SetNextTreeNodeOpenVal = false; + SetNextTreeNodeOpenCond = 0; + + ScalarAsInputTextId = 0; + DragCurrentValue = 0.0f; + DragLastMouseDelta = ImVec2(0.0f, 0.0f); + DragSpeedDefaultRatio = 1.0f / 100.0f; + DragSpeedScaleSlow = 0.01f; + DragSpeedScaleFast = 10.0f; + ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); + memset(Tooltip, 0, sizeof(Tooltip)); + PrivateClipboard = NULL; + OsImePosRequest = OsImePosSet = ImVec2(-1.0f, -1.0f); + + ModalWindowDarkeningRatio = 0.0f; + OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging + MouseCursor = ImGuiMouseCursor_Arrow; + memset(MouseCursorData, 0, sizeof(MouseCursorData)); + + LogEnabled = false; + LogFile = NULL; + LogClipboard = NULL; + LogStartDepth = 0; + LogAutoExpandMaxDepth = 2; + + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); + FramerateSecPerFrameIdx = 0; + FramerateSecPerFrameAccum = 0.0f; + CaptureMouseNextFrame = CaptureKeyboardNextFrame = -1; + memset(TempBuffer, 0, sizeof(TempBuffer)); + } +}; + +// Transient per-window data, reset at the beginning of the frame +// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiDrawContext is quite tenuous and could be reconsidered. +struct IMGUI_API ImGuiDrawContext +{ + ImVec2 CursorPos; + ImVec2 CursorPosPrevLine; + ImVec2 CursorStartPos; + ImVec2 CursorMaxPos; // Implicitly calculate the size of our contents, always extending. Saved into window->SizeContents at the end of the frame + float CurrentLineHeight; + float CurrentLineTextBaseOffset; + float PrevLineHeight; + float PrevLineTextBaseOffset; + float LogLinePosY; + int TreeDepth; + ImGuiID LastItemId; + ImRect LastItemRect; + bool LastItemHoveredAndUsable; // Item rectangle is hovered, and its window is currently interactable with (not blocked by a popup preventing access to the window) + bool LastItemHoveredRect; // Item rectangle is hovered, but its window may or not be currently interactable with (might be blocked by a popup preventing access to the window) + bool MenuBarAppending; + float MenuBarOffsetX; + ImVector ChildWindows; + ImGuiStorage* StateStorage; + ImGuiLayoutType LayoutType; + + // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. + float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window + float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] + bool AllowKeyboardFocus; // == AllowKeyboardFocusStack.back() [empty == true] + bool ButtonRepeat; // == ButtonRepeatStack.back() [empty == false] + ImVector ItemWidthStack; + ImVector TextWrapPosStack; + ImVector AllowKeyboardFocusStack; + ImVector ButtonRepeatStack; + ImVectorGroupStack; + ImGuiColorEditMode ColorEditMode; + int StackSizesBackup[6]; // Store size of various stacks for asserting + + float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) + float GroupOffsetX; + float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. + int ColumnsCurrent; + int ColumnsCount; + float ColumnsMinX; + float ColumnsMaxX; + float ColumnsStartPosY; + float ColumnsCellMinY; + float ColumnsCellMaxY; + bool ColumnsShowBorders; + ImGuiID ColumnsSetId; + ImVector ColumnsData; + + ImGuiDrawContext() + { + CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); + CurrentLineHeight = PrevLineHeight = 0.0f; + CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; + LogLinePosY = -1.0f; + TreeDepth = 0; + LastItemId = 0; + LastItemRect = ImRect(0.0f,0.0f,0.0f,0.0f); + LastItemHoveredAndUsable = LastItemHoveredRect = false; + MenuBarAppending = false; + MenuBarOffsetX = 0.0f; + StateStorage = NULL; + LayoutType = ImGuiLayoutType_Vertical; + ItemWidth = 0.0f; + ButtonRepeat = false; + AllowKeyboardFocus = true; + TextWrapPos = -1.0f; + ColorEditMode = ImGuiColorEditMode_RGB; + memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); + + IndentX = 0.0f; + ColumnsOffsetX = 0.0f; + ColumnsCurrent = 0; + ColumnsCount = 1; + ColumnsMinX = ColumnsMaxX = 0.0f; + ColumnsStartPosY = 0.0f; + ColumnsCellMinY = ColumnsCellMaxY = 0.0f; + ColumnsShowBorders = true; + ColumnsSetId = 0; + } +}; + +// Windows data +struct IMGUI_API ImGuiWindow +{ + char* Name; + ImGuiID ID; // == ImHash(Name) + ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ + int IndexWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. + ImVec2 PosFloat; + ImVec2 Pos; // Position rounded-up to nearest pixel + ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) + ImVec2 SizeFull; // Size when non collapsed + ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame + ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() + ImRect ContentsRegionRect; // Maximum visible content position in window coordinates. ~~ (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis + ImVec2 WindowPadding; // Window padding at the time of begin. We need to lock it, in particular manipulation of the ShowBorder would have an effect + ImGuiID MoveId; // == window->GetID("#MOVE") + ImVec2 Scroll; + ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) + ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered + bool ScrollbarX, ScrollbarY; + ImVec2 ScrollbarSizes; + float BorderSize; + bool Active; // Set to true on Begin() + bool WasActive; + bool Accessed; // Set to true when any widget access the current window + bool Collapsed; // Set when collapsing window to become only title-bar + bool SkipItems; // == Visible && !Collapsed + int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) + int AutoFitFramesX, AutoFitFramesY; + bool AutoFitOnlyGrows; + int AutoPosLastDirection; + int HiddenFrames; + int SetWindowPosAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowPos() call will succeed with this particular flag. + int SetWindowSizeAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowSize() call will succeed with this particular flag. + int SetWindowCollapsedAllowFlags; // bit ImGuiSetCond_*** specify if SetWindowCollapsed() call will succeed with this particular flag. + bool SetWindowPosCenterWanted; + + ImGuiDrawContext DC; // Temporary per-window data, reset at the beginning of the frame + ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack + ImRect ClipRect; // = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. + ImRect WindowRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. + int LastFrameActive; + float ItemWidthDefault; + ImGuiSimpleColumns MenuColumns; // Simplified columns storage for menu items + ImGuiStorage StateStorage; + float FontWindowScale; // Scale multiplier per-window + ImDrawList* DrawList; + ImGuiWindow* RootWindow; // If we are a child window, this is pointing to the first non-child parent window. Else point to ourself. + ImGuiWindow* RootNonPopupWindow; // If we are a child window, this is pointing to the first non-child non-popup parent window. Else point to ourself. + ImGuiWindow* ParentWindow; // If we are a child window, this is pointing to our parent window. Else point to NULL. + + // Navigation / Focus + int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() + int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) + int FocusIdxAllRequestCurrent; // Item being requested for focus + int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus + int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) + int FocusIdxTabRequestNext; // " + +public: + ImGuiWindow(const char* name); + ~ImGuiWindow(); + + ImGuiID GetID(const char* str, const char* str_end = NULL); + ImGuiID GetID(const void* ptr); + ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); + + ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } + float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } + float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } + ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } + float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } + ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } +}; + +//----------------------------------------------------------------------------- +// Internal API +// No guarantee of forward compatibility here. +//----------------------------------------------------------------------------- + +namespace ImGui +{ + // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) + // If this ever crash because g.CurrentWindow is NULL it means that either + // - ImGui::NewFrame() has never been called, which is illegal. + // - You are calling ImGui functions after ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. + inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } + inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->Accessed = true; return g.CurrentWindow; } + IMGUI_API ImGuiWindow* GetParentWindow(); + IMGUI_API ImGuiWindow* FindWindowByName(const char* name); + IMGUI_API void FocusWindow(ImGuiWindow* window); + + IMGUI_API void EndFrame(); // Ends the ImGui frame. Automatically called by Render()! you most likely don't need to ever call that yourself directly. If you don't need to render you can call EndFrame() but you'll have wasted CPU already. If you don't need to render, don't create any windows instead! + + IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); + IMGUI_API void SetHoveredID(ImGuiID id); + IMGUI_API void KeepAliveID(ImGuiID id); + + IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); + IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); + IMGUI_API bool ItemAdd(const ImRect& bb, const ImGuiID* id); + IMGUI_API bool IsClippedEx(const ImRect& bb, const ImGuiID* id, bool clip_even_when_logged); + IMGUI_API bool IsHovered(const ImRect& bb, ImGuiID id, bool flatten_childs = false); + IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, bool is_active, bool tab_stop = true); // Return true if focus is requested + IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); + IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); + IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); + + IMGUI_API void OpenPopupEx(const char* str_id, bool reopen_existing); + + // NB: All position are in absolute pixels coordinates (not window coordinates) + // FIXME: All those functions are a mess and needs to be refactored into something decent. AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. + // We need: a sort of symbol library, preferably baked into font atlas when possible + decent text rendering helpers. + IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); + IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); + IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); + IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); + IMGUI_API void RenderCollapseTriangle(ImVec2 pos, bool is_open, float scale = 1.0f); + IMGUI_API void RenderBullet(ImVec2 pos); + IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col); + IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. + + IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); + IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); + IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); + + IMGUI_API bool SliderBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_min, float v_max, float power, int decimal_precision, ImGuiSliderFlags flags = 0); + IMGUI_API bool SliderFloatN(const char* label, float* v, int components, float v_min, float v_max, const char* display_format, float power); + IMGUI_API bool SliderIntN(const char* label, int* v, int components, int v_min, int v_max, const char* display_format); + + IMGUI_API bool DragBehavior(const ImRect& frame_bb, ImGuiID id, float* v, float v_speed, float v_min, float v_max, int decimal_precision, float power); + IMGUI_API bool DragFloatN(const char* label, float* v, int components, float v_speed, float v_min, float v_max, const char* display_format, float power); + IMGUI_API bool DragIntN(const char* label, int* v, int components, float v_speed, int v_min, int v_max, const char* display_format); + + IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); + IMGUI_API bool InputFloatN(const char* label, float* v, int components, int decimal_precision, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputIntN(const char* label, int* v, int components, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputScalarEx(const char* label, ImGuiDataType data_type, void* data_ptr, void* step_ptr, void* step_fast_ptr, const char* scalar_format, ImGuiInputTextFlags extra_flags); + IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& aabb, const char* label, ImGuiDataType data_type, void* data_ptr, ImGuiID id, int decimal_precision); + + IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); + IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging + IMGUI_API void TreePushRawID(ImGuiID id); + + IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); + + IMGUI_API int ParseFormatPrecision(const char* fmt, int default_value); + IMGUI_API float RoundScalar(float value, int decimal_precision); + +} // namespace ImGui + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef _MSC_VER +#pragma warning (pop) +#endif diff --git a/apps/exampleViewer/common/imgui/stb_rect_pack.h b/apps/exampleViewer/common/imgui/stb_rect_pack.h new file mode 100644 index 0000000000..fafd889716 --- /dev/null +++ b/apps/exampleViewer/common/imgui/stb_rect_pack.h @@ -0,0 +1,573 @@ +// stb_rect_pack.h - v0.08 - public domain - rectangle packing +// Sean Barrett 2014 +// +// Useful for e.g. packing rectangular textures into an atlas. +// Does not do rotation. +// +// Not necessarily the awesomest packing method, but better than +// the totally naive one in stb_truetype (which is primarily what +// this is meant to replace). +// +// Has only had a few tests run, may have issues. +// +// More docs to come. +// +// No memory allocations; uses qsort() and assert() from stdlib. +// Can override those by defining STBRP_SORT and STBRP_ASSERT. +// +// This library currently uses the Skyline Bottom-Left algorithm. +// +// Please note: better rectangle packers are welcome! Please +// implement them to the same API, but with a different init +// function. +// +// Credits +// +// Library +// Sean Barrett +// Minor features +// Martins Mozeiko +// Bugfixes / warning fixes +// Jeremy Jaussaud +// +// Version history: +// +// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) +// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) +// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort +// 0.05: added STBRP_ASSERT to allow replacing assert +// 0.04: fixed minor bug in STBRP_LARGE_RECTS support +// 0.01: initial release +// +// LICENSE +// +// This software is in the public domain. Where that dedication is not +// recognized, you are granted a perpetual, irrevocable license to copy, +// distribute, and modify this file as you see fit. + +////////////////////////////////////////////////////////////////////////////// +// +// INCLUDE SECTION +// + +#ifndef STB_INCLUDE_STB_RECT_PACK_H +#define STB_INCLUDE_STB_RECT_PACK_H + +#define STB_RECT_PACK_VERSION 1 + +#ifdef STBRP_STATIC +#define STBRP_DEF static +#else +#define STBRP_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct stbrp_context stbrp_context; +typedef struct stbrp_node stbrp_node; +typedef struct stbrp_rect stbrp_rect; + +#ifdef STBRP_LARGE_RECTS +typedef int stbrp_coord; +#else +typedef unsigned short stbrp_coord; +#endif + +STBRP_DEF void stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); +// Assign packed locations to rectangles. The rectangles are of type +// 'stbrp_rect' defined below, stored in the array 'rects', and there +// are 'num_rects' many of them. +// +// Rectangles which are successfully packed have the 'was_packed' flag +// set to a non-zero value and 'x' and 'y' store the minimum location +// on each axis (i.e. bottom-left in cartesian coordinates, top-left +// if you imagine y increasing downwards). Rectangles which do not fit +// have the 'was_packed' flag set to 0. +// +// You should not try to access the 'rects' array from another thread +// while this function is running, as the function temporarily reorders +// the array while it executes. +// +// To pack into another rectangle, you need to call stbrp_init_target +// again. To continue packing into the same rectangle, you can call +// this function again. Calling this multiple times with multiple rect +// arrays will probably produce worse packing results than calling it +// a single time with the full rectangle array, but the option is +// available. + +struct stbrp_rect +{ + // reserved for your use: + int id; + + // input: + stbrp_coord w, h; + + // output: + stbrp_coord x, y; + int was_packed; // non-zero if valid packing + +}; // 16 bytes, nominally + + +STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); +// Initialize a rectangle packer to: +// pack a rectangle that is 'width' by 'height' in dimensions +// using temporary storage provided by the array 'nodes', which is 'num_nodes' long +// +// You must call this function every time you start packing into a new target. +// +// There is no "shutdown" function. The 'nodes' memory must stay valid for +// the following stbrp_pack_rects() call (or calls), but can be freed after +// the call (or calls) finish. +// +// Note: to guarantee best results, either: +// 1. make sure 'num_nodes' >= 'width' +// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' +// +// If you don't do either of the above things, widths will be quantized to multiples +// of small integers to guarantee the algorithm doesn't run out of temporary storage. +// +// If you do #2, then the non-quantized algorithm will be used, but the algorithm +// may run out of temporary storage and be unable to pack some rectangles. + +STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); +// Optionally call this function after init but before doing any packing to +// change the handling of the out-of-temp-memory scenario, described above. +// If you call init again, this will be reset to the default (false). + + +STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); +// Optionally select which packing heuristic the library should use. Different +// heuristics will produce better/worse results for different data sets. +// If you call init again, this will be reset to the default. + +enum +{ + STBRP_HEURISTIC_Skyline_default=0, + STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, + STBRP_HEURISTIC_Skyline_BF_sortHeight +}; + + +////////////////////////////////////////////////////////////////////////////// +// +// the details of the following structures don't matter to you, but they must +// be visible so you can handle the memory allocations for them + +struct stbrp_node +{ + stbrp_coord x,y; + stbrp_node *next; +}; + +struct stbrp_context +{ + int width; + int height; + int align; + int init_mode; + int heuristic; + int num_nodes; + stbrp_node *active_head; + stbrp_node *free_head; + stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' +}; + +#ifdef __cplusplus +} +#endif + +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION SECTION +// + +#ifdef STB_RECT_PACK_IMPLEMENTATION +#ifndef STBRP_SORT +#include +#define STBRP_SORT qsort +#endif + +#ifndef STBRP_ASSERT +#include +#define STBRP_ASSERT assert +#endif + +enum +{ + STBRP__INIT_skyline = 1 +}; + +STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) +{ + switch (context->init_mode) { + case STBRP__INIT_skyline: + STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); + context->heuristic = heuristic; + break; + default: + STBRP_ASSERT(0); + } +} + +STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) +{ + if (allow_out_of_mem) + // if it's ok to run out of memory, then don't bother aligning them; + // this gives better packing, but may fail due to OOM (even though + // the rectangles easily fit). @TODO a smarter approach would be to only + // quantize once we've hit OOM, then we could get rid of this parameter. + context->align = 1; + else { + // if it's not ok to run out of memory, then quantize the widths + // so that num_nodes is always enough nodes. + // + // I.e. num_nodes * align >= width + // align >= width / num_nodes + // align = ceil(width/num_nodes) + + context->align = (context->width + context->num_nodes-1) / context->num_nodes; + } +} + +STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) +{ + int i; +#ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(width <= 0xffff && height <= 0xffff); +#endif + + for (i=0; i < num_nodes-1; ++i) + nodes[i].next = &nodes[i+1]; + nodes[i].next = NULL; + context->init_mode = STBRP__INIT_skyline; + context->heuristic = STBRP_HEURISTIC_Skyline_default; + context->free_head = &nodes[0]; + context->active_head = &context->extra[0]; + context->width = width; + context->height = height; + context->num_nodes = num_nodes; + stbrp_setup_allow_out_of_mem(context, 0); + + // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) + context->extra[0].x = 0; + context->extra[0].y = 0; + context->extra[0].next = &context->extra[1]; + context->extra[1].x = (stbrp_coord) width; +#ifdef STBRP_LARGE_RECTS + context->extra[1].y = (1<<30); +#else + context->extra[1].y = 65535; +#endif + context->extra[1].next = NULL; +} + +// find minimum y position if it starts at x1 +static int stbrp__skyline_find_min_y(stbrp_context *, stbrp_node *first, int x0, int width, int *pwaste) +{ + //(void)c; + stbrp_node *node = first; + int x1 = x0 + width; + int min_y, visited_width, waste_area; + STBRP_ASSERT(first->x <= x0); + + #if 0 + // skip in case we're past the node + while (node->next->x <= x0) + ++node; + #else + STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency + #endif + + STBRP_ASSERT(node->x <= x0); + + min_y = 0; + waste_area = 0; + visited_width = 0; + while (node->x < x1) { + if (node->y > min_y) { + // raise min_y higher. + // we've accounted for all waste up to min_y, + // but we'll now add more waste for everything we've visted + waste_area += visited_width * (node->y - min_y); + min_y = node->y; + // the first time through, visited_width might be reduced + if (node->x < x0) + visited_width += node->next->x - x0; + else + visited_width += node->next->x - node->x; + } else { + // add waste area + int under_width = node->next->x - node->x; + if (under_width + visited_width > width) + under_width = width - visited_width; + waste_area += under_width * (min_y - node->y); + visited_width += under_width; + } + node = node->next; + } + + *pwaste = waste_area; + return min_y; +} + +typedef struct +{ + int x,y; + stbrp_node **prev_link; +} stbrp__findresult; + +static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) +{ + int best_waste = (1<<30), best_x, best_y = (1 << 30); + stbrp__findresult fr; + stbrp_node **prev, *node, *tail, **best = NULL; + + // align to multiple of c->align + width = (width + c->align - 1); + width -= width % c->align; + STBRP_ASSERT(width % c->align == 0); + + node = c->active_head; + prev = &c->active_head; + while (node->x + width <= c->width) { + int y,waste; + y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); + if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL + // bottom left + if (y < best_y) { + best_y = y; + best = prev; + } + } else { + // best-fit + if (y + height <= c->height) { + // can only use it if it first vertically + if (y < best_y || (y == best_y && waste < best_waste)) { + best_y = y; + best_waste = waste; + best = prev; + } + } + } + prev = &node->next; + node = node->next; + } + + best_x = (best == NULL) ? 0 : (*best)->x; + + // if doing best-fit (BF), we also have to try aligning right edge to each node position + // + // e.g, if fitting + // + // ____________________ + // |____________________| + // + // into + // + // | | + // | ____________| + // |____________| + // + // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned + // + // This makes BF take about 2x the time + + if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { + tail = c->active_head; + node = c->active_head; + prev = &c->active_head; + // find first node that's admissible + while (tail->x < width) + tail = tail->next; + while (tail) { + int xpos = tail->x - width; + int y,waste; + STBRP_ASSERT(xpos >= 0); + // find the left position that matches this + while (node->next->x <= xpos) { + prev = &node->next; + node = node->next; + } + STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); + y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); + if (y + height < c->height) { + if (y <= best_y) { + if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { + best_x = xpos; + STBRP_ASSERT(y <= best_y); + best_y = y; + best_waste = waste; + best = prev; + } + } + } + tail = tail->next; + } + } + + fr.prev_link = best; + fr.x = best_x; + fr.y = best_y; + return fr; +} + +static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) +{ + // find best position according to heuristic + stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); + stbrp_node *node, *cur; + + // bail if: + // 1. it failed + // 2. the best node doesn't fit (we don't always check this) + // 3. we're out of memory + if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { + res.prev_link = NULL; + return res; + } + + // on success, create new node + node = context->free_head; + node->x = (stbrp_coord) res.x; + node->y = (stbrp_coord) (res.y + height); + + context->free_head = node->next; + + // insert the new node into the right starting point, and + // let 'cur' point to the remaining nodes needing to be + // stiched back in + + cur = *res.prev_link; + if (cur->x < res.x) { + // preserve the existing one, so start testing with the next one + stbrp_node *next = cur->next; + cur->next = node; + cur = next; + } else { + *res.prev_link = node; + } + + // from here, traverse cur and free the nodes, until we get to one + // that shouldn't be freed + while (cur->next && cur->next->x <= res.x + width) { + stbrp_node *next = cur->next; + // move the current node to the free list + cur->next = context->free_head; + context->free_head = cur; + cur = next; + } + + // stitch the list back in + node->next = cur; + + if (cur->x < res.x + width) + cur->x = (stbrp_coord) (res.x + width); + +#ifdef _DEBUG + cur = context->active_head; + while (cur->x < context->width) { + STBRP_ASSERT(cur->x < cur->next->x); + cur = cur->next; + } + STBRP_ASSERT(cur->next == NULL); + + { + stbrp_node *L1 = NULL, *L2 = NULL; + int count=0; + cur = context->active_head; + while (cur) { + L1 = cur; + cur = cur->next; + ++count; + } + cur = context->free_head; + while (cur) { + L2 = cur; + cur = cur->next; + ++count; + } + STBRP_ASSERT(count == context->num_nodes+2); + } +#endif + + return res; +} + +static int rect_height_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->h > q->h) + return -1; + if (p->h < q->h) + return 1; + return (p->w > q->w) ? -1 : (p->w < q->w); +} + +static int rect_width_compare(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + if (p->w > q->w) + return -1; + if (p->w < q->w) + return 1; + return (p->h > q->h) ? -1 : (p->h < q->h); +} + +static int rect_original_order(const void *a, const void *b) +{ + stbrp_rect *p = (stbrp_rect *) a; + stbrp_rect *q = (stbrp_rect *) b; + return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); +} + +#ifdef STBRP_LARGE_RECTS +#define STBRP__MAXVAL 0xffffffff +#else +#define STBRP__MAXVAL 0xffff +#endif + +STBRP_DEF void stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) +{ + int i; + + // we use the 'was_packed' field internally to allow sorting/unsorting + for (i=0; i < num_rects; ++i) { + rects[i].was_packed = i; + #ifndef STBRP_LARGE_RECTS + STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); + #endif + } + + // sort according to heuristic + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); + + for (i=0; i < num_rects; ++i) { + if (rects[i].w == 0 || rects[i].h == 0) { + rects[i].x = rects[i].y = 0; // empty rect needs no space + } else { + stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); + if (fr.prev_link) { + rects[i].x = (stbrp_coord) fr.x; + rects[i].y = (stbrp_coord) fr.y; + } else { + rects[i].x = rects[i].y = STBRP__MAXVAL; + } + } + } + + // unsort + STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); + + // set was_packed flags + for (i=0; i < num_rects; ++i) + rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); +} +#endif diff --git a/apps/exampleViewer/common/imgui/stb_textedit.h b/apps/exampleViewer/common/imgui/stb_textedit.h new file mode 100644 index 0000000000..4b731a0c22 --- /dev/null +++ b/apps/exampleViewer/common/imgui/stb_textedit.h @@ -0,0 +1,1322 @@ +// [ImGui] this is a slightly modified version of stb_truetype.h 1.9. Those changes would need to be pushed into nothings/sb +// [ImGui] - fixed linestart handler when over last character of multi-line buffer + simplified existing code (#588, #815) +// [ImGui] - fixed a state corruption/crash bug in stb_text_redo and stb_textedit_discard_redo (#715) +// [ImGui] - fixed a crash bug in stb_textedit_discard_redo (#681) +// [ImGui] - fixed some minor warnings + +// stb_textedit.h - v1.9 - public domain - Sean Barrett +// Development of this library was sponsored by RAD Game Tools +// +// This C header file implements the guts of a multi-line text-editing +// widget; you implement display, word-wrapping, and low-level string +// insertion/deletion, and stb_textedit will map user inputs into +// insertions & deletions, plus updates to the cursor position, +// selection state, and undo state. +// +// It is intended for use in games and other systems that need to build +// their own custom widgets and which do not have heavy text-editing +// requirements (this library is not recommended for use for editing large +// texts, as its performance does not scale and it has limited undo). +// +// Non-trivial behaviors are modelled after Windows text controls. +// +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// +// DEPENDENCIES +// +// Uses the C runtime function 'memmove', which you can override +// by defining STB_TEXTEDIT_memmove before the implementation. +// Uses no other functions. Performs no runtime allocations. +// +// +// VERSION HISTORY +// +// 1.9 (2016-08-27) customizable move-by-word +// 1.8 (2016-04-02) better keyboard handling when mouse button is down +// 1.7 (2015-09-13) change y range handling in case baseline is non-0 +// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove +// 1.5 (2014-09-10) add support for secondary keys for OS X +// 1.4 (2014-08-17) fix signed/unsigned warnings +// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary +// 1.2 (2014-05-27) fix some RAD types that had crept into the new code +// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) +// 1.0 (2012-07-26) improve documentation, initial public release +// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode +// 0.2 (2011-11-28) fixes to undo/redo +// 0.1 (2010-07-08) initial version +// +// ADDITIONAL CONTRIBUTORS +// +// Ulf Winklemann: move-by-word in 1.1 +// Fabian Giesen: secondary key inputs in 1.5 +// Martins Mozeiko: STB_TEXTEDIT_memmove +// +// Bugfixes: +// Scott Graham +// Daniel Keller +// Omar Cornut +// +// USAGE +// +// This file behaves differently depending on what symbols you define +// before including it. +// +// +// Header-file mode: +// +// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, +// it will operate in "header file" mode. In this mode, it declares a +// single public symbol, STB_TexteditState, which encapsulates the current +// state of a text widget (except for the string, which you will store +// separately). +// +// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a +// primitive type that defines a single character (e.g. char, wchar_t, etc). +// +// To save space or increase undo-ability, you can optionally define the +// following things that are used by the undo system: +// +// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// If you don't define these, they are set to permissive types and +// moderate sizes. The undo system does no memory allocations, so +// it grows STB_TexteditState by the worst-case storage which is (in bytes): +// +// [4 + sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT +// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT +// +// +// Implementation mode: +// +// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it +// will compile the implementation of the text edit widget, depending +// on a large number of symbols which must be defined before the include. +// +// The implementation is defined only as static functions. You will then +// need to provide your own APIs in the same file which will access the +// static functions. +// +// The basic concept is that you provide a "string" object which +// behaves like an array of characters. stb_textedit uses indices to +// refer to positions in the string, implicitly representing positions +// in the displayed textedit. This is true for both plain text and +// rich text; even with rich text stb_truetype interacts with your +// code as if there was an array of all the displayed characters. +// +// Symbols that must be the same in header-file and implementation mode: +// +// STB_TEXTEDIT_CHARTYPE the character type +// STB_TEXTEDIT_POSITIONTYPE small type that a valid cursor position +// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow +// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer +// +// Symbols you must define for implementation mode: +// +// STB_TEXTEDIT_STRING the type of object representing a string being edited, +// typically this is a wrapper object with other data you need +// +// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) +// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters +// starting from character #n (see discussion below) +// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character +// to the xpos of the i+1'th char for a line of characters +// starting at character #n (i.e. accounts for kerning +// with previous char) +// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character +// (return type is int, -1 means not valid to insert) +// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based +// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize +// as manually wordwrapping for end-of-line positioning +// +// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i +// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) +// +// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key +// +// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left +// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right +// STB_TEXTEDIT_K_UP keyboard input to move cursor up +// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down +// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME +// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END +// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME +// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END +// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor +// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor +// STB_TEXTEDIT_K_UNDO keyboard input to perform undo +// STB_TEXTEDIT_K_REDO keyboard input to perform redo +// +// Optional: +// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode +// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), +// required for default WORDLEFT/WORDRIGHT handlers +// STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to +// STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to +// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT +// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT +// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line +// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line +// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text +// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text +// +// Todo: +// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page +// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page +// +// Keyboard input must be encoded as a single integer value; e.g. a character code +// and some bitflags that represent shift states. to simplify the interface, SHIFT must +// be a bitflag, so we can test the shifted state of cursor movements to allow selection, +// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. +// +// You can encode other things, such as CONTROL or ALT, in additional bits, and +// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, +// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN +// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, +// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the +// API below. The control keys will only match WM_KEYDOWN events because of the +// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN +// bit so it only decodes WM_CHAR events. +// +// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed +// row of characters assuming they start on the i'th character--the width and +// the height and the number of characters consumed. This allows this library +// to traverse the entire layout incrementally. You need to compute word-wrapping +// here. +// +// Each textfield keeps its own insert mode state, which is not how normal +// applications work. To keep an app-wide insert mode, update/copy the +// "insert_mode" field of STB_TexteditState before/after calling API functions. +// +// API +// +// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +// +// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) +// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +// +// Each of these functions potentially updates the string and updates the +// state. +// +// initialize_state: +// set the textedit state to a known good default state when initially +// constructing the textedit. +// +// click: +// call this with the mouse x,y on a mouse down; it will update the cursor +// and reset the selection start/end to the cursor point. the x,y must +// be relative to the text widget, with (0,0) being the top left. +// +// drag: +// call this with the mouse x,y on a mouse drag/up; it will update the +// cursor and the selection end point +// +// cut: +// call this to delete the current selection; returns true if there was +// one. you should FIRST copy the current selection to the system paste buffer. +// (To copy, just copy the current selection out of the string yourself.) +// +// paste: +// call this to paste text at the current cursor point or over the current +// selection if there is one. +// +// key: +// call this for keyboard inputs sent to the textfield. you can use it +// for "key down" events or for "translated" key events. if you need to +// do both (as in Win32), or distinguish Unicode characters from control +// inputs, set a high bit to distinguish the two; then you can define the +// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit +// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is +// clear. +// +// When rendering, you can read the cursor position and selection state from +// the STB_TexteditState. +// +// +// Notes: +// +// This is designed to be usable in IMGUI, so it allows for the possibility of +// running in an IMGUI that has NOT cached the multi-line layout. For this +// reason, it provides an interface that is compatible with computing the +// layout incrementally--we try to make sure we make as few passes through +// as possible. (For example, to locate the mouse pointer in the text, we +// could define functions that return the X and Y positions of characters +// and binary search Y and then X, but if we're doing dynamic layout this +// will run the layout algorithm many times, so instead we manually search +// forward in one pass. Similar logic applies to e.g. up-arrow and +// down-arrow movement.) +// +// If it's run in a widget that *has* cached the layout, then this is less +// efficient, but it's not horrible on modern computers. But you wouldn't +// want to edit million-line files with it. + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Header-file mode +//// +//// + +#ifndef INCLUDE_STB_TEXTEDIT_H +#define INCLUDE_STB_TEXTEDIT_H + +//////////////////////////////////////////////////////////////////////// +// +// STB_TexteditState +// +// Definition of STB_TexteditState which you should store +// per-textfield; it includes cursor position, selection state, +// and undo state. +// + +#ifndef STB_TEXTEDIT_UNDOSTATECOUNT +#define STB_TEXTEDIT_UNDOSTATECOUNT 99 +#endif +#ifndef STB_TEXTEDIT_UNDOCHARCOUNT +#define STB_TEXTEDIT_UNDOCHARCOUNT 999 +#endif +#ifndef STB_TEXTEDIT_CHARTYPE +#define STB_TEXTEDIT_CHARTYPE int +#endif +#ifndef STB_TEXTEDIT_POSITIONTYPE +#define STB_TEXTEDIT_POSITIONTYPE int +#endif + +typedef struct +{ + // private data + STB_TEXTEDIT_POSITIONTYPE where; + short insert_length; + short delete_length; + short char_storage; +} StbUndoRecord; + +typedef struct +{ + // private data + StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; + STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; + short undo_point, redo_point; + short undo_char_point, redo_char_point; +} StbUndoState; + +typedef struct +{ + ///////////////////// + // + // public data + // + + int cursor; + // position of the text cursor within the string + + int select_start; // selection start point + int select_end; + // selection start and end point in characters; if equal, no selection. + // note that start may be less than or greater than end (e.g. when + // dragging the mouse, start is where the initial click was, and you + // can drag in either direction) + + unsigned char insert_mode; + // each textfield keeps its own insert mode state. to keep an app-wide + // insert mode, copy this value in/out of the app state + + ///////////////////// + // + // private data + // + unsigned char cursor_at_end_of_line; // not implemented yet + unsigned char initialized; + unsigned char has_preferred_x; + unsigned char single_line; + unsigned char padding1, padding2, padding3; + float preferred_x; // this determines where the cursor up/down tries to seek to along x + StbUndoState undostate; +} STB_TexteditState; + + +//////////////////////////////////////////////////////////////////////// +// +// StbTexteditRow +// +// Result of layout query, used by stb_textedit to determine where +// the text in each row is. + +// result of layout query +typedef struct +{ + float x0,x1; // starting x location, end x location (allows for align=right, etc) + float baseline_y_delta; // position of baseline relative to previous row's baseline + float ymin,ymax; // height of row above and below baseline + int num_chars; +} StbTexteditRow; +#endif //INCLUDE_STB_TEXTEDIT_H + + +//////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////// +//// +//// Implementation mode +//// +//// + + +// implementation isn't include-guarded, since it might have indirectly +// included just the "header" portion +#ifdef STB_TEXTEDIT_IMPLEMENTATION + +#ifndef STB_TEXTEDIT_memmove +#include +#define STB_TEXTEDIT_memmove memmove +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// Mouse input handling +// + +// traverse the layout to locate the nearest character to a display position +static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) +{ + StbTexteditRow r; + int n = STB_TEXTEDIT_STRINGLEN(str); + float base_y = 0, prev_x; + int i=0, k; + + r.x0 = r.x1 = 0; + r.ymin = r.ymax = 0; + r.num_chars = 0; + + // search rows to find one that straddles 'y' + while (i < n) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (r.num_chars <= 0) + return n; + + if (i==0 && y < base_y + r.ymin) + return 0; + + if (y < base_y + r.ymax) + break; + + i += r.num_chars; + base_y += r.baseline_y_delta; + } + + // below all text, return 'after' last character + if (i >= n) + return n; + + // check if it's before the beginning of the line + if (x < r.x0) + return i; + + // check if it's before the end of the line + if (x < r.x1) { + // search characters in row for one that straddles 'x' + prev_x = r.x0; + for (k=0; k < r.num_chars; ++k) { + float w = STB_TEXTEDIT_GETWIDTH(str, i, k); + if (x < prev_x+w) { + if (x < prev_x+w/2) + return k+i; + else + return k+i+1; + } + prev_x += w; + } + // shouldn't happen, but if it does, fall through to end-of-line case + } + + // if the last character is a newline, return that. otherwise return 'after' the last character + if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) + return i+r.num_chars-1; + else + return i+r.num_chars; +} + +// API click: on mouse down, move the cursor to the clicked location, and reset the selection +static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + state->cursor = stb_text_locate_coord(str, x, y); + state->select_start = state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; +} + +// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location +static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) +{ + int p = stb_text_locate_coord(str, x, y); + if (state->select_start == state->select_end) + state->select_start = state->cursor; + state->cursor = state->select_end = p; +} + +///////////////////////////////////////////////////////////////////////////// +// +// Keyboard input handling +// + +// forward declarations +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); + +typedef struct +{ + float x,y; // position of n'th character + float height; // height of line + int first_char, length; // first char of row, and length + int prev_first; // first char of previous row +} StbFindState; + +// find the x/y location of a character, and remember info about the previous row in +// case we get a move-up event (for page up, we'll have to rescan) +static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) +{ + StbTexteditRow r; + int prev_start = 0; + int z = STB_TEXTEDIT_STRINGLEN(str); + int i=0, first; + + if (n == z) { + // if it's at the end, then find the last line -- simpler than trying to + // explicitly handle this case in the regular code + if (single_line) { + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; + } else { + find->y = 0; + find->x = 0; + find->height = 1; + while (i < z) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + prev_start = i; + i += r.num_chars; + } + find->first_char = i; + find->length = 0; + find->prev_first = prev_start; + } + return; + } + + // search rows to find the one that straddles character n + find->y = 0; + + for(;;) { + STB_TEXTEDIT_LAYOUTROW(&r, str, i); + if (n < i + r.num_chars) + break; + prev_start = i; + i += r.num_chars; + find->y += r.baseline_y_delta; + } + + find->first_char = first = i; + find->length = r.num_chars; + find->height = r.ymax - r.ymin; + find->prev_first = prev_start; + + // now scan to find xpos + find->x = r.x0; + i = 0; + for (i=0; first+i < n; ++i) + find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); +} + +#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) + +// make the selection/cursor state valid if client altered the string +static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + int n = STB_TEXTEDIT_STRINGLEN(str); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start > n) state->select_start = n; + if (state->select_end > n) state->select_end = n; + // if clamping forced them to be equal, move the cursor to match + if (state->select_start == state->select_end) + state->cursor = state->select_start; + } + if (state->cursor > n) state->cursor = n; +} + +// delete characters while updating undo +static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) +{ + stb_text_makeundo_delete(str, state, where, len); + STB_TEXTEDIT_DELETECHARS(str, where, len); + state->has_preferred_x = 0; +} + +// delete the section +static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + stb_textedit_clamp(str, state); + if (STB_TEXT_HAS_SELECTION(state)) { + if (state->select_start < state->select_end) { + stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); + state->select_end = state->cursor = state->select_start; + } else { + stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); + state->select_start = state->cursor = state->select_end; + } + state->has_preferred_x = 0; + } +} + +// canoncialize the selection so start <= end +static void stb_textedit_sortselection(STB_TexteditState *state) +{ + if (state->select_end < state->select_start) { + int temp = state->select_end; + state->select_end = state->select_start; + state->select_start = temp; + } +} + +// move cursor to first character of selection +static void stb_textedit_move_to_first(STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + state->cursor = state->select_start; + state->select_end = state->select_start; + state->has_preferred_x = 0; + } +} + +// move cursor to last character of selection +static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_sortselection(state); + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->select_start = state->select_end; + state->has_preferred_x = 0; + } +} + +#ifdef STB_TEXTEDIT_IS_SPACE +static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) +{ + return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; +} + +#ifndef STB_TEXTEDIT_MOVEWORDLEFT +static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) +{ + --c; // always move at least one character + while( c >= 0 && !is_word_boundary( str, c ) ) + --c; + + if( c < 0 ) + c = 0; + + return c; +} +#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous +#endif + +#ifndef STB_TEXTEDIT_MOVEWORDRIGHT +static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) +{ + const int len = STB_TEXTEDIT_STRINGLEN(str); + ++c; // always move at least one character + while( c < len && !is_word_boundary( str, c ) ) + ++c; + + if( c > len ) + c = len; + + return c; +} +#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next +#endif + +#endif + +// update selection and cursor to match each other +static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) +{ + if (!STB_TEXT_HAS_SELECTION(state)) + state->select_start = state->select_end = state->cursor; + else + state->cursor = state->select_end; +} + +// API cut: delete selection +static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + if (STB_TEXT_HAS_SELECTION(state)) { + stb_textedit_delete_selection(str,state); // implicity clamps + state->has_preferred_x = 0; + return 1; + } + return 0; +} + +// API paste: replace existing selection with passed-in text +static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) +{ + STB_TEXTEDIT_CHARTYPE *text = (STB_TEXTEDIT_CHARTYPE *) ctext; + // if there's a selection, the paste should delete it + stb_textedit_clamp(str, state); + stb_textedit_delete_selection(str,state); + // try to insert the characters + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { + stb_text_makeundo_insert(state, state->cursor, len); + state->cursor += len; + state->has_preferred_x = 0; + return 1; + } + // remove the undo since we didn't actually insert the characters + if (state->undostate.undo_point) + --state->undostate.undo_point; + return 0; +} + +// API key: process a keyboard input +static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int key) +{ +retry: + switch (key) { + default: { + int c = STB_TEXTEDIT_KEYTOTEXT(key); + if (c > 0) { + STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; + + // can't add newline in single-line mode + if (c == '\n' && state->single_line) + break; + + if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { + stb_text_makeundo_replace(str, state, state->cursor, 1, 1); + STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + ++state->cursor; + state->has_preferred_x = 0; + } + } else { + stb_textedit_delete_selection(str,state); // implicity clamps + if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { + stb_text_makeundo_insert(state, state->cursor, 1); + ++state->cursor; + state->has_preferred_x = 0; + } + } + } + break; + } + +#ifdef STB_TEXTEDIT_K_INSERT + case STB_TEXTEDIT_K_INSERT: + state->insert_mode = !state->insert_mode; + break; +#endif + + case STB_TEXTEDIT_K_UNDO: + stb_text_undo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_REDO: + stb_text_redo(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT: + // if currently there's a selection, move cursor to start of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else + if (state->cursor > 0) + --state->cursor; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_RIGHT: + // if currently there's a selection, move cursor to end of selection + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else + ++state->cursor; + stb_textedit_clamp(str, state); + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + // move selection left + if (state->select_end > 0) + --state->select_end; + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_MOVEWORDLEFT + case STB_TEXTEDIT_K_WORDLEFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + else { + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + +#ifdef STB_TEXTEDIT_MOVEWORDRIGHT + case STB_TEXTEDIT_K_WORDRIGHT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str, state); + else { + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); + stb_textedit_clamp( str, state ); + } + break; + + case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: + if( !STB_TEXT_HAS_SELECTION( state ) ) + stb_textedit_prep_selection_at_cursor(state); + + state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); + state->select_end = state->cursor; + + stb_textedit_clamp( str, state ); + break; +#endif + + case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + // move selection right + ++state->select_end; + stb_textedit_clamp(str, state); + state->cursor = state->select_end; + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_DOWN: + case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down in single-line behave like left&right + key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_last(str,state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // now find character position down a row + if (find.length) { + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + int start = find.first_char + find.length; + state->cursor = start; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_UP: + case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { + StbFindState find; + StbTexteditRow row; + int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; + + if (state->single_line) { + // on windows, up&down become left&right + key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); + goto retry; + } + + if (sel) + stb_textedit_prep_selection_at_cursor(state); + else if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_move_to_first(state); + + // compute current position of cursor point + stb_textedit_clamp(str, state); + stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); + + // can only go up if there's a previous row + if (find.prev_first != find.first_char) { + // now find character position up a row + float goal_x = state->has_preferred_x ? state->preferred_x : find.x; + float x; + state->cursor = find.prev_first; + STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); + x = row.x0; + for (i=0; i < row.num_chars; ++i) { + float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); + #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE + if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) + break; + #endif + x += dx; + if (x > goal_x) + break; + ++state->cursor; + } + stb_textedit_clamp(str, state); + + state->has_preferred_x = 1; + state->preferred_x = goal_x; + + if (sel) + state->select_end = state->cursor; + } + break; + } + + case STB_TEXTEDIT_K_DELETE: + case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + int n = STB_TEXTEDIT_STRINGLEN(str); + if (state->cursor < n) + stb_textedit_delete(str, state, state->cursor, 1); + } + state->has_preferred_x = 0; + break; + + case STB_TEXTEDIT_K_BACKSPACE: + case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: + if (STB_TEXT_HAS_SELECTION(state)) + stb_textedit_delete_selection(str, state); + else { + stb_textedit_clamp(str, state); + if (state->cursor > 0) { + stb_textedit_delete(str, state, state->cursor-1, 1); + --state->cursor; + } + } + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2: +#endif + case STB_TEXTEDIT_K_TEXTSTART: + state->cursor = state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2: +#endif + case STB_TEXTEDIT_K_TEXTEND: + state->cursor = STB_TEXTEDIT_STRINGLEN(str); + state->select_start = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTSTART2 + case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = 0; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_TEXTEND2 + case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: + stb_textedit_prep_selection_at_cursor(state); + state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); + state->has_preferred_x = 0; + break; + + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2: +#endif + case STB_TEXTEDIT_K_LINESTART: + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + if (state->single_line) + state->cursor = 0; + else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2: +#endif + case STB_TEXTEDIT_K_LINEEND: { + int n = STB_TEXTEDIT_STRINGLEN(str); + stb_textedit_clamp(str, state); + stb_textedit_move_to_first(state); + if (state->single_line) + state->cursor = n; + else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) + ++state->cursor; + state->has_preferred_x = 0; + break; + } + +#ifdef STB_TEXTEDIT_K_LINESTART2 + case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + if (state->single_line) + state->cursor = 0; + else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) + --state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; + break; + +#ifdef STB_TEXTEDIT_K_LINEEND2 + case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: +#endif + case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { + int n = STB_TEXTEDIT_STRINGLEN(str); + stb_textedit_clamp(str, state); + stb_textedit_prep_selection_at_cursor(state); + if (state->single_line) + state->cursor = n; + else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) + ++state->cursor; + state->select_end = state->cursor; + state->has_preferred_x = 0; + break; + } + +// @TODO: +// STB_TEXTEDIT_K_PGUP - move cursor up a page +// STB_TEXTEDIT_K_PGDOWN - move cursor down a page + } +} + +///////////////////////////////////////////////////////////////////////////// +// +// Undo processing +// +// @OPTIMIZE: the undo/redo buffer should be circular + +static void stb_textedit_flush_redo(StbUndoState *state) +{ + state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; +} + +// discard the oldest entry in the undo list +static void stb_textedit_discard_undo(StbUndoState *state) +{ + if (state->undo_point > 0) { + // if the 0th undo state has characters, clean those up + if (state->undo_rec[0].char_storage >= 0) { + int n = state->undo_rec[0].insert_length, i; + // delete n characters from all other records + state->undo_char_point = state->undo_char_point - (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) ((size_t)state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=0; i < state->undo_point; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage - (short) n; // vsnet05 // @OPTIMIZE: get rid of char_storage and infer it + } + --state->undo_point; + STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) ((size_t)state->undo_point*sizeof(state->undo_rec[0]))); + } +} + +// discard the oldest entry in the redo list--it's bad if this +// ever happens, but because undo & redo have to store the actual +// characters in different cases, the redo character buffer can +// fill up even though the undo buffer didn't +static void stb_textedit_discard_redo(StbUndoState *state) +{ + int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; + + if (state->redo_point <= k) { + // if the k'th undo state has characters, clean those up + if (state->undo_rec[k].char_storage >= 0) { + int n = state->undo_rec[k].insert_length, i; + // delete n characters from all other records + state->redo_char_point = state->redo_char_point + (short) n; // vsnet05 + STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((size_t)(STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); + for (i=state->redo_point; i < k; ++i) + if (state->undo_rec[i].char_storage >= 0) + state->undo_rec[i].char_storage = state->undo_rec[i].char_storage + (short) n; // vsnet05 + } + STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point, state->undo_rec + state->redo_point-1, (size_t) ((size_t)(STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); + ++state->redo_point; + } +} + +static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) +{ + // any time we create a new undo record, we discard redo + stb_textedit_flush_redo(state); + + // if we have no free records, we have to make room, by sliding the + // existing records down + if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + stb_textedit_discard_undo(state); + + // if the characters to store won't possibly fit in the buffer, we can't undo + if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { + state->undo_point = 0; + state->undo_char_point = 0; + return NULL; + } + + // if we don't have enough free characters in the buffer, we have to make room + while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) + stb_textedit_discard_undo(state); + + return &state->undo_rec[state->undo_point++]; +} + +static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) +{ + StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); + if (r == NULL) + return NULL; + + r->where = pos; + r->insert_length = (short) insert_len; + r->delete_length = (short) delete_len; + + if (insert_len == 0) { + r->char_storage = -1; + return NULL; + } else { + r->char_storage = state->undo_char_point; + state->undo_char_point = state->undo_char_point + (short) insert_len; + return &state->undo_char[r->char_storage]; + } +} + +static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord u, *r; + if (s->undo_point == 0) + return; + + // we need to do two things: apply the undo record, and create a redo record + u = s->undo_rec[s->undo_point-1]; + r = &s->undo_rec[s->redo_point-1]; + r->char_storage = -1; + + r->insert_length = u.delete_length; + r->delete_length = u.insert_length; + r->where = u.where; + + if (u.delete_length) { + // if the undo record says to delete characters, then the redo record will + // need to re-insert the characters that get deleted, so we need to store + // them. + + // there are three cases: + // there's enough room to store the characters + // characters stored for *redoing* don't leave room for redo + // characters stored for *undoing* don't leave room for redo + // if the last is true, we have to bail + + if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { + // the undo records take up too much character space; there's no space to store the redo characters + r->insert_length = 0; + } else { + int i; + + // there's definitely room to store the characters eventually + while (s->undo_char_point + u.delete_length > s->redo_char_point) { + // there's currently not enough room, so discard a redo record + stb_textedit_discard_redo(s); + // should never happen: + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + } + r = &s->undo_rec[s->redo_point-1]; + + r->char_storage = s->redo_char_point - u.delete_length; + s->redo_char_point = s->redo_char_point - (short) u.delete_length; + + // now save the characters + for (i=0; i < u.delete_length; ++i) + s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); + } + + // now we can carry out the deletion + STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); + } + + // check type of recorded action: + if (u.insert_length) { + // easy case: was a deletion, so we need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); + s->undo_char_point -= u.insert_length; + } + + state->cursor = u.where + u.insert_length; + + s->undo_point--; + s->redo_point--; +} + +static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) +{ + StbUndoState *s = &state->undostate; + StbUndoRecord *u, r; + if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) + return; + + // we need to do two things: apply the redo record, and create an undo record + u = &s->undo_rec[s->undo_point]; + r = s->undo_rec[s->redo_point]; + + // we KNOW there must be room for the undo record, because the redo record + // was derived from an undo record + + u->delete_length = r.insert_length; + u->insert_length = r.delete_length; + u->where = r.where; + u->char_storage = -1; + + if (r.delete_length) { + // the redo record requires us to delete characters, so the undo record + // needs to store the characters + + if (s->undo_char_point + u->insert_length > s->redo_char_point) { + u->insert_length = 0; + u->delete_length = 0; + } else { + int i; + u->char_storage = s->undo_char_point; + s->undo_char_point = s->undo_char_point + u->insert_length; + + // now save the characters + for (i=0; i < u->insert_length; ++i) + s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); + } + + STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); + } + + if (r.insert_length) { + // easy case: need to insert n characters + STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); + s->redo_char_point += r.insert_length; + } + + state->cursor = r.where + r.insert_length; + + s->undo_point++; + s->redo_point++; +} + +static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) +{ + stb_text_createundo(&state->undostate, where, 0, length); +} + +static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); + if (p) { + for (i=0; i < length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) +{ + int i; + STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); + if (p) { + for (i=0; i < old_length; ++i) + p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); + } +} + +// reset the state to default +static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) +{ + state->undostate.undo_point = 0; + state->undostate.undo_char_point = 0; + state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; + state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; + state->select_end = state->select_start = 0; + state->cursor = 0; + state->has_preferred_x = 0; + state->preferred_x = 0; + state->cursor_at_end_of_line = 0; + state->initialized = 1; + state->single_line = (unsigned char) is_single_line; + state->insert_mode = 0; +} + +// API initialize +static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) +{ + stb_textedit_clear_state(state, is_single_line); +} +#endif//STB_TEXTEDIT_IMPLEMENTATION diff --git a/apps/exampleViewer/common/imgui/stb_truetype.h b/apps/exampleViewer/common/imgui/stb_truetype.h new file mode 100644 index 0000000000..e6dae97515 --- /dev/null +++ b/apps/exampleViewer/common/imgui/stb_truetype.h @@ -0,0 +1,3263 @@ +// stb_truetype.h - v1.10 - public domain +// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// +// Misc other: +// Ryan Gordon +// Simon Glass +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket (with fix) +// Cass Everitt +// stoiko (Haemimont Games) +// Brian Hook +// Walter van Niftrik +// David Gow +// David Given +// Ivan-Assen Ivanov +// Anthony Pesch +// Johan Duparc +// Hou Qiming +// Fabian "ryg" Giesen +// Martins Mozeiko +// Cap Petschulat +// Omar Cornut +// github:aloucks +// Peter LaValle +// Sergey Popov +// Giumo X. Clanjor +// Higor Euripedes +// Thomas Fields +// Derek Vinyard +// +// VERSION HISTORY +// +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// This software is dual-licensed to the public domain and under the following +// license: you are granted a perpetual, irrevocable license to copy, modify, +// publish, and distribute this file as you see fit. +// +// USAGE +// +// Include this file in whatever places neeed to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since they different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// ADVANCED USAGE +// +// Quality: +// +// - Use the functions with Subpixel at the end to allow your characters +// to have subpixel positioning. Since the font is anti-aliased, not +// hinted, this is very import for quality. (This is not possible with +// baked fonts.) +// +// - Kerning is now supported, and if you're supporting subpixel rendering +// then kerning is worth using to give your text a polished look. +// +// Performance: +// +// - Convert Unicode codepoints to glyph indexes and operate on the glyphs; +// if you don't do this, stb_truetype is forced to do the conversion on +// every call. +// +// - There are a lot of memory allocations. We should modify it to take +// a temp buffer and allocate from the temp buffer (without freeing), +// should help performance a lot. +// +// NOTES +// +// The system uses the raw data found in the .ttf file without changing it +// and without building auxiliary data structures. This is a bit inefficient +// on little-endian systems (the data is big-endian), but assuming you're +// caching the bitmaps or glyph shapes this shouldn't be a big deal. +// +// It appears to be very hard to programmatically determine what font a +// given file is in a general way. I provide an API for this, but I don't +// recommend it. +// +// +// SOURCE STATISTICS (based on v0.6c, 2050 LOC) +// +// Documentation & header file 520 LOC \___ 660 LOC documentation +// Sample code 140 LOC / +// Truetype parsing 620 LOC ---- 620 LOC TrueType +// Software rasterization 240 LOC \ . +// Curve tesselation 120 LOC \__ 550 LOC Bitmap creation +// Bitmap management 100 LOC / +// Baked bitmap interface 70 LOC / +// Font name matching & access 150 LOC ---- 150 +// C runtime library abstraction 60 LOC ---- 60 +// +// +// PERFORMANCE MEASUREMENTS FOR 1.06: +// +// 32-bit 64-bit +// Previous release: 8.83 s 7.68 s +// Pool allocations: 7.72 s 6.34 s +// Inline sort : 6.54 s 5.65 s +// New rasterizer : 5.63 s 5.00 s + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// SAMPLE PROGRAMS +//// +// +// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless +// +#if 0 +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +unsigned char ttf_buffer[1<<20]; +unsigned char temp_bitmap[512*512]; + +stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphs +GLuint ftex; + +void my_stbtt_initfont(void) +{ + fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); + stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! + // can free ttf_buffer at this point + glGenTextures(1, &ftex); + glBindTexture(GL_TEXTURE_2D, ftex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); + // can free temp_bitmap at this point + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); +} + +void my_stbtt_print(float x, float y, char *text) +{ + // assume orthographic projection with units = screen pixels, origin at top left + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, ftex); + glBegin(GL_QUADS); + while (*text) { + if (*text >= 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// if return is positive, the first unused row of the bitmap +// if return is negative, returns the negative of the number of characters that fit +// if return is 0, no characters fit and no rows were used +// This uses a very crappy packing. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// Call GetBakedQuad with char_index = 'character - first_char', and it +// creates the quad you need to draw and advances the current position. +// +// The coordinate system used assumes y increases downwards. +// +// Characters will extend both above and below the current position; +// see discussion of "BASELINE" above. +// +// It's inefficient; you might want to c&p it and optimize it. + + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// Initializes a packing context stored in the passed-in stbtt_pack_context. +// Future calls using this context will pack characters into the bitmap passed +// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// the distance from one row to the next (or 0 to mean they are packed tightly +// together). "padding" is the amount of padding to leave between each +// character (normally you want '1' for bitmaps you'll use as textures with +// bilinear filtering). +// +// Returns 0 on failure, 1 on success. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// Creates character bitmaps from the font_index'th font found in fontdata (use +// font_index=0 if you don't know what that is). It creates num_chars_in_range +// bitmaps for characters with unicode values starting at first_unicode_char_in_range +// and increasing. Data for how to render them is stored in chardata_for_range; +// pass these to stbtt_GetPackedQuad to get back renderable quads. +// +// font_size is the full height of the character from ascender to descender, +// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed +// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// Creates character bitmaps from multiple ranges of characters stored in +// ranges. This will usually create a better-packed bitmap than multiple +// calls to stbtt_PackFontRange. Note that you can call this multiple +// times within a single PackBegin/PackEnd. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// Oversampling a font increases the quality by allowing higher-quality subpixel +// positioning, and is especially valuable at smaller text sizes. +// +// This function sets the amount of oversampling for all following calls to +// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given +// pack context. The default (no oversampling) is achieved by h_oversample=1 +// and v_oversample=1. The total number of pixels required is +// h_oversample*v_oversample larger than the default; for example, 2x2 +// oversampling requires 4x the storage of 1x1. For best results, render +// oversampled textures with bilinear filtering. Look at the readme in +// stb/tests/oversample for information about oversampled fonts +// +// To use with PackFontRangesGather etc., you must set it before calls +// call to PackFontRangesGatherRects. + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// Calling these functions in sequence is roughly equivalent to calling +// stbtt_PackFontRanges(). If you more control over the packing of multiple +// fonts, or if you want to pack custom data into a font texture, take a look +// at the source to of stbtt_PackFontRanges() and create a custom version +// using these functions, e.g. call GatherRects multiple times, +// building up a single array of rects, then call PackRects once, +// then call RenderIntoRects repeatedly. This may result in a +// better packing than calling PackFontRanges multiple times +// (or it may not). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// Each .ttf/.ttc file may have more than one font. Each font has a sequential +// index number starting from 0. Call this function to get the font offset for +// a given index; it returns -1 if the index is out of range. A regular .ttf +// file will only define one font and it always be at offset 0, so it will +// return '0' for index 0, and -1 for all other indices. You can just skip +// this step if you know it's that kind of font. + + +// The following structure is defined publically so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// Given an offset into the file that defines a font, this function builds +// the necessary cached info for the rest of the system. You must allocate +// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't +// need to do anything special to free it, because the contents are pure +// value data with no additional data structures. Returns 0 on failure. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// If you're going to perform multiple operations on the same character +// and you want a speed-up, call this function with the character you're +// going to process, then use glyph-based functions instead of the +// codepoint-based functions. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose "height" is 'pixels' tall. +// Height is measured as the distance from the highest ascender to the lowest +// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics +// and computing: +// scale = pixels / (ascent - descent) +// so if you prefer to measure height by the ascent only, use a similar calculation. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// computes a scale factor to produce a font whose EM size is mapped to +// 'pixels' tall. This is probably what traditional APIs compute, but +// I'm not positive. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// ascent is the coordinate above the baseline the font extends; descent +// is the coordinate below the baseline the font extends (i.e. it is typically negative) +// lineGap is the spacing between one row's descent and the next row's ascent... +// so you should advance the vertical position by "*ascent - *descent + *lineGap" +// these are expressed in unscaled coordinates, so you must multiply by +// the scale factor for a given size + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// leftSideBearing is the offset from the current horizontal position to the left edge of the character +// advanceWidth is the offset from the current horizontal position to the next horizontal position +// these are expressed in unscaled coordinates + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// returns # of vertices and fills *vertices with the pointer to them +// these are expressed in "unscaled" coordinates +// +// The shape is a series of countours. Each one starts with +// a STBTT_moveto, then consists of a series of mixed +// STBTT_lineto and STBTT_curveto segments. A lineto +// draws a line from previous endpoint to its x,y; a curveto +// draws a quadratic bezier from previous endpoint to +// its x,y, using cx,cy as the bezier control point. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// allocates a large-enough single-channel 8bpp bitmap and renders the +// specified character/glyph at the specified scale into it, with +// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). +// *width & *height are filled out with the width & height of the bitmap, +// which is stored left-to-right, top-to-bottom. +// +// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap +// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap +// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the +// width and height and positioning info for it first. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// get the bbox of the bitmap centered around the glyph origin; so the +// bitmap width is ix1-ix0, height is iy1-iy0, and location to place +// the bitmap top left is (leftSideBearing*scale,iy0). +// (Note that the bitmap uses y-increases-down, but the shape uses +// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Finding the right font... +// +// You should really just solve this offline, keep your own tables +// of what font is what, and don't try to get it out of the .ttf file. +// That's because getting it out of the .ttf file is really hard, because +// the names in the file can appear in many possible encodings, in many +// possible languages, and e.g. if you need a case-insensitive comparison, +// the details of that depend on the encoding & language in a complex way +// (actually underspecified in truetype, but also gigantic). +// +// But you can use the provided functions in two possible ways: +// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on +// unicode-encoded names to try to find the font you want; +// you can run this before calling stbtt_InitFont() +// +// stbtt_GetFontNameString() lets you get any of the various strings +// from the file yourself and do your own comparisons on them. +// You have to have called stbtt_InitFont() first. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// returns the offset (not index) of the font that matches, or -1 if none +// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". +// if you use any other flag, use a font name like "Arial"; this checks +// the 'macStyle' header field; i don't know if fonts set this consistently +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// returns 1/0 whether the first string interpreted as utf8 is identical to +// the second string interpreted as big-endian utf16... useful for strings from next func + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// returns the string (which may be big-endian double byte, e.g. for unicode) +// and puts the length in bytes in *length. +// +// some of the values for the IDs are below; for more see the truetype spec: +// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html +// http://www.microsoft.com/typography/otspec/name.htm + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) + + #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) + #define ttSHORT(p) (* (stbtt_int16 *) (p)) + #define ttULONG(p) (* (stbtt_uint32 *) (p)) + #define ttLONG(p) (* (stbtt_int32 *) (p)) + +#else + + static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } + static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#endif + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(const stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +{ + stbtt_uint8 *data = (stbtt_uint8 *) data2; + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + return 0; + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + if (unicode_codepoint < start) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours == -1) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else if (numberOfContours < 0) { + // @TODO other compound variations? + STBTT_assert(0); + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = sy1 - sy0; + STBTT_assert(x >= 0 && x < len); + scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; + scanline_fill[x] += e->direction * height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = (x1+1 - x0) * dy + y_top; + + sign = e->direction; + // area of the rectangle covered from y0..y_crossing + area = sign * (y_crossing-sy0); + // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) + scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); + + step = sign * dy; + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; + area += step; + } + y_crossing += dy * (x2 - (x1+1)); + + STBTT_assert(STBTT_fabs(area) <= 1.01f); + + scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); + + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clear pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + float y1,y2; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + y1 = (x - x0) / dx + y_top; + y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + (void)vsubsample; + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + STBTT_assert(z->ey >= scan_y_top); + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshhold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count, *winding_lengths; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + ++k; + } + } + + return k; +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// diff --git a/apps/exampleViewer/common/util/AsyncRenderEngine.cpp b/apps/exampleViewer/common/util/AsyncRenderEngine.cpp new file mode 100644 index 0000000000..c20bc49ccb --- /dev/null +++ b/apps/exampleViewer/common/util/AsyncRenderEngine.cpp @@ -0,0 +1,185 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "AsyncRenderEngine.h" + +namespace ospray { + + AsyncRenderEngine::~AsyncRenderEngine() + { + stop(); + } + + void AsyncRenderEngine::setRenderer(cpp::Renderer renderer, + cpp::Renderer rendererDW, + cpp::FrameBuffer frameBufferDW) + { + this->renderer = renderer; + this->rendererDW = rendererDW; + this->frameBufferDW = frameBufferDW; + } + + void AsyncRenderEngine::setFbSize(const ospcommon::vec2i &size) + { + fbSize = size; + } + + void AsyncRenderEngine::scheduleObjectCommit(const cpp::ManagedObject &obj) + { + std::lock_guard lock{objMutex}; + objsToCommit.push_back(obj.object()); + } + + void AsyncRenderEngine::start(int numThreads) + { + if (state == ExecState::RUNNING) + return; + + numOsprayThreads = numThreads; + + validate(); + + if (state == ExecState::INVALID) + throw std::runtime_error("Can't start the engine in an invalid state!"); + + state = ExecState::RUNNING; + backgroundThread = std::thread([&](){ run(); }); + } + + void AsyncRenderEngine::stop() + { + if (state != ExecState::RUNNING) + return; + + state = ExecState::STOPPED; + if (backgroundThread.joinable()) + backgroundThread.join(); + } + + ExecState AsyncRenderEngine::runningState() const + { + return state; + } + + bool AsyncRenderEngine::hasNewFrame() const + { + return newPixels; + } + + double AsyncRenderEngine::lastFrameFps() const + { + return fps.perSecondSmoothed(); + } + + const std::vector &AsyncRenderEngine::mapFramebuffer() + { + fbMutex.lock(); + newPixels = false; + return pixelBuffer[mappedPB]; + } + + void AsyncRenderEngine::unmapFramebuffer() + { + fbMutex.unlock(); + } + + void AsyncRenderEngine::validate() + { + if (state == ExecState::INVALID) + { + renderer.update(); + rendererDW.update(); + state = renderer.ref().handle() ? ExecState::STOPPED : ExecState::INVALID; + } + } + + bool AsyncRenderEngine::checkForObjCommits() + { + bool commitOccurred = false; + + if (!objsToCommit.empty()) { + std::lock_guard lock{objMutex}; + + for (auto obj : objsToCommit) + ospCommit(obj); + + objsToCommit.clear(); + commitOccurred = true; + } + + return commitOccurred; + } + + bool AsyncRenderEngine::checkForFbResize() + { + bool changed = fbSize.update(); + + if (changed) { + auto &size = fbSize.ref(); + frameBuffer = cpp::FrameBuffer(size, OSP_FB_SRGBA, + OSP_FB_COLOR | OSP_FB_DEPTH | + OSP_FB_ACCUM | OSP_FB_VARIANCE); + + nPixels = size.x * size.y; + pixelBuffer[0].resize(nPixels); + pixelBuffer[1].resize(nPixels); + } + + return changed; + } + + void AsyncRenderEngine::run() + { + auto device = ospGetCurrentDevice(); + if (numOsprayThreads > 0) + ospDeviceSet1i(device, "numThreads", numOsprayThreads); + ospDeviceCommit(device); + + while (state == ExecState::RUNNING) { + bool resetAccum = false; + resetAccum |= renderer.update(); + resetAccum |= checkForFbResize(); + resetAccum |= checkForObjCommits(); + + if (resetAccum) { + frameBuffer.clear(OSP_FB_ACCUM); + if (frameBufferDW) + frameBufferDW.clear(OSP_FB_ACCUM); + } + + fps.start(); + renderer.ref().renderFrame(frameBuffer, OSP_FB_COLOR | OSP_FB_ACCUM); + if (rendererDW.ref()) + rendererDW.ref().renderFrame(frameBufferDW, OSP_FB_COLOR | OSP_FB_ACCUM); + fps.stop(); + + auto *srcPB = (uint32_t*)frameBuffer.map(OSP_FB_COLOR); + auto *dstPB = (uint32_t*)pixelBuffer[currentPB].data(); + + memcpy(dstPB, srcPB, nPixels*sizeof(uint32_t)); + + frameBuffer.unmap(srcPB); + + if (fbMutex.try_lock()) + { + std::swap(currentPB, mappedPB); + newPixels = true; + fbMutex.unlock(); + } + } + } + +}// namespace ospray diff --git a/apps/exampleViewer/common/util/AsyncRenderEngine.h b/apps/exampleViewer/common/util/AsyncRenderEngine.h new file mode 100644 index 0000000000..36252c7f2d --- /dev/null +++ b/apps/exampleViewer/common/util/AsyncRenderEngine.h @@ -0,0 +1,110 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +// std +#include +#include +#include + +// ospcommon +#include "ospcommon/box.h" +#include "ospcommon/utility/CodeTimer.h" +#include "ospcommon/utility/TransactionalValue.h" + +// ospray::cpp +#include "ospray/ospray_cpp/Renderer.h" + +// ospImGui util +#include "ImguiUtilExport.h" + +namespace ospray { + + enum class ExecState {STOPPED, RUNNING, INVALID}; + + class OSPRAY_IMGUI_UTIL_INTERFACE AsyncRenderEngine + { + public: + + AsyncRenderEngine() = default; + ~AsyncRenderEngine(); + + // Properties // + + void setRenderer(cpp::Renderer renderer, + cpp::Renderer rendererDW, + cpp::FrameBuffer frameBufferDW); + + void setFbSize(const ospcommon::vec2i &size); + + // Method to say that an objects needs to be comitted before next frame // + + void scheduleObjectCommit(const cpp::ManagedObject &obj); + + // Engine conrols // + + virtual void start(int numThreads = -1); + virtual void stop(); + + ExecState runningState() const; + + // Output queries // + + bool hasNewFrame() const; + double lastFrameFps() const; + + const std::vector &mapFramebuffer(); + void unmapFramebuffer(); + + protected: + + // Helper functions // + + virtual void validate(); + bool checkForObjCommits(); + bool checkForFbResize(); + virtual void run(); + + // Data // + + int numOsprayThreads {-1}; + + std::thread backgroundThread; + std::atomic state {ExecState::INVALID}; + + cpp::FrameBuffer frameBuffer; + cpp::FrameBuffer frameBufferDW; + + ospcommon::utility::TransactionalValue renderer; + ospcommon::utility::TransactionalValue rendererDW; + ospcommon::utility::TransactionalValue fbSize; + + int nPixels {0}; + + int currentPB {0}; + int mappedPB {1}; + std::mutex fbMutex; + std::vector pixelBuffer[2]; + + std::mutex objMutex; + std::vector objsToCommit; + + std::atomic newPixels {false}; + + ospcommon::utility::CodeTimer fps; + }; +}// namespace ospray diff --git a/apps/exampleViewer/common/util/AsyncRenderEngineSg.cpp b/apps/exampleViewer/common/util/AsyncRenderEngineSg.cpp new file mode 100644 index 0000000000..710ccdeac4 --- /dev/null +++ b/apps/exampleViewer/common/util/AsyncRenderEngineSg.cpp @@ -0,0 +1,97 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "AsyncRenderEngineSg.h" + +#include "sg/common/FrameBuffer.h" + +namespace ospray { + namespace sg { + + AsyncRenderEngineSg::AsyncRenderEngineSg(const std::shared_ptr &sgRenderer, + const std::shared_ptr &sgRendererDW) + : scenegraph(sgRenderer), + scenegraphDW(sgRendererDW) + { + } + + void AsyncRenderEngineSg::run() + { + while (state == ExecState::RUNNING) { + static sg::TimeStamp lastFTime; + + auto &sgFB = scenegraph->child("frameBuffer"); + + static bool once = false; //TODO: initial commit as timestamp can not + // be set to 0 + static int counter = 0; + if (sgFB.childrenLastModified() > lastFTime || !once) { + auto &size = sgFB["size"]; + nPixels = size.valueAs().x * size.valueAs().y; + pixelBuffer[0].resize(nPixels); + pixelBuffer[1].resize(nPixels); + lastFTime = sg::TimeStamp(); + } + + if (scenegraph->childrenLastModified() > lastRTime || !once) { + double time = ospcommon::getSysTime(); + scenegraph->traverse("verify"); + double verifyTime = ospcommon::getSysTime() - time; + time = ospcommon::getSysTime(); + scenegraph->traverse("commit"); + double commitTime = ospcommon::getSysTime() - time; + + if (scenegraphDW) { + scenegraphDW->traverse("verify"); + scenegraphDW->traverse("commit"); + } + + lastRTime = sg::TimeStamp(); + } + + fps.start(); + scenegraph->traverse("render"); + if (scenegraphDW) + scenegraphDW->traverse("render"); + once = true; + + fps.stop(); + auto sgFBptr = + std::static_pointer_cast(sgFB.shared_from_this()); + + auto *srcPB = (uint32_t*)sgFBptr->map(); + auto *dstPB = (uint32_t*)pixelBuffer[currentPB].data(); + + memcpy(dstPB, srcPB, nPixels*sizeof(uint32_t)); + + sgFBptr->unmap(srcPB); + + if (fbMutex.try_lock()) { + std::swap(currentPB, mappedPB); + newPixels = true; + fbMutex.unlock(); + } + } + } + + void AsyncRenderEngineSg::validate() + { + if (state == ExecState::INVALID) + state = ExecState::STOPPED; + } + + } // ::ospray::sg +} // ::ospray diff --git a/apps/qtViewer/FPSCounter.h b/apps/exampleViewer/common/util/AsyncRenderEngineSg.h similarity index 68% rename from apps/qtViewer/FPSCounter.h rename to apps/exampleViewer/common/util/AsyncRenderEngineSg.h index 2cbc89bd45..53ce71b102 100644 --- a/apps/qtViewer/FPSCounter.h +++ b/apps/exampleViewer/common/util/AsyncRenderEngineSg.h @@ -16,26 +16,30 @@ #pragma once -#include "sg/common/Common.h" +#include "sg/common/Node.h" +#include "AsyncRenderEngine.h" namespace ospray { - namespace viewer { + namespace sg { - /*! small helper class to compute (smoothed) frames-per-second values */ - struct FPSCounter + class OSPRAY_IMGUI_UTIL_INTERFACE AsyncRenderEngineSg + : public AsyncRenderEngine { - FPSCounter(); - void startRender(); - void doneRender(); - double getSmoothFPS() const; - double getFPS() const; + public: + + AsyncRenderEngineSg(const std::shared_ptr &sgRenderer, + const std::shared_ptr &sgRendererDW); + ~AsyncRenderEngineSg() = default; private: - double fps; - double smooth_nom; - double smooth_den; - double frameStartTime; + + virtual void run() override; + virtual void validate() override; + + const std::shared_ptr scenegraph; + const std::shared_ptr scenegraphDW; + sg::TimeStamp lastRTime; }; - } // ::ospray::viewer + } // ::ospray::sg } // ::ospray diff --git a/apps/volumeViewer/glUtil/CMakeLists.txt b/apps/exampleViewer/common/util/CMakeLists.txt similarity index 76% rename from apps/volumeViewer/glUtil/CMakeLists.txt rename to apps/exampleViewer/common/util/CMakeLists.txt index 18614fb8d8..2a00433864 100644 --- a/apps/volumeViewer/glUtil/CMakeLists.txt +++ b/apps/exampleViewer/common/util/CMakeLists.txt @@ -1,5 +1,5 @@ ## ======================================================================== ## -## Copyright 2009-2017 Intel Corporation ## +## Copyright 2009-2016 Intel Corporation ## ## ## ## Licensed under the Apache License, Version 2.0 (the "License"); ## ## you may not use this file except in compliance with the License. ## @@ -14,16 +14,18 @@ ## limitations under the License. ## ## ======================================================================== ## -FIND_PACKAGE(OpenGL REQUIRED) - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray) -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - -LIST(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/modules/opengl) +ospray_create_library(ospray_imgui_util + ImguiUtilExport.h + AsyncRenderEngine.cpp +LINK + ospray +) -OSPRAY_CREATE_LIBRARY(ospray_opengl_util - util.cpp - LINK +ospray_create_library(ospray_imgui_util_sg + ImguiUtilExport.h + AsyncRenderEngine.cpp + AsyncRenderEngineSg.cpp +LINK ospray - ${OPENGL_LIBRARIES} - ) + ospray_sg +) diff --git a/modules/mpi/OSPMPIConfig.h.in b/apps/exampleViewer/common/util/ImguiUtilExport.h similarity index 76% rename from modules/mpi/OSPMPIConfig.h.in rename to apps/exampleViewer/common/util/ImguiUtilExport.h index 22ebd7b8da..613d63bf40 100644 --- a/modules/mpi/OSPMPIConfig.h.in +++ b/apps/exampleViewer/common/util/ImguiUtilExport.h @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2017 Intel Corporation // +// Copyright 2009-2016 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -16,5 +16,13 @@ #pragma once -#cmakedefine OSPRAY_PIN_ASYNC +#ifdef _WIN32 +# if defined(ospray_imgui_util_EXPORTS) || defined(ospray_imgui_util_sg_EXPORTS) +# define OSPRAY_IMGUI_UTIL_INTERFACE __declspec(dllexport) +# else +# define OSPRAY_IMGUI_UTIL_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPRAY_IMGUI_UTIL_INTERFACE +#endif diff --git a/apps/exampleViewer/ospExampleViewer.cpp b/apps/exampleViewer/ospExampleViewer.cpp new file mode 100644 index 0000000000..78c411436e --- /dev/null +++ b/apps/exampleViewer/ospExampleViewer.cpp @@ -0,0 +1,179 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include +#include +#include +#include "common/commandline/Utility.h" +#include "ospcommon/networking/Socket.h" + +#include "widgets/imguiViewer.h" + +namespace exampleViewer { + + using namespace commandline; + + ospcommon::vec3f translate; + ospcommon::vec3f scale; + bool lockFirstFrame = false; + bool fullscreen = false; + std::string displayWall = ""; + + namespace dw { + + struct ServiceInfo { + /* constructor that initializes everything to default values */ + ServiceInfo() + : totalPixelsInWall(-1,-1), + mpiPortName("") + {} + + /*! total pixels in the entire display wall, across all + indvididual displays, and including bezels (future versios + will allow to render to smaller resolutions, too - and have + the clients upscale this - but for now the client(s) have to + render at exactly this resolution */ + ospcommon::vec2i totalPixelsInWall; + + /*! the MPI port name that the service is listening on client + connections for (ie, the one to use with + client::establishConnection) */ + std::string mpiPortName; + + /*! whether this runs in stereo mode */ + int stereo; + + /*! read a service info from a given hostName:port. The service + has to already be running on that port + + Note this may throw a std::runtime_error if the connection + cannot be established + */ + void getFrom(const std::string &hostName, + const int portNo); + }; + /*! read a service info from a given hostName:port. The service + has to already be running on that port */ + void ServiceInfo::getFrom(const std::string &hostName, + const int portNo) + { + ospcommon::socket_t sock = ospcommon::connect(hostName.c_str(),portNo); + if (!sock) + throw std::runtime_error("could not create display wall connection!"); + + mpiPortName = read_string(sock); + totalPixelsInWall.x = read_int(sock); + totalPixelsInWall.y = read_int(sock); + stereo = read_int(sock); + close(sock); + } + } + + void parseExtraParametersFromComandLine(int ac, const char **&av) + { + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "--translate") { + translate.x = atof(av[++i]); + translate.y = atof(av[++i]); + translate.z = atof(av[++i]); + } else if (arg == "--display-wall" || arg == "-dw") { + displayWall = av[++i]; + } else if (arg == "--scale") { + scale.x = atof(av[++i]); + scale.y = atof(av[++i]); + scale.z = atof(av[++i]); + } else if (arg == "--lockFirstFrame") { + lockFirstFrame = true; + } else if (arg == "--fullscreen") { + fullscreen = true; + } + } + } + + extern "C" int main(int ac, const char **av) + { + int init_error = ospInit(&ac, av); + if (init_error != OSP_NO_ERROR) { + std::cerr << "FATAL ERROR DURING INITIALIZATION!" << std::endl; + return init_error; + } + + auto device = ospGetCurrentDevice(); + ospDeviceSetStatusFunc(device, + [](const char *msg) { std::cout << msg; }); + + ospDeviceSetErrorFunc(device, + [](OSPError e, const char *msg) { + std::cout << "OSPRAY ERROR [" << e << "]: " + << msg << std::endl; + std::exit(1); + }); + + ospray::imgui3D::init(&ac,av); + + auto ospObjs = parseWithDefaultParsersDW(ac, av); + + std::deque bbox; + std::deque model; + ospray::cpp::Renderer renderer; + ospray::cpp::Renderer rendererDW; + ospray::cpp::Camera camera; + ospray::cpp::FrameBuffer frameBufferDW; + + std::tie(bbox, model, renderer, rendererDW, camera) = ospObjs; + + parseExtraParametersFromComandLine(ac, av); + + if (displayWall != "") { + std::cout << "#############################################" << std::endl; + std::cout << "found --display-wall cmdline argument ...." << std::endl; + std::cout << "trying to connect to display wall service on " + << displayWall << ":2903" << std::endl; + + dw::ServiceInfo dwService; + dwService.getFrom(displayWall,2903); + std::cout << "found display wall service on MPI port " + << dwService.mpiPortName << std::endl; + std::cout << "#############################################" << std::endl; + frameBufferDW = ospray::cpp::FrameBuffer(dwService.totalPixelsInWall, + (OSPFrameBufferFormat)OSP_FB_NONE, + OSP_FB_COLOR|OSP_FB_ACCUM); + + ospLoadModule("displayWald"); + OSPPixelOp pixelOp = ospNewPixelOp("display_wald"); + ospSetString(pixelOp,"streamName",dwService.mpiPortName.c_str()); + ospCommit(pixelOp); + ospSetPixelOp(frameBufferDW.handle(),pixelOp); + rendererDW.set("frameBuffer", frameBufferDW.handle()); + rendererDW.commit(); + } else { + // no diplay wall - nix the display wall renderer + rendererDW = ospray::cpp::Renderer(); + } + + ospray::ImGuiViewer window(bbox, model, renderer, rendererDW, + frameBufferDW, camera); + window.setScale(scale); + window.setLockFirstAnimationFrame(lockFirstFrame); + window.setTranslation(translate); + window.create("ospImGui: OSPRay ImGui Viewer App", fullscreen); + + ospray::imgui3D::run(); + return 0; + } + +} diff --git a/apps/exampleViewer/ospExampleViewerSg.cpp b/apps/exampleViewer/ospExampleViewerSg.cpp new file mode 100644 index 0000000000..666e7885c3 --- /dev/null +++ b/apps/exampleViewer/ospExampleViewerSg.cpp @@ -0,0 +1,343 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "common/sg/SceneGraph.h" +#include "common/sg/Renderer.h" +#include "common/sg/importer/Importer.h" +#include "ospcommon/FileName.h" +#include "ospcommon/networking/Socket.h" +#include "ospcommon/vec.h" + +#include "sg/geometry/TriangleMesh.h" + +#include "widgets/imguiViewerSg.h" +#include + +namespace dw { + + struct ServiceInfo { + /* constructor that initializes everything to default values */ + ServiceInfo() + : totalPixelsInWall(-1,-1), + mpiPortName("") + {} + + /*! total pixels in the entire display wall, across all + indvididual displays, and including bezels (future versios + will allow to render to smaller resolutions, too - and have + the clients upscale this - but for now the client(s) have to + render at exactly this resolution */ + ospcommon::vec2i totalPixelsInWall; + + /*! the MPI port name that the service is listening on client + connections for (ie, the one to use with + client::establishConnection) */ + std::string mpiPortName; + + /*! whether this runs in stereo mode */ + int stereo; + + /*! read a service info from a given hostName:port. The service + has to already be running on that port + + Note this may throw a std::runtime_error if the connection + cannot be established + */ + void getFrom(const std::string &hostName, + const int portNo); + }; + /*! read a service info from a given hostName:port. The service + has to already be running on that port */ + void ServiceInfo::getFrom(const std::string &hostName, + const int portNo) + { + ospcommon::socket_t sock = ospcommon::connect(hostName.c_str(),portNo); + if (!sock) + throw std::runtime_error("could not create display wall connection!"); + + mpiPortName = read_string(sock); + totalPixelsInWall.x = read_int(sock); + totalPixelsInWall.y = read_int(sock); + stereo = read_int(sock); + close(sock); + } +} + + +using namespace ospcommon; +using namespace ospray; + +std::vector files; +std::string initialRendererType; +bool addPlane = true; +bool debug = false; +bool fullscreen = false; +bool print = false; + +void parseCommandLine(int ac, const char **&av) +{ + for (int i = 1; i < ac; i++) { + const std::string arg = av[i]; + if (arg == "-np" || arg == "--no-plane") { + addPlane = false; + } else if (arg == "-d" || arg == "--debug") { + debug = true; + } else if (arg == "-r" || arg == "--renderer") { + initialRendererType = av[++i]; + } else if (arg == "-m" || arg == "--module") { + ospLoadModule(av[++i]); + } else if (arg == "--print") { + print=true; + } else if (arg == "--fullscreen") { + fullscreen = true; + } else if (arg[0] != '-') { + files.push_back(av[i]); + } + } +} + +//parse command line arguments containing the format: +// -nodeName:...:nodeName=value,value,value +void parseCommandLineSG(int ac, const char **&av, sg::Node &root) +{ + for(int i=1;i < ac; i++) { + std::string arg(av[i]); + size_t f; + std::string value(""); + if (arg.size() < 2 || arg[0] != '-') + continue; + + while ((f = arg.find(":")) != std::string::npos || + (f = arg.find(",")) != std::string::npos) { + arg[f] = ' '; + } + + f = arg.find("="); + if (f != std::string::npos) + value = arg.substr(f+1,arg.size()); + + if (value != "") { + std::stringstream ss; + ss << arg.substr(1,f-1); + std::string child; + std::reference_wrapper node_ref = root; + while (ss >> child) { + node_ref = node_ref.get().childRecursive(child); + } + auto &node = node_ref.get(); + //Carson: TODO: reimplement with a way of determining type of node value + // currently relies on exception on value cast + try { + node.valueAs(); + node.setValue(value); + } catch(...) {}; + try { + std::stringstream vals(value); + float x; + vals >> x; + node.valueAs(); + node.setValue(x); + } catch(...) {} + try { + std::stringstream vals(value); + int x; + vals >> x; + node.valueAs(); + node.setValue(x); + } catch(...) {} + try { + std::stringstream vals(value); + bool x; + vals >> x; + node.valueAs(); + node.setValue(x); + } catch(...) {} + try { + std::stringstream vals(value); + float x,y,z; + vals >> x >> y >> z; + node.valueAs(); + node.setValue(ospcommon::vec3f(x,y,z)); + } catch(...) {} + try { + std::stringstream vals(value); + int x,y; + vals >> x >> y; + node.valueAs(); + node.setValue(ospcommon::vec2i(x,y)); + } catch(...) {} + } + } +} + +void addPlaneToScene(sg::Node& world) +{ + auto bbox = world.bounds(); + if (bbox.empty()) { + bbox.lower = vec3f(-5,0,-5); + bbox.upper = vec3f(5,10,5); + } + + osp::vec3f *vertices = new osp::vec3f[4]; + float ps = bbox.upper.x*3.f; + float py = bbox.lower.z-.1f; + + py = bbox.lower.y+0.01f; + vertices[0] = osp::vec3f{-ps, py, -ps}; + vertices[1] = osp::vec3f{-ps, py, ps}; + vertices[2] = osp::vec3f{ps, py, -ps}; + vertices[3] = osp::vec3f{ps, py, ps}; + auto position = std::make_shared((vec3f*)&vertices[0], + size_t(4), + false); + osp::vec3i *triangles = new osp::vec3i[2]; + triangles[0] = osp::vec3i{0,1,2}; + triangles[1] = osp::vec3i{1,2,3}; + auto index = std::make_shared((vec3i*)&triangles[0], + size_t(2), + false); + auto &plane = world.createChild("plane", "Instance"); + auto &mesh = plane.child("model").createChild("mesh", "TriangleMesh"); + + std::shared_ptr sg_plane = + std::static_pointer_cast(mesh.shared_from_this()); + sg_plane->vertex = position; + sg_plane->index = index; + auto &planeMaterial = mesh["material"]; + planeMaterial["Kd"].setValue(vec3f(0.5f)); + planeMaterial["Ks"].setValue(vec3f(0.6f)); + planeMaterial["Ns"].setValue(2.f); +} + +int main(int ac, const char **av) +{ + int init_error = ospInit(&ac, av); + if (init_error != OSP_NO_ERROR) { + std::cerr << "FATAL ERROR DURING INITIALIZATION!" << std::endl; + return init_error; + } + + auto device = ospGetCurrentDevice(); + ospDeviceSetStatusFunc(device, + [](const char *msg) { std::cout << msg; }); + + ospDeviceSetErrorFunc(device, + [](OSPError e, const char *msg) { + std::cout << "OSPRAY ERROR [" << e << "]: " + << msg << std::endl; + std::exit(1); + }); + +#ifdef _WIN32 + // TODO: Why do we not have the sg symbols already available for us + // since we link against it? + loadLibrary("ospray_sg"); +#endif + + ospray::imgui3D::init(&ac,av); + + parseCommandLine(ac, av); + + auto renderer_ptr = sg::createNode("renderer", "Renderer"); + auto &renderer = *renderer_ptr; + /*! the renderer we use for rendering on the display wall; null if + no dw available */ + std::shared_ptr rendererDW; + /*! display wall service info - ignore if 'rendererDW' is null */ + dw::ServiceInfo dwService; + + const char *dwNodeName = getenv("DISPLAY_WALL"); + if (dwNodeName) { + std::cout << "#######################################################" + << std::endl; + std::cout << "found a DISPLAY_WALL environment variable ...." << std::endl; + std::cout << "trying to connect to display wall service on " + << dwNodeName << ":2903" << std::endl; + + dwService.getFrom(dwNodeName,2903); + std::cout << "found display wall service on MPI port " + << dwService.mpiPortName << std::endl; + std::cout << "#######################################################" + << std::endl; + rendererDW = sg::createNode("renderer", "Renderer"); + } + + renderer["shadowsEnabled"].setValue(true); + renderer["aoSamples"].setValue(1); + renderer["camera"]["fovy"].setValue(60.f); + + if (rendererDW.get()) { + rendererDW->child("shadowsEnabled").setValue(true); + rendererDW->child("aoSamples").setValue(1); + rendererDW->child("camera")["fovy"].setValue(60.f); + } + + if (!initialRendererType.empty()) { + renderer["rendererType"].setValue(initialRendererType); + if (rendererDW.get()) { + rendererDW->child("rendererType").setValue(initialRendererType); + } + } + + auto &lights = renderer["lights"]; + + auto &sun = lights.createChild("sun", "DirectionalLight"); + sun["color"].setValue(vec3f(1.f,232.f/255.f,166.f/255.f)); + sun["direction"].setValue(vec3f(0.462f,-1.f,-.1f)); + sun["intensity"].setValue(1.5f); + + auto &bounce = lights.createChild("bounce", "DirectionalLight"); + bounce["color"].setValue(vec3f(127.f/255.f,178.f/255.f,255.f/255.f)); + bounce["direction"].setValue(vec3f(-.93,-.54f,-.605f)); + bounce["intensity"].setValue(0.25f); + + auto &ambient = lights.createChild("ambient", "AmbientLight"); + ambient["intensity"].setValue(0.9f); + ambient["color"].setValue(vec3f(174.f/255.f,218.f/255.f,255.f/255.f)); + + auto &world = renderer["world"]; + + for (auto file : files) { + FileName fn = file; + auto importerNode_ptr = sg::createNode(fn.name(), "Importer"); + auto &importerNode = *importerNode_ptr; + importerNode["fileName"].setValue(fn.str()); + world += importerNode_ptr; + } + + parseCommandLineSG(ac, av, renderer); + + if (rendererDW.get()) { + rendererDW->setChild("world", renderer["world"].shared_from_this()); + rendererDW->setChild("lights", renderer["lights"].shared_from_this()); + + auto &frameBuffer = rendererDW->child("frameBuffer"); + frameBuffer["size"].setValue(dwService.totalPixelsInWall); + frameBuffer["displayWall"].setValue(dwService.mpiPortName); + } + + if (print || debug) + renderer.traverse("print"); + + ospray::ImGuiViewerSg window(renderer_ptr, rendererDW); + + if (addPlane) addPlaneToScene(world); + + window.create("OSPRay Example Viewer App", fullscreen); + + ospray::imgui3D::run(); +} + diff --git a/apps/exampleViewer/widgets/CMakeLists.txt b/apps/exampleViewer/widgets/CMakeLists.txt new file mode 100644 index 0000000000..658a4c8eb7 --- /dev/null +++ b/apps/exampleViewer/widgets/CMakeLists.txt @@ -0,0 +1,55 @@ +## ======================================================================== ## +## Copyright 2009-2016 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +include_directories( + ${CMAKE_CURRENT_LIST_DIR}/../common/gl3w + ${CMAKE_CURRENT_LIST_DIR}/../common/imgui + ${GLFW_INCLUDE_DIRS} + ${OPENGL_INCLUDE_DIR} +) + +ospray_create_library(ospray_imgui3d + Imgui3dExport.h + imgui_impl_glfw_gl3.cpp + imgui3D.cpp + imguiViewer.cpp +LINK + ospray + ospray_common + ospray_imgui_util + ospray_tfn + glfw + gl3w + imgui + ${OPENGL_LIBRARIES} +) + +ospray_create_library(ospray_imgui3d_sg + Imgui3dExport.h + imgui_impl_glfw_gl3.cpp + imgui3D.cpp + imguiViewerSg.cpp + transferFunction.cpp +LINK + ospray + ospray_common + ospray_imgui_util_sg + ospray_tfn + glfw + gl3w + imgui + ${OPENGL_LIBRARIES} +) diff --git a/apps/common/widgets/Glut3dExport.h b/apps/exampleViewer/widgets/Imgui3dExport.h similarity index 80% rename from apps/common/widgets/Glut3dExport.h rename to apps/exampleViewer/widgets/Imgui3dExport.h index b2a5fbe40c..96ca3a1254 100644 --- a/apps/common/widgets/Glut3dExport.h +++ b/apps/exampleViewer/widgets/Imgui3dExport.h @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2017 Intel Corporation // +// Copyright 2009-2016 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -17,11 +17,11 @@ #pragma once #ifdef _WIN32 -# ifdef ospray_glut3d_EXPORTS -# define OSPRAY_GLUT3D_INTERFACE __declspec(dllexport) +# if defined(ospray_imgui3d_EXPORTS) || defined(ospray_imgui3d_sg_EXPORTS) +# define OSPRAY_IMGUI3D_INTERFACE __declspec(dllexport) # else -# define OSPRAY_GLUT3D_INTERFACE __declspec(dllimport) +# define OSPRAY_IMGUI3D_INTERFACE __declspec(dllimport) # endif #else -# define OSPRAY_GLUT3D_INTERFACE +# define OSPRAY_IMGUI3D_INTERFACE #endif diff --git a/apps/exampleViewer/widgets/imgui3D.cpp b/apps/exampleViewer/widgets/imgui3D.cpp new file mode 100644 index 0000000000..ee84d07633 --- /dev/null +++ b/apps/exampleViewer/widgets/imgui3D.cpp @@ -0,0 +1,833 @@ +// ======================================================================== // +// Copyright 2016 SURVICE Engineering Company // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "imgui3D.h" + +#include +#include "imgui_impl_glfw_gl3.h" + +#include "ospcommon/utility/CodeTimer.h" +#include "ospray/version.h" + +#include +#include +#include + +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# define _USE_MATH_DEFINES +# include // M_PI +#else +# include +#endif + +#include +#include +#include +#include + +extern "C" void glDrawPixels( GLsizei width, GLsizei height, + GLenum format, GLenum type, + const GLvoid *pixels ); + +namespace ospray { + + namespace imgui3D { + + bool dumpScreensDuringAnimation = false; + + static ImGui3DWidget *currentWidget = nullptr; + + bool ImGui3DWidget::showGui = false; + + // Class definitions ////////////////////////////////////////////////////// + + /*! write given frame buffer to file, in PPM P6 format. */ + void saveFrameBufferToFile(const char *fileName, + const uint32_t *pixel, + const uint32_t sizeX, const uint32_t sizeY) + { + FILE *file = fopen(fileName,"wb"); + if (!file) { + std::cerr << "#osp:glut3D: Warning - could not create screenshot file '" + << fileName << "'" << std::endl; + return; + } + fprintf(file,"P6\n%i %i\n255\n",sizeX,sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (size_t y = 0; y < sizeY; y++) { + const unsigned char *in = + (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (size_t x = 0; x < sizeX; x++) { + out[3*x+0] = in[4*x+0]; + out[3*x+1] = in[4*x+1]; + out[3*x+2] = in[4*x+2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file,"\n"); + fclose(file); + std::cout << "#osp:glut3D: saved framebuffer to file " + << fileName << std::endl; + } + +#define INVERT_RMB + /*! currently active window */ + ImGui3DWidget *ImGui3DWidget::activeWindow = nullptr; + vec2i ImGui3DWidget::defaultInitSize(1024,768); + + bool ImGui3DWidget::animating = false; + + // InspectCenter Glut3DWidget::INSPECT_CENTER; + /*! viewport as specified on the command line */ + ImGui3DWidget::ViewPort *viewPortFromCmdLine = nullptr; + vec3f upVectorFromCmdLine(0,1,0); + + // ------------------------------------------------------------------ + // implementation of glut3d::viewPorts + // ------------------------------------------------------------------ + ImGui3DWidget::ViewPort::ViewPort() : + modified(true), + from(0,0,-1), + at(0,0,0), + up(upVectorFromCmdLine), + openingAngle(60.f), + aspect(1.f) + { + frame = AffineSpace3fa::translate(from) * AffineSpace3fa(ospcommon::one); + } + + void ImGui3DWidget::ViewPort::snapUp() + { + if (fabsf(dot(up,frame.l.vz)) < 1e-3f) + return; + frame.l.vx = normalize(cross(frame.l.vy,up)); + frame.l.vz = normalize(cross(frame.l.vx,frame.l.vy)); + frame.l.vy = normalize(cross(frame.l.vz,frame.l.vx)); + } + + void ImGui3DWidget::motion(const vec2i &pos) + { + currMousePos = pos; + if (!renderingPaused) manipulator->motion(this); + lastMousePos = currMousePos; + } + + ImGui3DWidget::ImGui3DWidget(FrameBufferMode frameBufferMode, + ManipulatorMode initialManipulator, + int allowedManipulators) : + lastMousePos(-1,-1), + currMousePos(-1,-1), + windowSize(-1,-1), + motionSpeed(.003f), + rotateSpeed(.003f), + frameBufferMode(frameBufferMode), + fontScale(2.f), + ucharFB(nullptr) + { + if (activeWindow != nullptr) + throw std::runtime_error("ERROR: Can't create more than one ImGui3DWidget!"); + + activeWindow = this; + + worldBounds.lower = vec3f(-1); + worldBounds.upper = vec3f(+1); + + if (allowedManipulators & INSPECT_CENTER_MODE) { + inspectCenterManipulator = new InspectCenter(this); + } + if (allowedManipulators & MOVE_MODE) { + moveModeManipulator = new MoveMode(this); + } + switch(initialManipulator) { + case MOVE_MODE: + manipulator = moveModeManipulator; + break; + case INSPECT_CENTER_MODE: + manipulator = inspectCenterManipulator; + break; + } + Assert2(manipulator != nullptr,"invalid initial manipulator mode"); + + if (viewPortFromCmdLine) { + viewPort = *viewPortFromCmdLine; + + if (length(viewPort.up) < 1e-3f) + viewPort.up = vec3f(0,0,1.f); + + this->worldBounds = worldBounds; + computeFrame(); + } + + currButton[0] = currButton[1] = currButton[2] = GLFW_RELEASE; + + displayTime=-1.f; + renderTime=-1.f; + guiTime=-1.f; + totalTime=-1.f; + + } + + void ImGui3DWidget::computeFrame() + { + viewPort.frame.l.vy = normalize(viewPort.at - viewPort.from); + viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,viewPort.up)); + viewPort.frame.l.vz = + normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); + viewPort.frame.p = viewPort.from; + viewPort.snapUp(); + viewPort.modified = true; + } + + void ImGui3DWidget::setZUp(const vec3f &up) + { + viewPort.up = up; + if (up != vec3f(0.f)) + viewPort.snapUp(); + } + + void ImGui3DWidget::reshape(const vec2i &newSize) + { + windowSize = newSize; + viewPort.aspect = newSize.x/float(newSize.y); + } + + void ImGui3DWidget::display() + { + if (animating) { + auto *hack = + (InspectCenter*)ImGui3DWidget::activeWindow->inspectCenterManipulator; + hack->rotate(-10.f * ImGui3DWidget::activeWindow->motionSpeed, 0); + } + + + if (frameBufferMode == ImGui3DWidget::FRAMEBUFFER_UCHAR && ucharFB) { + glDrawPixels(windowSize.x, windowSize.y, + GL_RGBA, GL_UNSIGNED_BYTE, ucharFB); +#ifndef _WIN32 + if (ImGui3DWidget::animating && dumpScreensDuringAnimation) { + char tmpFileName[] = "/tmp/ospray_scene_dump_file.XXXXXXXXXX"; + static const char *dumpFileRoot; + if (!dumpFileRoot) + dumpFileRoot = getenv("OSPRAY_SCREEN_DUMP_ROOT"); + if (!dumpFileRoot) { + auto rc = mkstemp(tmpFileName); + (void)rc; + dumpFileRoot = tmpFileName; + } + + char fileName[100000]; + sprintf(fileName,"%s_%08ld.ppm",dumpFileRoot,times(nullptr)); + saveFrameBufferToFile(fileName,ucharFB,windowSize.x,windowSize.y); + } +#endif + } else if (frameBufferMode == ImGui3DWidget::FRAMEBUFFER_FLOAT && floatFB) { + glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_FLOAT, floatFB); + } else { + glClearColor(0.f,0.f,0.f,1.f); + glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + } + } + + void ImGui3DWidget::buildGui() + { + } + + void ImGui3DWidget::setViewPort(const vec3f from, + const vec3f at, + const vec3f up) + { + const vec3f dir = at - from; + viewPort.at = at; + viewPort.from = from; + viewPort.up = up; + + this->worldBounds = worldBounds; + viewPort.frame.l.vy = normalize(dir); + viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,up)); + viewPort.frame.l.vz = + normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); + viewPort.frame.p = from; + viewPort.snapUp(); + viewPort.modified = true; + } + + void ImGui3DWidget::setWorldBounds(const box3f &worldBounds) + { + vec3f center = ospcommon::center(worldBounds); + vec3f diag = worldBounds.size(); + diag = max(diag,vec3f(0.3f*length(diag))); + vec3f from = center - .75f*vec3f(-.6*diag.x,-1.2f*diag.y,.8f*diag.z); + vec3f dir = center - from; + vec3f up = viewPort.up; + + if (!viewPortFromCmdLine) { + viewPort.at = center; + viewPort.from = from; + viewPort.up = up; + + if (length(up) < 1e-3f) + up = vec3f(0,0,1.f); + + this->worldBounds = worldBounds; + viewPort.frame.l.vy = normalize(dir); + viewPort.frame.l.vx = normalize(cross(viewPort.frame.l.vy,up)); + viewPort.frame.l.vz = + normalize(cross(viewPort.frame.l.vx,viewPort.frame.l.vy)); + viewPort.frame.p = from; + viewPort.snapUp(); + viewPort.modified = true; + } + + motionSpeed = length(diag) * .001f; + } + + void ImGui3DWidget::setTitle(const std::string &title) + { + Assert2(window != nullptr, + "must call Glut3DWidget::create() before calling setTitle()"); + glfwSetWindowTitle(window, title.c_str()); + } + + void ImGui3DWidget::create(const char *title, bool fullScreen) + { + // Setup window + auto error_callback = [](int error, const char* description) { + fprintf(stderr, "Error %d: %s\n", error, description); + }; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + throw std::runtime_error("Could not initialize glfw!"); + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + auto size = defaultInitSize; + + auto defaultSizeFromEnv = + getEnvVar("OSPRAY_APPS_DEFAULT_WINDOW_SIZE"); + + if (defaultSizeFromEnv.first) { + int rc = sscanf(defaultSizeFromEnv.second.c_str(), + "%dx%d", &size.x, &size.y); + if (rc != 2) { + throw std::runtime_error("could not parse" + " OSPRAY_APPS_DEFAULT_WINDOW_SIZE " + "env-var. Must be of format xx<>Z " + "(e.g., '1024x768'"); + } + } + + if (fullScreen) { + auto *monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + + window = glfwCreateWindow(mode->width, mode->height, + title, monitor, nullptr); + } + else + window = glfwCreateWindow(size.x, size.y, title, nullptr, nullptr); + + glfwMakeContextCurrent(window); + gl3wInit(); + + // NOTE(jda) - move key handler registration into this class + ImGui_ImplGlfwGL3_Init(window, true); + + + glfwSetCursorPosCallback( + window, + [](GLFWwindow*, double xpos, double ypos) { + ImGuiIO& io = ImGui::GetIO(); + if (!io.WantCaptureMouse) + ImGui3DWidget::activeWindow->motion(vec2i(xpos, ypos)); + } + ); + + glfwSetMouseButtonCallback( + window, + [](GLFWwindow*, int button, int action, int mods) { + ImGui3DWidget::activeWindow->currButton[button] = action; + } + ); + + glfwSetCharCallback( + window, + [](GLFWwindow*, unsigned int c) { + ImGuiIO& io = ImGui::GetIO(); + if (c > 0 && c < 0x10000) + io.AddInputCharacter((unsigned short)c); + + if (!io.WantCaptureKeyboard) + ImGui3DWidget::activeWindow->keypress(c); + } + ); + + currentWidget = this; + } + + void run() + { + if (!currentWidget) + throw std::runtime_error("ImGuiViewer window not created/set!"); + + auto *window = currentWidget->window; + + int display_w = 0, display_h = 0; + + ImFontConfig config; + config.MergeMode = false; + + #if 0 // NOTE(jda) - this can cause crashes in Debug builds, needs fixed + ImGuiIO& io = ImGui::GetIO(); + ImFont* font = + io.Fonts->AddFontFromFileTTF("LibreBaskerville-Regular.ttf", + 28, &config); + if (font) { + std::cout << "loaded font\n"; + currentWidget->fontScale = 1.f; + } + #endif + + // Main loop + while (!glfwWindowShouldClose(window)) { + ospcommon::utility::CodeTimer timer; + ospcommon::utility::CodeTimer timerTotal; + + timerTotal.start(); + glfwPollEvents(); + timer.start(); + ImGui_ImplGlfwGL3_NewFrame(); + timer.stop(); + + if (ImGui3DWidget::showGui) + currentWidget->buildGui(); + + ImGui::SetNextWindowPos(ImVec2(10,10)); + auto overlayFlags = ImGuiWindowFlags_NoTitleBar | + ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings; + bool open; + if (!ImGui::Begin("Example: Fixed Overlay", &open, + ImVec2(0,0), 0.f, overlayFlags)) { + ImGui::End(); + } else { + ImFont* font = ImGui::GetFont(); + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(.3,.3,.3,1.f)); + ImGui::SetWindowFontScale(currentWidget->fontScale*1.0f); + font->Scale = 5.f; + ImGui::Text("%s", ("OSPRay v" + std::string(OSPRAY_VERSION)).c_str()); + font->Scale = 1.f; + ImGui::SetWindowFontScale(currentWidget->fontScale*0.7f); + ImGui::PopStyleColor(1); + + std::stringstream ss; + ss << 1.f/currentWidget->renderTime; + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(.8,.8,.8,1.f)); + ImGui::Text("%s", ("FPS: " + ss.str()).c_str()); + ImGui::Text("press \'g\' for menu"); + ImGui::PopStyleColor(1); + + ImGui::End(); + } + + timer.start(); + int new_w = 0, new_h = 0; + glfwGetFramebufferSize(window, &new_w, &new_h); + + if (new_w != display_w || new_h != display_h) { + display_w = new_w; + display_h = new_h; + currentWidget->reshape(vec2i(display_w, display_h)); + } + + glViewport(0, 0, new_w, new_h); + timer.stop(); + + timer.start(); + // Render OSPRay frame + currentWidget->display(); + timer.stop(); + currentWidget->displayTime = timer.secondsSmoothed(); + + timer.start(); + // Render GUI + ImGui::Render(); + timer.stop(); + currentWidget->guiTime = timer.secondsSmoothed(); + + glfwSwapBuffers(window); + timerTotal.stop(); + currentWidget->totalTime = timerTotal.secondsSmoothed(); + } + + // Cleanup + ImGui_ImplGlfwGL3_Shutdown(); + glfwTerminate(); + } + + void init(int32_t *ac, const char **av) + { + for(int i = 1; i < *ac;i++) { + std::string arg(av[i]); + if (arg == "-win") { + std::string arg2(av[i+1]); + size_t pos = arg2.find("x"); + if (pos != std::string::npos) { + arg2.replace(pos, 1, " "); + std::stringstream ss(arg2); + ss >> ImGui3DWidget::defaultInitSize.x + >> ImGui3DWidget::defaultInitSize.y; + removeArgs(*ac,(char **&)av,i,2); --i; + } else { + ImGui3DWidget::defaultInitSize.x = atoi(av[i+1]); + ImGui3DWidget::defaultInitSize.y = atoi(av[i+1]); + removeArgs(*ac,(char **&)av,i,3); --i; + } + continue; + } if (arg == "--1k" || arg == "-1k") { + ImGui3DWidget::defaultInitSize.x = + ImGui3DWidget::defaultInitSize.y = 1024; + removeArgs(*ac,(char **&)av,i,1); --i; + continue; + } if (arg == "--size") { + ImGui3DWidget::defaultInitSize.x = atoi(av[i+1]); + ImGui3DWidget::defaultInitSize.y = atoi(av[i+2]); + removeArgs(*ac,(char **&)av,i,3); --i; + continue; + } if (arg == "-v" || arg == "--view") { + std::ifstream fin(av[i+1]); + if (!fin.is_open()) + { + throw std::runtime_error("Failed to open \"" + + std::string(av[i+1]) + + "\" for reading"); + } + + if (!viewPortFromCmdLine) + viewPortFromCmdLine = new ImGui3DWidget::ViewPort; + + auto& fx = viewPortFromCmdLine->from.x; + auto& fy = viewPortFromCmdLine->from.y; + auto& fz = viewPortFromCmdLine->from.z; + + auto& ax = viewPortFromCmdLine->at.x; + auto& ay = viewPortFromCmdLine->at.y; + auto& az = viewPortFromCmdLine->at.z; + + auto& ux = upVectorFromCmdLine.x; + auto& uy = upVectorFromCmdLine.y; + auto& uz = upVectorFromCmdLine.z; + + auto& fov = viewPortFromCmdLine->openingAngle; + + auto token = std::string(""); + while (fin >> token) { + if (token == "-vp") + fin >> fx >> fy >> fz; + else if (token == "-vu") + fin >> ux >> uy >> uz; + else if (token == "-vi") + fin >> ax >> ay >> az; + else if (token == "-fv") + fin >> fov; + else { + throw std::runtime_error("Unrecognized token: \"" + token + + '\"'); + } + } + + assert(i+1 < *ac); + removeArgs(*ac,(char **&)av, i, 2); --i; + continue; + } if (arg == "-vu") { + upVectorFromCmdLine.x = atof(av[i+1]); + upVectorFromCmdLine.y = atof(av[i+2]); + upVectorFromCmdLine.z = atof(av[i+3]); + if (viewPortFromCmdLine) + viewPortFromCmdLine->up = upVectorFromCmdLine; + assert(i+3 < *ac); + removeArgs(*ac,(char **&)av,i,4); --i; + continue; + } if (arg == "-vp") { + if (!viewPortFromCmdLine) + viewPortFromCmdLine = new ImGui3DWidget::ViewPort; + viewPortFromCmdLine->from.x = atof(av[i+1]); + viewPortFromCmdLine->from.y = atof(av[i+2]); + viewPortFromCmdLine->from.z = atof(av[i+3]); + assert(i+3 < *ac); + removeArgs(*ac,(char **&)av,i,4); --i; + continue; + } if (arg == "-vi") { + if (!viewPortFromCmdLine) + viewPortFromCmdLine = new ImGui3DWidget::ViewPort; + viewPortFromCmdLine->at.x = atof(av[i+1]); + viewPortFromCmdLine->at.y = atof(av[i+2]); + viewPortFromCmdLine->at.z = atof(av[i+3]); + assert(i+3 < *ac); + removeArgs(*ac,(char **&)av,i,4); --i; + continue; + } + } + } + + // ------------------------------------------------------------------ + // base manipulator + // ------------------------------------------------------------------ + void Manipulator::motion(ImGui3DWidget *widget) + { + auto &state = widget->currButton; + if (state[GLFW_MOUSE_BUTTON_RIGHT] == GLFW_PRESS) { + dragRight(widget,widget->currMousePos,widget->lastMousePos); + } else if (state[GLFW_MOUSE_BUTTON_MIDDLE] == GLFW_PRESS) { + dragMiddle(widget,widget->currMousePos,widget->lastMousePos); + } else if (state[GLFW_MOUSE_BUTTON_LEFT] == GLFW_PRESS) { + dragLeft(widget,widget->currMousePos,widget->lastMousePos); + } + } + + // ------------------------------------------------------------------ + // INSPECT_CENTER manipulator + // ------------------------------------------------------------------ + InspectCenter::InspectCenter(ImGui3DWidget *widget) + : Manipulator(widget) + , pivot(ospcommon::center(widget->worldBounds)) + {} + + void InspectCenter::rotate(float du, float dv) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + const vec3f pivot = widget->viewPort.at;//center(widget->worldBounds); + AffineSpace3fa xfm + = AffineSpace3fa::translate(pivot) + * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) + * AffineSpace3fa::rotate(cam.frame.l.vz,-du) + * AffineSpace3fa::translate(-pivot); + cam.frame = xfm * cam.frame; + cam.from = xfmPoint(xfm,cam.from); + cam.at = xfmPoint(xfm,cam.at); + cam.snapUp(); + cam.modified = true; + } + + /*! INSPECT_CENTER::RightButton: move lookfrom/viewPort positoin + forward/backward on right mouse button */ + void InspectCenter::dragRight(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float fwd = +#ifdef INVERT_RMB +#else + - +#endif + (to.y - from.y) * 4 * widget->motionSpeed; + // * length(widget->worldBounds.size()); + float oldDist = length(cam.at - cam.from); + float newDist = oldDist - fwd; + if (newDist < 1e-3f) + return; + cam.from = cam.at - newDist * cam.frame.l.vy; + cam.frame.p = cam.from; + cam.modified = true; + } + + /*! INSPECT_CENTER::MiddleButton: move lookat/center of interest + forward/backward on middle mouse button */ + void InspectCenter::dragMiddle(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float du = (to.x - from.x); + float dv = (to.y - from.y); + + AffineSpace3fa xfm = + AffineSpace3fa::translate(widget->motionSpeed * dv * cam.frame.l.vz ) + * AffineSpace3fa::translate(-1.0f * widget->motionSpeed + * du * cam.frame.l.vx); + + cam.frame = xfm * cam.frame; + cam.from = xfmPoint(xfm, cam.from); + cam.at = xfmPoint(xfm, cam.at); + cam.modified = true; + } + + void InspectCenter::dragLeft(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float du = (to.x - from.x) * widget->rotateSpeed; + float dv = (to.y - from.y) * widget->rotateSpeed; + + const vec3f pivot = cam.at; + AffineSpace3fa xfm + = AffineSpace3fa::translate(pivot) + * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) + * AffineSpace3fa::rotate(cam.frame.l.vz,-du) + * AffineSpace3fa::translate(-pivot); + cam.frame = xfm * cam.frame; + cam.from = xfmPoint(xfm,cam.from); + cam.at = xfmPoint(xfm,cam.at); + cam.snapUp(); + cam.modified = true; + } + + // ------------------------------------------------------------------ + // MOVE_MOVE manipulator - TODO. + // ------------------------------------------------------------------ + + void MoveMode::dragRight(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float fwd = +#ifdef INVERT_RMB +#else + - +#endif + (to.y - from.y) * 4 * widget->motionSpeed; + cam.from = cam.from + fwd * cam.frame.l.vy; + cam.at = cam.at + fwd * cam.frame.l.vy; + cam.frame.p = cam.from; + cam.modified = true; + } + + /*! todo */ + void MoveMode::dragMiddle(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float du = (to.x - from.x); + float dv = (to.y - from.y); + + auto xfm = + AffineSpace3fa::translate(widget->motionSpeed * dv * cam.frame.l.vz ) * + AffineSpace3fa::translate(-1.0f * widget->motionSpeed * du * cam.frame.l.vx); + + cam.frame = xfm * cam.frame; + cam.from = xfmPoint(xfm, cam.from); + cam.at = xfmPoint(xfm, cam.at); + cam.modified = true; + } + + void MoveMode::dragLeft(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) + { + ImGui3DWidget::ViewPort &cam = widget->viewPort; + float du = (to.x - from.x) * widget->rotateSpeed; + float dv = (to.y - from.y) * widget->rotateSpeed; + + const vec3f pivot = cam.from; //center(widget->worldBounds); + AffineSpace3fa xfm + = AffineSpace3fa::translate(pivot) + * AffineSpace3fa::rotate(cam.frame.l.vx,-dv) + * AffineSpace3fa::rotate(cam.frame.l.vz,-du) + * AffineSpace3fa::translate(-pivot); + cam.frame = xfm * cam.frame; + cam.from = xfmPoint(xfm,cam.from); + cam.at = xfmPoint(xfm,cam.at); + cam.snapUp(); + cam.modified = true; + } + + void ImGui3DWidget::keypress(char key) + { + switch (key) { + case '!': { + if (ImGui3DWidget::animating) { + dumpScreensDuringAnimation = !dumpScreensDuringAnimation; + } else { + char tmpFileName[] = "/tmp/ospray_screen_dump_file.XXXXXXXX"; + static const char *dumpFileRoot; + if (!dumpFileRoot) + dumpFileRoot = getenv("OSPRAY_SCREEN_DUMP_ROOT"); +#ifndef _WIN32 + if (!dumpFileRoot) { + auto rc = mkstemp(tmpFileName); + (void)rc; + dumpFileRoot = tmpFileName; + } +#endif + char fileName[100000]; + static int frameDumpSequenceID = 0; + sprintf(fileName,"%s_%05d.ppm",dumpFileRoot,frameDumpSequenceID++); + if (ucharFB) + saveFrameBufferToFile(fileName,ucharFB,windowSize.x,windowSize.y); + return; + } + + break; + } + case 'C': + PRINT(viewPort); + break; + case 'g': + showGui = !showGui; + break; + case 'I': + manipulator = inspectCenterManipulator; + break; + case 'M': + case 'F': + manipulator = moveModeManipulator; + break; + case 'A': + ImGui3DWidget::animating = !ImGui3DWidget::animating; + break; + case '+': + motionSpeed *= 1.5f; + break; + case '-': + motionSpeed /= 1.5f; + break; + case 27 /*ESC*/: + case 'q': + case 'Q': + std::exit(0); + break; + default: + break; + } + } + + std::ostream &operator<<(std::ostream &o, const ImGui3DWidget::ViewPort &cam) + { + o << "// " + << " -vp " << cam.from.x << " " << cam.from.y << " " << cam.from.z + << " -vi " << cam.at.x << " " << cam.at.y << " " << cam.at.z + << " -vu " << cam.up.x << " " << cam.up.y << " " << cam.up.z + << std::endl; + o << "" << std::endl; + o << " " << cam.from.x << " " << cam.from.y << " " << cam.from.z << "" << std::endl; + o << " " << cam.at.x << " " << cam.at.y << " " << cam.at.z << "" << std::endl; + o << " " << cam.up.x << " " << cam.up.y << " " << cam.up.z << "" << std::endl; + o << " " << cam.aspect << "" << std::endl; + o << " " << cam.frame.l.vx << "" << std::endl; + o << " " << cam.frame.l.vy << "" << std::endl; + o << " " << cam.frame.l.vz << "" << std::endl; + o << " " << cam.frame.p << "" << std::endl; + o << ""; + return o; + } + } +} + diff --git a/apps/exampleViewer/widgets/imgui3D.h b/apps/exampleViewer/widgets/imgui3D.h new file mode 100644 index 0000000000..885f06e95b --- /dev/null +++ b/apps/exampleViewer/widgets/imgui3D.h @@ -0,0 +1,247 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "ospcommon/common.h" +#include "ospcommon/box.h" +#include "ospcommon/AffineSpace.h" + +#include "Imgui3dExport.h" + +class GLFWwindow; + +namespace ospray { + //! dedicated namespace for 3D glut viewer widget + namespace imgui3D { + + using namespace ospcommon; + + /*! initialize everything IMGUI-related */ + OSPRAY_IMGUI3D_INTERFACE void init(int32_t *ac, const char **av); + /*! switch over to IMGUI for control flow. This func will not return */ + OSPRAY_IMGUI3D_INTERFACE void run(); + + using ospcommon::AffineSpace3fa; + + struct ImGui3DWidget; + + struct OSPRAY_IMGUI3D_INTERFACE Manipulator { + // this is the fct that gets called when the mouse moved in the + // associated window + virtual void motion(ImGui3DWidget *widget); + Manipulator(ImGui3DWidget *widget) + : widget(widget) {} + + // helper functions called from the default 'motion' fct + virtual void dragLeft(ImGui3DWidget *widget, + const vec2i &to, + const vec2i &from) {} + virtual void dragRight(ImGui3DWidget *widget, + const vec2i &to, + const vec2i &from) {} + virtual void dragMiddle(ImGui3DWidget *widget, + const vec2i &to, + const vec2i &from) {} + + ImGui3DWidget *widget; + }; + + struct InspectCenter : public Manipulator + { + void dragLeft(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + void dragRight(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + void dragMiddle(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + InspectCenter(ImGui3DWidget *widget); + void rotate(float du, float dv); + + vec3f pivot; + }; + + struct MoveMode : public Manipulator + { + void dragLeft(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + void dragRight(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + void dragMiddle(ImGui3DWidget *widget, + const vec2i &to, const vec2i &from) override; + MoveMode(ImGui3DWidget *widget) : Manipulator(widget) {} + }; + + /*! a IMGUI-based 3D viewer widget that includes simple sample code + for manipulating a 3D viewPort with the mouse. + + This widget should allow users to easily write simple 3D viewers + with simple built-in viewPort motion. In theory, all one hsa to do + after creating the window (and initializeing the viewPort is + implement the respective 'display' callback to do the actual + rendering, using the widget's infrastructure for the boilerplace + stuff. + + If specified (\see FrameBufferMode the widget will automatically + allocate a frame buffer (of either uchar4 or float4 type), in + which case the 'display' routine will only have to write to the + respective frame buffer (the widget will do the respective gl + calls to display that framebuffer); if not specified, it's up to + the derived class to implement whatever opengl calls are required + to draw the window's content. + + */ + struct OSPRAY_IMGUI3D_INTERFACE ImGui3DWidget + { + /*! size we'll create a window at */ + static vec2i defaultInitSize; + + typedef enum { + FRAMEBUFFER_UCHAR,FRAMEBUFFER_FLOAT,FRAMEBUFFER_DEPTH,FRAMEBUFFER_NONE + } FrameBufferMode; + typedef enum { + MOVE_MODE =(1<<0), + INSPECT_CENTER_MODE =(1<<1) + } ManipulatorMode; + + /*! internal viewPort class */ + struct OSPRAY_IMGUI3D_INTERFACE ViewPort { + bool modified; /* the viewPort will set this flag any time any of + its values get changed. */ + + vec3f from; + vec3f at; + vec3f up; + float openingAngle; //!< in degrees, along Y direction + float aspect; //!< aspect ratio X:Y + // float focalDistance; + + /*! camera frame in which the Y axis is the depth axis, and X + and Z axes are parallel to the screen X and Y axis. The frame + itself remains normalized. */ + AffineSpace3fa frame; + + /*! set 'up' vector. if this vector is '0,0,0' the viewer will + *not* apply the up-vector after camera manipulation */ + void snapUp(); + + ViewPort(); + }; + + // static InspectCenter INSPECT_CENTER; + Manipulator *inspectCenterManipulator; + Manipulator *moveModeManipulator; + + /*! current manipulator */ + Manipulator *manipulator; + + ImGui3DWidget(FrameBufferMode frameBufferMode, + ManipulatorMode initialManipulator=INSPECT_CENTER_MODE, + int allowedManipulators=INSPECT_CENTER_MODE|MOVE_MODE); + + /*! set a default camera position that views given bounds from the + top left front */ + virtual void setWorldBounds(const box3f &worldBounds); + /*! set window title */ + void setTitle(const std::string &title); + /*! set viewport to given values */ + void setViewPort(const vec3f from, const vec3f at, const vec3f up); + + // ------------------------------------------------------------------ + // event handling - override this to change this widgets behavior + // to input events + // ------------------------------------------------------------------ + + virtual void motion(const vec2i &pos); + virtual void reshape(const vec2i &newSize); + /*! display this window. By default this will just clear this + window's framebuffer; it's up to the user to override this fct + to do something more useful */ + virtual void display(); + + virtual void buildGui(); + + // ------------------------------------------------------------------ + // helper functions + // ------------------------------------------------------------------ + /*! create this window. Note that this just *creates* the window, + but glut will not do anything else with this window before + 'run' got called */ + void create(const char *title, bool fullScreen = false); + + // ------------------------------------------------------------------ + // camera helper code + // ------------------------------------------------------------------ + void snapUp(); + /*! set 'up' vector. if this vector is '0,0,0' the viewer will + *not* apply the up-vector after camera manipulation */ + virtual void setZUp(const vec3f &up); + void noZUp() { setZUp(vec3f(0.f)); } + + // ------------------------------------------------------------------ + // internal state variables + // ------------------------------------------------------------------ + vec2i lastMousePos; /*! last mouse screen position of mouse before + current motion */ + vec2i currMousePos; /*! current screen position of mouse */ + int lastButton[3], currButton[3]; + ViewPort viewPort; + box3f worldBounds; /*!< world bounds, to automatically set viewPort + lookat, mouse speed, etc */ + vec2i windowSize; + /*! camera speed modifier - affects how many units the camera + _moves_ with each unit on the screen */ + float motionSpeed; + /*! camera rotation speed modifier - affects how many units the + camera _rotates_ with each unit on the screen */ + float rotateSpeed; + FrameBufferMode frameBufferMode; + + /*! recompute current viewPort's frame from cameras 'from', + 'at', 'up' values. */ + void computeFrame(); + + static ImGui3DWidget *activeWindow; + + static bool animating; + static bool showGui; + double displayTime; + double renderTime; + double guiTime; + double totalTime; + float fontScale; + + bool renderingPaused {false}; + /*! pointer to the frame buffer data. it is the repsonsiblity of + the applicatoin derived from this class to properly allocate + and deallocate the frame buffer pointer */ + union { + /*! uchar[4] RGBA-framebuffer, if applicable */ + uint32_t *ucharFB; + /*! float[4] RGBA-framebuffer, if applicable */ + vec3fa *floatFB; + }; + + GLFWwindow *window {nullptr}; + + virtual void keypress(char key); + }; + + OSPRAY_IMGUI3D_INTERFACE std::ostream &operator<<(std::ostream &o, + const ImGui3DWidget::ViewPort &cam); + } +} + diff --git a/apps/exampleViewer/widgets/imguiViewer.cpp b/apps/exampleViewer/widgets/imguiViewer.cpp new file mode 100644 index 0000000000..03483be429 --- /dev/null +++ b/apps/exampleViewer/widgets/imguiViewer.cpp @@ -0,0 +1,481 @@ +// ======================================================================== // +// Copyright 2016 SURVICE Engineering Company // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "imguiViewer.h" + +#include + +using std::string; +using namespace ospcommon; + +// Static local helper functions ////////////////////////////////////////////// + +// helper function to write the rendered image as PPM file +static void writePPM(const string &fileName, const int sizeX, const int sizeY, + const uint32_t *pixel) +{ + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); +} + +// ImGuiViewer definitions //////////////////////////////////////////////////// + +namespace ospray { + + ImGuiViewer::ImGuiViewer(const std::deque &worldBounds, + const std::deque &model, + cpp::Renderer renderer, + cpp::Camera camera) + : ImGuiViewer(worldBounds, model, renderer, + cpp::Renderer(), cpp::FrameBuffer(), camera) + { + } + + ImGuiViewer::ImGuiViewer(const std::deque &worldBounds, + const std::deque &model, + cpp::Renderer renderer, + cpp::Renderer rendererDW, + cpp::FrameBuffer frameBufferDW, + cpp::Camera camera) + : ImGui3DWidget(ImGui3DWidget::FRAMEBUFFER_NONE), + sceneModels(model), + worldBounds(worldBounds), + camera(camera), + renderer(renderer), + rendererDW(rendererDW), + frameBufferDW(frameBufferDW) + { + if (!worldBounds.empty()) + setWorldBounds(worldBounds[0]); + + renderer.set("model", sceneModels[0]); + renderer.set("camera", camera); + renderer.set("bgColor", 1.f, 1.f, 1.f, 1.f); + + if (rendererDW) { + rendererDW.set("model", sceneModels[0]); + rendererDW.set("camera", camera); + rendererDW.set("bgColor", 1.f, 1.f, 1.f, 1.f); + } + renderEngine.setRenderer(renderer, rendererDW, frameBufferDW); + renderEngine.setFbSize({1024, 768}); + + renderEngine.scheduleObjectCommit(renderer); + if (rendererDW) + renderEngine.scheduleObjectCommit(rendererDW); + renderEngine.start(); + + frameTimer = ospcommon::getSysTime(); + originalView = viewPort; + } + + ImGuiViewer::~ImGuiViewer() + { + renderEngine.stop(); + } + + void ImGuiViewer::setRenderer(OSPRenderer renderer, + OSPRenderer rendererDW, + OSPFrameBuffer frameBufferDW) + { + this->renderer = renderer; + this->rendererDW = rendererDW; + this->frameBufferDW = frameBufferDW; + renderEngine.setRenderer(renderer,rendererDW,frameBufferDW); + } + + void ImGuiViewer::reshape(const vec2i &newSize) + { + ImGui3DWidget::reshape(newSize); + windowSize = newSize; + + viewPort.modified = true; + + renderEngine.setFbSize(newSize); + pixelBuffer.resize(newSize.x * newSize.y); + } + + void ImGuiViewer::keypress(char key) + { + switch (key) { + case ' ': + animationPaused = !animationPaused; + break; + case '<': + animationFrameDelta = max(animationFrameDelta-0.01, 0.0001); + break; + case '>': + animationFrameDelta = min(animationFrameDelta+0.01, 1.0); + break; + case '=': + case '+': + motionSpeed *= 1.5f; + std::cout << "new motion speed: " << motionSpeed << std::endl; + break; + case '-': + motionSpeed /= 1.5f; + std::cout << "new motion speed: " << motionSpeed << std::endl; + break; + case 'R': + toggleRenderingPaused(); + break; + case '!': + saveScreenshot("ospimguiviewer"); + break; + case 'X': + if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(1,0,0); + } + viewPort.modified = true; + break; + case 'Y': + if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,1,0); + } + viewPort.modified = true; + break; + case 'Z': + if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,0,1); + } + viewPort.modified = true; + break; + case 'c': + viewPort.modified = true;//Reset accumulation + break; + case 'r': + resetView(); + break; + case 'p': + printViewport(); + break; + case 27 /*ESC*/: + case 'q': + case 'Q': + renderEngine.stop(); + std::exit(0); + break; + default: + ImGui3DWidget::keypress(key); + } + } + + void ImGuiViewer::resetView() + { + auto oldAspect = viewPort.aspect; + viewPort = originalView; + viewPort.aspect = oldAspect; + } + + void ImGuiViewer::printViewport() + { + printf("-vp %f %f %f -vu %f %f %f -vi %f %f %f\n", + viewPort.from.x, viewPort.from.y, viewPort.from.z, + viewPort.up.x, viewPort.up.y, viewPort.up.z, + viewPort.at.x, viewPort.at.y, viewPort.at.z); + fflush(stdout); + } + + void ImGuiViewer::saveScreenshot(const std::string &basename) + { + writePPM(basename + ".ppm", windowSize.x, windowSize.y, pixelBuffer.data()); + std::cout << "saved current frame to '" << basename << ".ppm'" << std::endl; + } + + void ImGuiViewer::toggleRenderingPaused() + { + renderingPaused = !renderingPaused; + renderingPaused ? renderEngine.stop() : renderEngine.start(); + } + + void ImGuiViewer::setWorldBounds(const box3f &worldBounds) + { + ImGui3DWidget::setWorldBounds(worldBounds); + aoDistance = (worldBounds.upper.x - worldBounds.lower.x)/4.f; + renderer.set("aoDistance", aoDistance); + if (rendererDW) + rendererDW.set("aoDistance", aoDistance); + renderEngine.scheduleObjectCommit(renderer); + } + + void ImGuiViewer::display() + { + updateAnimation(ospcommon::getSysTime()-frameTimer); + frameTimer = ospcommon::getSysTime(); + + if (viewPort.modified) { + Assert2(camera.handle(),"ospray camera is null"); + camera.set("pos", viewPort.from); + auto dir = viewPort.at - viewPort.from; + camera.set("dir", dir); + camera.set("up", viewPort.up); + camera.set("aspect", viewPort.aspect); + camera.set("fovy", viewPort.openingAngle); + + viewPort.modified = false; + renderEngine.scheduleObjectCommit(camera); + } + + if (renderEngine.hasNewFrame()) { + auto &mappedFB = renderEngine.mapFramebuffer(); + size_t nPixels = windowSize.x * windowSize.y; + + if (mappedFB.size() == nPixels) { + auto *srcPixels = mappedFB.data(); + auto *dstPixels = pixelBuffer.data(); + memcpy(dstPixels, srcPixels, nPixels * sizeof(uint32_t)); + lastFrameFPS = renderEngine.lastFrameFps(); + renderTime = 1.f/lastFrameFPS; + } + + renderEngine.unmapFramebuffer(); + } + + ucharFB = pixelBuffer.data(); + frameBufferMode = ImGui3DWidget::FRAMEBUFFER_UCHAR; + ImGui3DWidget::display(); + + // that pointer is no longer valid, so set it to null + ucharFB = nullptr; + } + + void ImGuiViewer::updateAnimation(double deltaSeconds) + { + if (sceneModels.size() < 2) + return; + if (animationPaused) + return; + animationTimer += deltaSeconds; + int framesSize = sceneModels.size(); + const int frameStart = (lockFirstAnimationFrame ? 1 : 0); + if (lockFirstAnimationFrame) + framesSize--; + + if (animationTimer > animationFrameDelta) + { + animationFrameId++; + + //set animation time to remainder off of delta + animationTimer -= int(animationTimer/deltaSeconds) * deltaSeconds; + + size_t dataFrameId = animationFrameId%framesSize+frameStart; + if (lockFirstAnimationFrame) + { + ospcommon::affine3f xfm = ospcommon::one; + xfm *= ospcommon::affine3f::translate(translate) + * ospcommon::affine3f::scale(scale); + OSPGeometry dynInst = + ospNewInstance((OSPModel)sceneModels[dataFrameId].object(), + (osp::affine3f&)xfm); + ospray::cpp::Model worldModel = ospNewModel(); + ospcommon::affine3f staticXFM = ospcommon::one; + OSPGeometry staticInst = + ospNewInstance((OSPModel)sceneModels[0].object(), + (osp::affine3f&)staticXFM); + //Carson: TODO: creating new world model every frame unecessary + worldModel.addGeometry(staticInst); + worldModel.addGeometry(dynInst); + renderEngine.scheduleObjectCommit(worldModel); + renderer.set("model", worldModel); + if (rendererDW) + rendererDW.set("model", worldModel); + } + else + { + renderer.set("model", sceneModels[dataFrameId]); + if (rendererDW) + rendererDW.set("model", sceneModels[dataFrameId]); + } + + renderEngine.scheduleObjectCommit(renderer); + if (rendererDW) + renderEngine.scheduleObjectCommit(rendererDW); + } + } + + void ImGuiViewer::buildGui() + { + ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; + + static bool demo_window = false; + + ImGui::Begin("Viewer Controls: press 'g' to show/hide", nullptr, flags); + ImGui::SetWindowFontScale(0.5f*fontScale); + + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("App")) { +#if 0 + ImGui::Checkbox("Show ImGui Demo Window", &demo_window); +#endif + + ImGui::Checkbox("Auto-Rotate", &animating); + + bool paused = renderingPaused; + if (ImGui::Checkbox("Pause Rendering", &paused)) + toggleRenderingPaused(); + + if (ImGui::MenuItem("Take Screenshot")) + saveScreenshot("ospimguiviewer"); + + if (ImGui::MenuItem("Quit")) { + renderEngine.stop(); + std::exit(0); + } + + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("View")) { + bool orbitMode = (manipulator == inspectCenterManipulator); + bool flyMode = (manipulator == moveModeManipulator); + + if (ImGui::Checkbox("Orbit Camera Mode", &orbitMode)) { + manipulator = inspectCenterManipulator; + } + if (ImGui::Checkbox("Fly Camera Mode", &flyMode)) { + manipulator = moveModeManipulator; + } + + if (ImGui::MenuItem("Reset View")) resetView(); + if (ImGui::MenuItem("Reset Accumulation")) viewPort.modified = true; + if (ImGui::MenuItem("Print View")) printViewport(); + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + if (demo_window) ImGui::ShowTestWindow(&demo_window); + + if (ImGui::CollapsingHeader("FPS Statistics", "FPS Statistics", + true, true)) { + ImGui::NewLine(); + ImGui::Text("OSPRay render rate: %.1f FPS", lastFrameFPS); + ImGui::Text(" GUI display rate: %.1f FPS", ImGui::GetIO().Framerate); + ImGui::NewLine(); + } + + if (ImGui::CollapsingHeader("Renderer Parameters")) { + bool renderer_changed = false; + + static int numThreads = -1; + if (ImGui::InputInt("# threads", &numThreads, 1)) { + renderEngine.stop(); + renderEngine.start(numThreads); + renderer_changed = true; + } + + static int ao = 1; + if (ImGui::SliderInt("aoSamples", &ao, 0, 32)) { + renderer.set("aoSamples", ao); + if (rendererDW) + rendererDW.set("aoSamples", ao); + + renderer_changed = true; + } + + if (ImGui::InputFloat("aoDistance", &aoDistance)) { + renderer.set("aoDistance", aoDistance); + if (rendererDW) + rendererDW.set("aoDistance", aoDistance); + renderer_changed = true; + } + + static bool ao_transparency = false; + if (ImGui::Checkbox("ao transparency", &ao_transparency)) { + renderer.set("aoTransparencyEnabled", int(ao_transparency)); + if (rendererDW) + rendererDW.set("aoTransparencyEnabled", int(ao_transparency)); + renderer_changed = true; + } + + static bool shadows = true; + if (ImGui::Checkbox("shadows", &shadows)) { + renderer.set("shadowsEnabled", int(shadows)); + if (rendererDW) + rendererDW.set("shadowsEnabled", int(shadows)); + renderer_changed = true; + } + + static bool singleSidedLighting = true; + if (ImGui::Checkbox("single sided lighting", &singleSidedLighting)) { + renderer.set("oneSidedLighting", int(singleSidedLighting)); + if (rendererDW) + rendererDW.set("oneSidedLighting", int(singleSidedLighting)); + renderer_changed = true; + } + + static int exponent = -6; + if (ImGui::SliderInt("ray epsilon (exponent)", &exponent, -10, 2)) { + renderer.set("epsilon", ospcommon::pow(10.f, (float)exponent)); + if (rendererDW) + rendererDW.set("epsilon", ospcommon::pow(10.f, (float)exponent)); + renderer_changed = true; + } + + static int spp = 1; + if (ImGui::SliderInt("spp", &spp, -4, 16)) { + renderer.set("spp", spp); + if (rendererDW) + rendererDW.set("spp", spp); + renderer_changed = true; + } + + static float varianceThreshold = 0.f; + if (ImGui::InputFloat("variance threshold", &varianceThreshold)) { + renderer.set("varianceThreshold", varianceThreshold); + renderer_changed = true; + } + + static ImVec4 bg_color = ImColor(255, 255, 255); + if (ImGui::ColorEdit3("bg_color", (float*)&bg_color)) { + renderer.set("bgColor", bg_color.x, bg_color.y, bg_color.z); + if (rendererDW) + rendererDW.set("bgColor", bg_color.x, bg_color.y, bg_color.z); + renderer_changed = true; + } + + if (renderer_changed) { + renderEngine.scheduleObjectCommit(renderer); + if (rendererDW) + renderEngine.scheduleObjectCommit(rendererDW); + } + } + + ImGui::End(); + } + +}// namepace ospray diff --git a/apps/common/widgets/OSPGlutViewer.h b/apps/exampleViewer/widgets/imguiViewer.h similarity index 61% rename from apps/common/widgets/OSPGlutViewer.h rename to apps/exampleViewer/widgets/imguiViewer.h index c46b889ad9..ce613fd56b 100644 --- a/apps/common/widgets/OSPGlutViewer.h +++ b/apps/exampleViewer/widgets/imguiViewer.h @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2017 Intel Corporation // +// Copyright 2009-2016 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -19,8 +19,6 @@ #include #include -// viewer widget -#include "common/widgets/glut3D.h" // mini scene graph for loading the model #include "common/miniSG/miniSG.h" @@ -28,7 +26,10 @@ #include #include -#include "common/widgets/Glut3dExport.h" +#include "../common/util/AsyncRenderEngine.h" + +#include "imgui3D.h" +#include "Imgui3dExport.h" #include @@ -37,76 +38,76 @@ namespace ospray { /*! mini scene graph viewer widget. \internal Note that all handling of camera is almost exactly similar to the code in volView; might make sense to move that into a common class! */ - class OSPRAY_GLUT3D_INTERFACE OSPGlutViewer - : public ospray::glut3D::Glut3DWidget + class OSPRAY_IMGUI3D_INTERFACE ImGuiViewer + : public ospray::imgui3D::ImGui3DWidget { public: - OSPGlutViewer(const std::deque &worldBounds, - std::deque model, - cpp::Renderer renderer, - cpp::Camera camera); + ImGuiViewer(const std::deque &worldBounds, + const std::deque &model, + cpp::Renderer renderer, + cpp::Camera camera); + + ImGuiViewer(const std::deque &worldBounds, + const std::deque &model, + cpp::Renderer renderer, + cpp::Renderer rendererDW, + cpp::FrameBuffer frameBufferDW, + cpp::Camera camera); + ~ImGuiViewer(); + + void setRenderer(OSPRenderer renderer, + OSPRenderer rendererDW, + OSPFrameBuffer frameBufferDW); + void setScale(const ospcommon::vec3f& v ) {scale = v;} + void setTranslation(const ospcommon::vec3f& v) {translate = v;} + void setLockFirstAnimationFrame(bool st) {lockFirstAnimationFrame = st;} - void create(const char* title, - const ospcommon::vec2i& size = ospray::glut3D::Glut3DWidget::defaultInitSize, - bool fullScreen = false); + protected: + + virtual void reshape(const ospcommon::vec2i &newSize) override; + virtual void keypress(char key) override; - void setRenderer(OSPRenderer renderer); - void resetAccumulation(); - void toggleFullscreen(); void resetView(); void printViewport(); void saveScreenshot(const std::string &basename); - void setScale(const ospcommon::vec3f& v ) {scale = v;} - void setTranslation(const ospcommon::vec3f& v) {translate = v;} - void setLockFirstAnimationFrame(bool st) {lockFirstAnimationFrame = st;} + void toggleRenderingPaused(); // We override this so we can update the AO ray length void setWorldBounds(const ospcommon::box3f &worldBounds) override; - protected: + void display() override; - virtual void reshape(const ospcommon::vec2i &newSize) override; - virtual void keypress(char key, const ospcommon::vec2i &where) override; - virtual void mouseButton(int32_t whichButton, bool released, - const ospcommon::vec2i &pos) override; virtual void updateAnimation(double deltaSeconds); - void display() override; - - void switchRenderers(); + virtual void buildGui() override; // Data // std::deque sceneModels; std::deque worldBounds; - cpp::FrameBuffer frameBuffer; - cpp::Renderer renderer; - cpp::Camera camera; - - ospray::glut3D::FPSCounter fps; + cpp::Camera camera; + cpp::Renderer renderer; + cpp::Renderer rendererDW; + cpp::FrameBuffer frameBufferDW; - std::mutex rendererMutex; - cpp::Renderer queuedRenderer; - - bool alwaysRedraw; + double lastFrameFPS; ospcommon::vec2i windowSize; - bool fullScreen; - glut3D::Glut3DWidget::ViewPort glutViewPort; - - std::atomic resetAccum; - - std::string windowTitle; + imgui3D::ImGui3DWidget::ViewPort originalView; double frameTimer; - double animationTimer; - double animationFrameDelta; - size_t animationFrameId; - bool animationPaused; - bool lockFirstAnimationFrame; //use for static scene + double animationTimer {0.}; + double animationFrameDelta {0.03}; + size_t animationFrameId {0}; + bool animationPaused {false}; + bool lockFirstAnimationFrame {false}; //use for static scene ospcommon::vec3f translate; - ospcommon::vec3f scale; - int frameID={0}; + ospcommon::vec3f scale {1.f, 1.f, 1.f}; + + float aoDistance {1e20f}; + + AsyncRenderEngine renderEngine; + std::vector pixelBuffer; }; }// namespace ospray diff --git a/apps/exampleViewer/widgets/imguiViewerSg.cpp b/apps/exampleViewer/widgets/imguiViewerSg.cpp new file mode 100644 index 0000000000..d2b0d0eac6 --- /dev/null +++ b/apps/exampleViewer/widgets/imguiViewerSg.cpp @@ -0,0 +1,482 @@ +// ======================================================================== // +// Copyright 2016 SURVICE Engineering Company // +// Copyright 2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "imguiViewerSg.h" +#include "common/sg/common/FrameBuffer.h" +#include "transferFunction.h" + +#include +#include + +using std::string; +using namespace ospcommon; + +// Static local helper functions ////////////////////////////////////////////// + +// helper function to write the rendered image as PPM file +static void writePPM(const string &fileName, const int sizeX, const int sizeY, + const uint32_t *pixel) +{ + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + const unsigned char *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); +} + +// ImGuiViewer definitions //////////////////////////////////////////////////// + +namespace ospray { + + ImGuiViewerSg::ImGuiViewerSg(const std::shared_ptr &scenegraph, + const std::shared_ptr &scenegraphDW + ) + : ImGui3DWidget(ImGui3DWidget::FRAMEBUFFER_NONE), + scenegraph(scenegraph), + scenegraphDW(scenegraphDW), + renderEngine(scenegraph, scenegraphDW) + { + //do initial commit to make sure bounds are correctly computed + scenegraph->traverse("verify"); + scenegraph->traverse("commit"); + auto bbox = scenegraph->child("world").bounds(); + if (bbox.empty()) { + bbox.lower = vec3f(-5,0,-5); + bbox.upper = vec3f(5,10,5); + } + setWorldBounds(bbox); + renderEngine.setFbSize({1024, 768}); + + renderEngine.start(); + + originalView = viewPort; + } + + ImGuiViewerSg::~ImGuiViewerSg() + { + renderEngine.stop(); + } + + void ImGuiViewerSg::reshape(const vec2i &newSize) + { + ImGui3DWidget::reshape(newSize); + windowSize = newSize; + + viewPort.modified = true; + + renderEngine.setFbSize(newSize); + scenegraph->child("frameBuffer")["size"].setValue(newSize); + + pixelBuffer.resize(newSize.x * newSize.y); + } + + void ImGuiViewerSg::keypress(char key) + { + switch (key) { + case 'R': + toggleRenderingPaused(); + break; + case '!': + saveScreenshot("ospimguiviewer"); + break; + case 'X': + if (viewPort.up == vec3f(1,0,0) || viewPort.up == vec3f(-1.f,0,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(1,0,0); + } + viewPort.modified = true; + break; + case 'Y': + if (viewPort.up == vec3f(0,1,0) || viewPort.up == vec3f(0,-1.f,0)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,1,0); + } + viewPort.modified = true; + break; + case 'Z': + if (viewPort.up == vec3f(0,0,1) || viewPort.up == vec3f(0,0,-1.f)) { + viewPort.up = - viewPort.up; + } else { + viewPort.up = vec3f(0,0,1); + } + viewPort.modified = true; + break; + case 'c': + viewPort.modified = true;//Reset accumulation + break; + case 'r': + resetView(); + break; + case 'p': + printViewport(); + break; + case 27 /*ESC*/: + case 'q': + case 'Q': + renderEngine.stop(); + std::exit(0); + break; + default: + ImGui3DWidget::keypress(key); + } + } + + void ImGuiViewerSg::resetView() + { + auto oldAspect = viewPort.aspect; + viewPort = originalView; + viewPort.aspect = oldAspect; + } + + void ImGuiViewerSg::printViewport() + { + printf("-vp %f %f %f -vu %f %f %f -vi %f %f %f\n", + viewPort.from.x, viewPort.from.y, viewPort.from.z, + viewPort.up.x, viewPort.up.y, viewPort.up.z, + viewPort.at.x, viewPort.at.y, viewPort.at.z); + fflush(stdout); + } + + void ImGuiViewerSg::saveScreenshot(const std::string &basename) + { + writePPM(basename + ".ppm", windowSize.x, windowSize.y, pixelBuffer.data()); + std::cout << "saved current frame to '" << basename << ".ppm'" << std::endl; + } + + void ImGuiViewerSg::toggleRenderingPaused() + { + renderingPaused = !renderingPaused; + renderingPaused ? renderEngine.stop() : renderEngine.start(); + } + + void ImGuiViewerSg::display() + { + if (viewPort.modified) { + auto dir = viewPort.at - viewPort.from; + dir = normalize(dir); + auto &camera = scenegraph->child("camera"); + camera["dir"].setValue(dir); + camera["pos"].setValue(viewPort.from); + camera["up"].setValue(viewPort.up); + +#if 1 + if (scenegraphDW.get()) { + auto &camera = scenegraphDW->child("camera"); + camera["dir"].setValue(dir); + camera["pos"].setValue(viewPort.from); + camera["up"].setValue(viewPort.up); + } +#endif + + viewPort.modified = false; + } + + if (renderEngine.hasNewFrame()) { + auto &mappedFB = renderEngine.mapFramebuffer(); + size_t nPixels = windowSize.x * windowSize.y; + + if (mappedFB.size() == nPixels) { + auto *srcPixels = mappedFB.data(); + auto *dstPixels = pixelBuffer.data(); + memcpy(dstPixels, srcPixels, nPixels * sizeof(uint32_t)); + lastFrameFPS = renderEngine.lastFrameFps(); + renderTime = 1.f/lastFrameFPS; + } + + renderEngine.unmapFramebuffer(); + } + + ucharFB = pixelBuffer.data(); + frameBufferMode = ImGui3DWidget::FRAMEBUFFER_UCHAR; + ImGui3DWidget::display(); + + lastTotalTime = ImGui3DWidget::totalTime; + lastGUITime = ImGui3DWidget::guiTime; + lastDisplayTime = ImGui3DWidget::displayTime; + + // that pointer is no longer valid, so set it to null + ucharFB = nullptr; + } + + void ImGuiViewerSg::buildGui() + { + ImGuiWindowFlags flags = ImGuiWindowFlags_MenuBar; + + static bool demo_window = false; + + ImGui::Begin("Viewer Controls: press 'g' to show/hide", nullptr, flags); + ImGui::SetWindowFontScale(0.5f*fontScale); + + if (ImGui::BeginMenuBar()) { + if (ImGui::BeginMenu("App")) { + + ImGui::Checkbox("Auto-Rotate", &animating); + bool paused = renderingPaused; + if (ImGui::Checkbox("Pause Rendering", &paused)) { + toggleRenderingPaused(); + } + if (ImGui::MenuItem("Take Screenshot")) saveScreenshot("ospimguiviewer"); + if (ImGui::MenuItem("Quit")) { + renderEngine.stop(); + std::exit(0); + } + ImGui::EndMenu(); + } + + if (ImGui::BeginMenu("View")) { + bool orbitMode = (manipulator == inspectCenterManipulator); + bool flyMode = (manipulator == moveModeManipulator); + + if (ImGui::Checkbox("Orbit Camera Mode", &orbitMode)) { + manipulator = inspectCenterManipulator; + } + if (ImGui::Checkbox("Fly Camera Mode", &flyMode)) { + manipulator = moveModeManipulator; + } + + if (ImGui::MenuItem("Reset View")) resetView(); + if (ImGui::MenuItem("Reset Accumulation")) viewPort.modified = true; + if (ImGui::MenuItem("Print View")) printViewport(); + + ImGui::EndMenu(); + } + + ImGui::EndMenuBar(); + } + + if (demo_window) ImGui::ShowTestWindow(&demo_window); + + if (ImGui::CollapsingHeader("FPS Statistics", "FPS Statistics", + true, false)) { + ImGui::NewLine(); + ImGui::Text("OSPRay render rate: %.1f FPS", lastFrameFPS); + ImGui::Text(" Total GUI frame rate: %.1f FPS", ImGui::GetIO().Framerate); + ImGui::Text(" Total 3dwidget time: %.1fms ", lastTotalTime*1000.f); + ImGui::Text(" GUI time: %.1fms ", lastGUITime*1000.f); + ImGui::Text(" display pixel time: %.1fms ", lastDisplayTime*1000.f); + ImGui3DWidget::display(); + ImGui::NewLine(); + } + + if (ImGui::CollapsingHeader("SceneGraph", "SceneGraph", true, true)) + buildGUINode("root", scenegraph, 0); + + ImGui::End(); + } + + void ImGuiViewerSg::buildGUINode(std::string name, std::shared_ptr node, int indent) + { + int styles=0; + if (!node->isValid()) { + ImGui::PushStyleColor(ImGuiCol_Text, ImColor(200, 75, 48,255)); + styles++; + } + std::string text; + std::string nameLower=name; + std::transform(nameLower.begin(), nameLower.end(), nameLower.begin(), ::tolower); + std::string nodeNameLower=node->name(); + std::transform(nodeNameLower.begin(), nodeNameLower.end(), nodeNameLower.begin(), ::tolower); + if (nameLower != nodeNameLower) + text += std::string(name+" -> "+node->name()+" : "); + else + text += std::string(name+" : "); + if (node->type() == "vec3f") { + ImGui::Text("%s", text.c_str()); + ImGui::SameLine(); + vec3f val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if ((node->flags() & sg::NodeFlags::gui_color)) { + if (ImGui::ColorEdit3(text.c_str(), (float*)&val.x)) + node->setValue(val); + } + else if ((node->flags() & sg::NodeFlags::gui_slider)) { + if (ImGui::SliderFloat3(text.c_str(), &val.x, + node->min().get().x, + node->max().get().x)) + node->setValue(val); + } + else if (ImGui::DragFloat3(text.c_str(), (float*)&val.x, .01f)) { + node->setValue(val); + } + } else if (node->type() == "vec2f") { + ImGui::Text(text.c_str(),""); + ImGui::SameLine(); + vec2f val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if (ImGui::DragFloat2(text.c_str(), (float*)&val.x, .01f)) { + node->setValue(val); + } + } else if (node->type() == "vec2i") { + ImGui::Text("%s", text.c_str()); + ImGui::SameLine(); + vec2i val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if (ImGui::DragInt2(text.c_str(), (int*)&val.x)) { + node->setValue(val); + } + } else if (node->type() == "float") { + ImGui::Text(text.c_str(),""); + ImGui::SameLine(); + float val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if ((node->flags() & sg::NodeFlags::gui_slider)) { + if (ImGui::SliderFloat(text.c_str(), &val, + node->min().get(), + node->max().get())) + node->setValue(val); + } + else if (ImGui::DragFloat(text.c_str(), &val, .01f)) { + node->setValue(val); + } + } else if (node->type() == "bool") { + ImGui::Text(text.c_str(),""); + ImGui::SameLine(); + bool val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if (ImGui::Checkbox(text.c_str(), &val)) { + node->setValue(val); + } + } else if (node->type() == "int") { + ImGui::Text(text.c_str(),""); + ImGui::SameLine(); + int val = node->valueAs(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if ((node->flags() & sg::NodeFlags::gui_slider)) { + if (ImGui::SliderInt(text.c_str(), &val, + node->min().get(), + node->max().get())) + node->setValue(val); + } + else if (ImGui::DragInt(text.c_str(), &val)) { + node->setValue(val); + } + } else if (node->type() == "string") { + std::string value = node->valueAs().c_str(); + char* buf = (char*)malloc(value.size()+1+256); + strcpy(buf,value.c_str()); + buf[value.size()] = '\0'; + ImGui::Text(text.c_str(),""); + ImGui::SameLine(); + text = "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if (ImGui::InputText(text.c_str(), buf, + value.size()+256, + ImGuiInputTextFlags_EnterReturnsTrue)) + node->setValue(std::string(buf)); + } else { // generic holder node + text+=node->type(); + text += "##"+((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); //TODO: use unique uuid for every node + if (ImGui::TreeNodeEx(text.c_str(), + (indent > 0) ? 0 : ImGuiTreeNodeFlags_DefaultOpen)) { + { + std::string popupName = "Add Node: ##" + + ((std::ostringstream&)(std::ostringstream("") + << node.get())).str(); + static bool addChild = true; + if (ImGui::BeginPopupContextItem("item context menu")) { + char buf[256]; + buf[0]='\0'; + if (ImGui::InputText("node name: ", buf, + 256, ImGuiInputTextFlags_EnterReturnsTrue)) { + std::cout << "add node: \"" << buf << "\"\n"; + try { + static int counter = 0; + std::stringstream ss; + ss << "userDefinedNode" << counter++; + node->add(sg::createNode(ss.str(), buf)); + } + catch (...) + { + std::cerr << "invalid node type: " << buf << std::endl; + } + } + + ImGui::EndPopup(); + } if (addChild) { + if (ImGui::BeginPopup(popupName.c_str())) { + char buf[256]; + buf[0]='\0'; + if (ImGui::InputText("node name: ", buf, 256, + ImGuiInputTextFlags_EnterReturnsTrue)) { + std::cout << "add node: \"" << buf << "\"\n"; + try { + static int counter = 0; + std::stringstream ss; + ss << "userDefinedNode" << counter++; + node->add(sg::createNode(ss.str(), buf)); + } catch (...) { + std::cerr << "invalid node type: " << buf << std::endl; + } + } + ImGui::EndPopup(); + } + else + addChild = false; + } + + if (node->type() == "TransferFunction") { + if (!node->hasChild("transferFunctionWidget")) { + std::shared_ptr tfn = + std::dynamic_pointer_cast(node); + + node->createChildWithValue("transferFunctionWidget","Node", + TransferFunction(tfn)); + } + + auto &tfnWidget = + node->child("transferFunctionWidget").valueAs(); + + tfnWidget.render(); + tfnWidget.drawUi(); + } + } + + if (!node->isValid()) + ImGui::PopStyleColor(styles--); + + for(auto child : node->childrenMap()) + buildGUINode(child.first,child.second, ++indent); + + ImGui::TreePop(); + } + } + + if (!node->isValid()) + ImGui::PopStyleColor(styles--); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("%s", node->documentation().c_str()); + } + +}// namepace ospray diff --git a/apps/exampleViewer/widgets/imguiViewerSg.h b/apps/exampleViewer/widgets/imguiViewerSg.h new file mode 100644 index 0000000000..34a501ff3e --- /dev/null +++ b/apps/exampleViewer/widgets/imguiViewerSg.h @@ -0,0 +1,77 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +#include "../common/util/AsyncRenderEngineSg.h" + +#include "imgui3D.h" +#include "Imgui3dExport.h" + +#include "common/sg/SceneGraph.h" + +#include + +namespace ospray { + + /*! mini scene graph viewer widget. \internal Note that all handling + of camera is almost exactly similar to the code in volView; + might make sense to move that into a common class! */ + class OSPRAY_IMGUI3D_INTERFACE ImGuiViewerSg + : public ospray::imgui3D::ImGui3DWidget + { + public: + + ImGuiViewerSg(const std::shared_ptr &scenegraph, + const std::shared_ptr &scenegraphDW); + ~ImGuiViewerSg(); + + protected: + + void reshape(const ospcommon::vec2i &newSize) override; + void keypress(char key) override; + + void resetView(); + void printViewport(); + void saveScreenshot(const std::string &basename); + void toggleRenderingPaused(); + + void display() override; + + void buildGui() override; + void buildGUINode(std::string name, std::shared_ptr node, int indent); + + // Data // + + double lastFrameFPS; + double lastGUITime; + double lastDisplayTime; + double lastTotalTime; + + ospcommon::vec2i windowSize; + imgui3D::ImGui3DWidget::ViewPort originalView; + + std::shared_ptr scenegraph; + std::shared_ptr scenegraphDW; + + sg::AsyncRenderEngineSg renderEngine; + std::vector pixelBuffer; + }; + +}// namespace ospray diff --git a/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.cpp b/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.cpp new file mode 100644 index 0000000000..524f53d1df --- /dev/null +++ b/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.cpp @@ -0,0 +1,311 @@ +// ImGui GLFW binding with OpenGL3 + shaders +// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). +// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +#include +#include "imgui_impl_glfw_gl3.h" + +// GL3W/GLFW +#include +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WGL +#include +#endif + +// Data +static GLFWwindow* g_Window = NULL; +static double g_Time = 0.0f; +static bool g_MousePressed[3] = { false, false, false }; +static float g_MouseWheel = 0.0f; +static GLuint g_FontTexture = 0; + +// This is the main rendering function that you have to implement and provide to ImGui (via setting up 'RenderDrawListsFn' in the ImGuiIO structure) +// If text or lines are blurry when integrating ImGui in your engine: +// - in your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f) +void ImGui_ImplGlfwGL3_RenderDrawLists(ImDrawData* draw_data) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + ImGuiIO& io = ImGui::GetIO(); + int fb_width = (int)(io.DisplaySize.x * io.DisplayFramebufferScale.x); + int fb_height = (int)(io.DisplaySize.y * io.DisplayFramebufferScale.y); + if (fb_width == 0 || fb_height == 0) + return; + draw_data->ScaleClipRects(io.DisplayFramebufferScale); + + // We are using the OpenGL fixed pipeline to make the example code simpler to read! + // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers. + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glEnable(GL_SCISSOR_TEST); + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnable(GL_TEXTURE_2D); + //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context + + // Setup viewport, orthographic projection matrix + glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + glMatrixMode(GL_PROJECTION); + glPushMatrix(); + glLoadIdentity(); + glOrtho(0.0f, io.DisplaySize.x, io.DisplaySize.y, 0.0f, -1.0f, +1.0f); + glMatrixMode(GL_MODELVIEW); + glPushMatrix(); + glLoadIdentity(); + + // Render command lists + #define OFFSETOF(TYPE, ELEMENT) ((size_t)&(((TYPE *)0)->ELEMENT)) + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; + const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; + glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, pos))); + glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, uv))); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + OFFSETOF(ImDrawVert, col))); + + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback) + { + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); + glScissor((int)pcmd->ClipRect.x, (int)(fb_height - pcmd->ClipRect.w), (int)(pcmd->ClipRect.z - pcmd->ClipRect.x), (int)(pcmd->ClipRect.w - pcmd->ClipRect.y)); + glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); + } + idx_buffer += pcmd->ElemCount; + } + } + #undef OFFSETOF + + // Restore modified state + glDisableClientState(GL_COLOR_ARRAY); + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glDisableClientState(GL_VERTEX_ARRAY); + glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); + glMatrixMode(GL_MODELVIEW); + glPopMatrix(); + glMatrixMode(GL_PROJECTION); + glPopMatrix(); + glPopAttrib(); + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], +(GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); +} + +static const char* ImGui_ImplGlfwGL3_GetClipboardText(void* user_data) +{ + return glfwGetClipboardString((GLFWwindow*)user_data); +} + +static void ImGui_ImplGlfwGL3_SetClipboardText(void* user_data, const char* text) +{ + glfwSetClipboardString((GLFWwindow*)user_data, text); +} + +void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) +{ + if (action == GLFW_PRESS && button >= 0 && button < 3) + g_MousePressed[button] = true; +} + +void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow*, double /*xoffset*/, double yoffset) +{ + g_MouseWheel += (float)yoffset; // Use fractional mouse wheel, 1.0 unit 5 lines. +} + +void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow*, int key, int, int action, int mods) +{ + ImGuiIO& io = ImGui::GetIO(); + if (action == GLFW_PRESS) + io.KeysDown[key] = true; + if (action == GLFW_RELEASE) + io.KeysDown[key] = false; + + (void)mods; // Modifiers are not reliable across systems + io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; + io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; + io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; + io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; +} + +bool ImGui_ImplGlfwGL3_CreateFontsTexture() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +bool ImGui_ImplGlfwGL3_CreateDeviceObjects() +{ + // Build texture atlas + ImGuiIO& io = ImGui::GetIO(); + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. + + // Upload texture to graphics system + GLint last_texture; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); + glGenTextures(1, &g_FontTexture); + glBindTexture(GL_TEXTURE_2D, g_FontTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + + // Store our identifier + io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; + + // Restore state + glBindTexture(GL_TEXTURE_2D, last_texture); + + return true; +} + +void ImGui_ImplGlfwGL3_InvalidateDeviceObjects() +{ + if (g_FontTexture) + { + glDeleteTextures(1, &g_FontTexture); + ImGui::GetIO().Fonts->TexID = 0; + g_FontTexture = 0; + } +} + +bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks) +{ + g_Window = window; + + ImGuiIO& io = ImGui::GetIO(); + // Keyboard mapping. ImGui will use those indices to peek into the io.KeyDown[] array. + io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; + io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; + io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; + io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; + io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; + io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; + io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; + io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; + io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; + io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; + io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; + io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; + io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; + io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; + io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; + io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; + io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; + io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; + io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; + + // Alternatively you can set this to NULL and call ImGui::GetDrawData() + // after ImGui::Render() to get the same ImDrawData pointer. + io.RenderDrawListsFn = ImGui_ImplGlfwGL3_RenderDrawLists; + io.SetClipboardTextFn = ImGui_ImplGlfwGL3_SetClipboardText; + io.GetClipboardTextFn = ImGui_ImplGlfwGL3_GetClipboardText; + io.ClipboardUserData = g_Window; +#ifdef _WIN32 + io.ImeWindowHandle = glfwGetWin32Window(g_Window); +#endif + + if (install_callbacks) + { + glfwSetMouseButtonCallback(window, ImGui_ImplGlfwGL3_MouseButtonCallback); + glfwSetScrollCallback(window, ImGui_ImplGlfwGL3_ScrollCallback); + glfwSetKeyCallback(window, ImGui_ImplGlfwGL3_KeyCallback); + } + + return true; +} + +void ImGui_ImplGlfwGL3_Shutdown() +{ + ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); + ImGui::Shutdown(); +} + +void ImGui_ImplGlfwGL3_NewFrame() +{ + if (!g_FontTexture) + ImGui_ImplGlfwGL3_CreateDeviceObjects(); + + ImGuiIO& io = ImGui::GetIO(); + + // Setup display size (every frame to accommodate for window resizing) + int w, h; + int display_w, display_h; + glfwGetWindowSize(g_Window, &w, &h); + glfwGetFramebufferSize(g_Window, &display_w, &display_h); + io.DisplaySize = ImVec2((float)w, (float)h); + io.DisplayFramebufferScale = + ImVec2(w > 0 ? ((float)display_w / w) : + 0, h > 0 ? ((float)display_h / h) : 0); + + // Setup time step + double current_time = glfwGetTime(); + io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); + g_Time = current_time; + + // Setup inputs + // (we already got mouse wheel, keyboard keys & characters from glfw callbacks polled in glfwPollEvents()) + if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) + { + double mouse_x, mouse_y; + glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); + io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); // Mouse position in screen coordinates (set to -1,-1 if no mouse / on another screen, etc.) + } + else + { + io.MousePos = ImVec2(-1,-1); + } + + for (int i = 0; i < 3; i++) + { + io.MouseDown[i] = g_MousePressed[i] || glfwGetMouseButton(g_Window, i) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. + g_MousePressed[i] = false; + } + + io.MouseWheel = g_MouseWheel; + g_MouseWheel = 0.0f; + + // Hide OS mouse cursor if ImGui is drawing it + glfwSetInputMode(g_Window, GLFW_CURSOR, io.MouseDrawCursor ? GLFW_CURSOR_HIDDEN : GLFW_CURSOR_NORMAL); + + // Start the frame + ImGui::NewFrame(); +} diff --git a/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.h b/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.h new file mode 100644 index 0000000000..e0dee112ab --- /dev/null +++ b/apps/exampleViewer/widgets/imgui_impl_glfw_gl3.h @@ -0,0 +1,28 @@ +#include "Imgui3dExport.h" + +// ImGui GLFW binding with OpenGL3 + shaders +// In this binding, ImTextureID is used to store an OpenGL 'GLuint' texture identifier. Read the FAQ about ImTextureID in imgui.cpp. + +// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. +// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). +// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. +// https://github.com/ocornut/imgui + +class GLFWwindow; + +bool ImGui_ImplGlfwGL3_Init(GLFWwindow* window, bool install_callbacks); +void ImGui_ImplGlfwGL3_Shutdown(); +void ImGui_ImplGlfwGL3_NewFrame(); + +// Use if you want to reset your rendering device without losing ImGui state. +void ImGui_ImplGlfwGL3_InvalidateDeviceObjects(); +bool ImGui_ImplGlfwGL3_CreateDeviceObjects(); + +// GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) +// Provided here if you want to chain callbacks. +// You can also handle inputs yourself and use those as a reference. +void ImGui_ImplGlfwGL3_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); +void ImGui_ImplGlfwGL3_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); +void ImGui_ImplGlfwGL3_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); +void ImGui_ImplGlfwGL3_CharCallback(GLFWwindow* window, unsigned int c); + diff --git a/apps/exampleViewer/widgets/transferFunction.cpp b/apps/exampleViewer/widgets/transferFunction.cpp new file mode 100644 index 0000000000..dc158c1e3e --- /dev/null +++ b/apps/exampleViewer/widgets/transferFunction.cpp @@ -0,0 +1,431 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "transferFunction.h" + +using namespace ospcommon; + +static float lerp(const float a, const float b, const float t) +{ + return (1.0 - t) * a + t * b; +} + +namespace ospray { + +TransferFunction::Line::Line() : + line({vec2f(0, 0), + vec2f(1, 1)}), + color(0xffffffff) +{ +} + +void TransferFunction::Line::movePoint(const float &startX, const vec2f &end) +{ + // Find the closest point to where the user clicked + auto fnd = std::min_element(line.begin(), line.end(), + [&startX](const vec2f &a, const vec2f &b){ + return std::abs(startX - a.x) < std::abs(startX - b.x); + }); + // If there's no nearby point we need to insert a new one + // TODO: How much fudge to allow for here? + if (std::abs(startX - fnd->x) >= 0.01){ + std::vector::iterator split = line.begin(); + for (; split != line.end(); ++split){ + if (split->x < startX && startX < (split + 1)->x){ + break; + } + } + assert(split != line.end()); + line.insert(split + 1, end); + } else { + *fnd = end; + // Keep the start and end points clamped to the left/right side + if (fnd == line.begin()){ + fnd->x = 0; + } else if (fnd == line.end() - 1){ + fnd->x = 1; + } else { + // If it's a point in the middle keep it from going past its neighbors + fnd->x = clamp(fnd->x, (fnd - 1)->x, (fnd + 1)->x); + } + } +} +void TransferFunction::Line::removePoint(const float &x) +{ + if (line.size() == 2){ + return; + } + // See if we have a segment starting near that point + auto fnd = std::min_element(line.begin(), line.end(), + [&x](const vec2f &a, const vec2f &b){ + return std::abs(x - a.x) < std::abs(x - b.x); + }); + // Don't allow erasure of the start and end points of the line + if (fnd != line.end() && fnd + 1 != line.end() && fnd != line.begin()){ + line.erase(fnd); + } +} + +TransferFunction::TransferFunction(std::shared_ptr &tfn) : + transferFcn(tfn), + activeLine(3), + tfcnSelection(JET), + customizing(false), + fcnChanged(true), + paletteTex(0), + textBuffer(512, '\0') +{ + // TODO: Use the transfer function passed to use to configure the initial widget lines + rgbaLines[0].color = 0xff0000ff; + rgbaLines[1].color = 0xff00ff00; + rgbaLines[2].color = 0xffff0000; + rgbaLines[3].color = 0xffffffff; + loadColorMapPresets(); + setColorMap(false); +} +TransferFunction::~TransferFunction() +{ + if (paletteTex){ + glDeleteTextures(1, &paletteTex); + } +} +TransferFunction::TransferFunction(const TransferFunction &t) : + transferFcn(t.transferFcn), + rgbaLines(t.rgbaLines), + activeLine(t.activeLine), + tfcnSelection(t.tfcnSelection), + customizing(t.customizing), + transferFunctions(t.transferFunctions), + fcnChanged(true), + paletteTex(0), + textBuffer(512, '\0') +{ + setColorMap(false); +} +TransferFunction& TransferFunction::operator=(const TransferFunction &t) +{ + if (this == &t) { + return *this; + } + transferFcn = t.transferFcn; + rgbaLines = t.rgbaLines; + activeLine = t.activeLine; + tfcnSelection = t.tfcnSelection; + customizing = t.customizing; + transferFunctions = t.transferFunctions; + fcnChanged = true; + setColorMap(false); + return *this; +} + +void TransferFunction::drawUi() +{ + if (ImGui::Begin("Transfer Function")){ + ImGui::Text("Left click and drag to add/move points\nRight click to remove\n"); + ImGui::InputText("filename", textBuffer.data(), textBuffer.size() - 1); + + if (ImGui::Button("Save")){ + save(textBuffer.data()); + } + ImGui::SameLine(); + if (ImGui::Button("Load")){ + load(textBuffer.data()); + } + + std::vector colorMaps(transferFunctions.size(), nullptr); + std::transform(transferFunctions.begin(), transferFunctions.end(), colorMaps.begin(), + [](const tfn::TransferFunction &t) { return t.name.c_str(); }); + if (ImGui::Combo("ColorMap", &tfcnSelection, colorMaps.data(), colorMaps.size())) { + setColorMap(false); + } + + ImGui::Checkbox("Customize", &customizing); + if (customizing) { + ImGui::SameLine(); + ImGui::RadioButton("Red", &activeLine, 0); ImGui::SameLine(); + ImGui::SameLine(); + ImGui::RadioButton("Green", &activeLine, 1); ImGui::SameLine(); + ImGui::SameLine(); + ImGui::RadioButton("Blue", &activeLine, 2); ImGui::SameLine(); + ImGui::SameLine(); + ImGui::RadioButton("Alpha", &activeLine, 3); + } else { + activeLine = 3; + } + + vec2f canvasPos(ImGui::GetCursorScreenPos().x, ImGui::GetCursorScreenPos().y); + vec2f canvasSize(ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y); + // Force some min size of the editor + if (canvasSize.x < 50.f){ + canvasSize.x = 50.f; + } + if (canvasSize.y < 50.f){ + canvasSize.y = 50.f; + } + + if (paletteTex){ + ImGui::Image(reinterpret_cast(paletteTex), ImVec2(canvasSize.x, 16)); + canvasPos.y += 20; + canvasSize.y -= 20; + } + + ImDrawList *draw_list = ImGui::GetWindowDrawList(); + draw_list->AddRect(canvasPos, canvasPos + canvasSize, ImColor(255, 255, 255)); + + const vec2f viewScale(canvasSize.x, -canvasSize.y + 10); + const vec2f viewOffset(canvasPos.x, canvasPos.y + canvasSize.y - 10); + + ImGui::InvisibleButton("canvas", canvasSize); + if (ImGui::IsItemHovered()){ + vec2f mousePos = vec2f(ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); + mousePos = (mousePos - viewOffset) / viewScale; + // Need to somehow find which line of RGBA the mouse is closest too + if (ImGui::GetIO().MouseDown[0]){ + rgbaLines[activeLine].movePoint(mousePos.x, mousePos); + fcnChanged = true; + } else if (ImGui::IsMouseClicked(1)){ + rgbaLines[activeLine].removePoint(mousePos.x); + fcnChanged = true; + } + } + draw_list->PushClipRect(canvasPos, canvasPos + canvasSize); + + if (customizing) { + for (int i = 0; i < static_cast(rgbaLines.size()); ++i){ + if (i == activeLine){ + continue; + } + for (size_t j = 0; j < rgbaLines[i].line.size() - 1; ++j){ + const vec2f &a = rgbaLines[i].line[j]; + const vec2f &b = rgbaLines[i].line[j + 1]; + draw_list->AddLine(viewOffset + viewScale * a, viewOffset + viewScale * b, + rgbaLines[i].color, 2.0f); + } + } + } + // Draw the active line on top + for (size_t j = 0; j < rgbaLines[activeLine].line.size() - 1; ++j){ + const vec2f &a = rgbaLines[activeLine].line[j]; + const vec2f &b = rgbaLines[activeLine].line[j + 1]; + draw_list->AddLine(viewOffset + viewScale * a, viewOffset + viewScale * b, + rgbaLines[activeLine].color, 2.0f); + draw_list->AddCircleFilled(viewOffset + viewScale * a, 4.f, rgbaLines[activeLine].color); + // If we're last draw the list Circle as well + if (j == rgbaLines[activeLine].line.size() - 2) { + draw_list->AddCircleFilled(viewOffset + viewScale * b, 4.f, rgbaLines[activeLine].color); + } + } + draw_list->PopClipRect(); + } + ImGui::End(); +} + +void TransferFunction::render() +{ + // TODO: How many samples for a palette? 128 or 256 is probably plent + const int samples = 256; + // Upload to GL if the transfer function has changed + if (!paletteTex){ + GLint prevBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevBinding); + glGenTextures(1, &paletteTex); + + glBindTexture(GL_TEXTURE_2D, paletteTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, samples, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + if (prevBinding) { + glBindTexture(GL_TEXTURE_2D, prevBinding); + } + } + if (fcnChanged){ + GLint prevBinding = 0; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &prevBinding); + + // Sample the palette then upload the data + std::vector palette(samples * 4, 0); + std::vector ospColors(samples, vec3f(0)); + std::vector ospAlpha(samples, vec2f(0)); + // Step along the alpha line and sample it + std::array::const_iterator, 4> lit = { + rgbaLines[0].line.begin(), rgbaLines[1].line.begin(), + rgbaLines[2].line.begin(), rgbaLines[3].line.begin() + }; + const float step = 1.0 / samples; + + for (size_t i = 0; i < samples; ++i){ + const float x = step * i; + std::array sampleColor; + for (size_t j = 0; j < 4; ++j){ + if (x > (lit[j] + 1)->x) { + ++lit[j]; + } + assert(lit[j] != rgbaLines[j].line.end()); + const float t = (x - lit[j]->x) / ((lit[j] + 1)->x - lit[j]->x); + // It's hard to click down at exactly 0, so offset a little bit + sampleColor[j] = clamp(lerp(lit[j]->y - 0.001, (lit[j] + 1)->y - 0.001, t)); + } + for (size_t j = 0; j < 3; ++j) { + palette[i * 4 + j] = clamp(sampleColor[j] * 255.0, 0.0, 255.0); + ospColors[i][j] = sampleColor[j]; + } + ospAlpha[i].x = x; + ospAlpha[i].y = sampleColor[3]; + palette[i * 4 + 3] = 255; + } + glBindTexture(GL_TEXTURE_2D, paletteTex); + glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, samples, 1, GL_RGBA, GL_UNSIGNED_BYTE, + static_cast(palette.data())); + + transferFcn->setColorMap(ospColors); + transferFcn->setAlphaMap(ospAlpha); + + if (prevBinding) { + glBindTexture(GL_TEXTURE_2D, prevBinding); + } + fcnChanged = false; + } +} +void TransferFunction::load(const ospcommon::FileName &fileName) +{ + tfn::TransferFunction loaded(fileName); + transferFunctions.emplace_back(fileName); + tfcnSelection = transferFunctions.size() - 1; + setColorMap(true); +} +void TransferFunction::save(const ospcommon::FileName &fileName) const +{ + // For opacity we can store the associated data value and only have 1 line, + // so just save it out directly + tfn::TransferFunction output(transferFunctions[tfcnSelection].name, + std::vector(), rgbaLines[3].line, 0, 1, 1); + + // Pull the RGB line values to compute the transfer function and save it out + // here we may need to do some interpolation, if the RGB lines have differing numbers + // of control points + // Find which x values we need to sample to get all the control points for the tfcn. + std::vector controlPoints; + for (size_t i = 0; i < 3; ++i) { + for (const auto &x : rgbaLines[i].line) { + controlPoints.push_back(x.x); + } + } + // Filter out same or within epsilon control points to get unique list + std::sort(controlPoints.begin(), controlPoints.end()); + auto uniqueEnd = std::unique(controlPoints.begin(), controlPoints.end(), + [](const float &a, const float &b) { return std::abs(a - b) < 0.0001; }); + controlPoints.erase(uniqueEnd, controlPoints.end()); + + // Step along the lines and sample them + std::array::const_iterator, 3> lit = { + rgbaLines[0].line.begin(), rgbaLines[1].line.begin(), + rgbaLines[2].line.begin() + }; + for (const auto &x : controlPoints) { + std::array sampleColor; + for (size_t j = 0; j < 3; ++j){ + if (x > (lit[j] + 1)->x) { + ++lit[j]; + } + assert(lit[j] != rgbaLines[j].line.end()); + const float t = (x - lit[j]->x) / ((lit[j] + 1)->x - lit[j]->x); + // It's hard to click down at exactly 0, so offset a little bit + sampleColor[j] = clamp(lerp(lit[j]->y - 0.001, (lit[j] + 1)->y - 0.001, t)); + } + output.rgbValues.push_back(vec3f(sampleColor[0], sampleColor[1], sampleColor[2])); + } + + output.save(fileName); +} +void TransferFunction::setColorMap(const bool useOpacity) +{ + const std::vector &colors = transferFunctions[tfcnSelection].rgbValues; + const std::vector &opacities = transferFunctions[tfcnSelection].opacityValues; + for (size_t i = 0; i < 3; ++i) { + rgbaLines[i].line.clear(); + } + const float nColors = static_cast(colors.size()) - 1; + for (size_t i = 0; i < colors.size(); ++i) { + for (size_t j = 0; j < 3; ++j) { + rgbaLines[j].line.push_back(vec2f(i / nColors, colors[i][j])); + } + } + + if (useOpacity && !opacities.empty()) { + rgbaLines[3].line.clear(); + const double valMin = transferFunctions[tfcnSelection].dataValueMin; + const double valMax = transferFunctions[tfcnSelection].dataValueMax; + // Setup opacity if the colormap has it + for (size_t i = 0; i < opacities.size(); ++i) { + const float x = (opacities[i].x - valMin) / (valMax - valMin); + rgbaLines[3].line.push_back(vec2f(x, opacities[i].y)); + } + } + fcnChanged = true; +} +void TransferFunction::loadColorMapPresets() +{ + std::vector colors; + // The presets have no existing opacity value + const std::vector opacity; + // From the old volume viewer, these are based on ParaView + // Jet transfer function + colors.push_back(vec3f(0 , 0, 0.562493)); + colors.push_back(vec3f(0 , 0, 1 )); + colors.push_back(vec3f(0 , 1, 1 )); + colors.push_back(vec3f(0.500008, 1, 0.500008)); + colors.push_back(vec3f(1 , 1, 0 )); + colors.push_back(vec3f(1 , 0, 0 )); + colors.push_back(vec3f(0.500008, 0, 0 )); + transferFunctions.emplace_back("Jet", colors, opacity, 0, 1, 1); + colors.clear(); + + colors.push_back(vec3f(0 , 0 , 0 )); + colors.push_back(vec3f(0 , 0.120394 , 0.302678 )); + colors.push_back(vec3f(0 , 0.216587 , 0.524575 )); + colors.push_back(vec3f(0.0552529, 0.345022 , 0.659495 )); + colors.push_back(vec3f(0.128054 , 0.492592 , 0.720287 )); + colors.push_back(vec3f(0.188952 , 0.641306 , 0.792096 )); + colors.push_back(vec3f(0.327672 , 0.784939 , 0.873426 )); + colors.push_back(vec3f(0.60824 , 0.892164 , 0.935546 )); + colors.push_back(vec3f(0.881376 , 0.912184 , 0.818097 )); + colors.push_back(vec3f(0.9514 , 0.835615 , 0.449271 )); + colors.push_back(vec3f(0.904479 , 0.690486 , 0 )); + colors.push_back(vec3f(0.854063 , 0.510857 , 0 )); + colors.push_back(vec3f(0.777096 , 0.330175 , 0.000885023)); + colors.push_back(vec3f(0.672862 , 0.139086 , 0.00270085 )); + colors.push_back(vec3f(0.508812 , 0 , 0 )); + colors.push_back(vec3f(0.299413 , 0.000366217, 0.000549325)); + colors.push_back(vec3f(0.0157473, 0.00332647 , 0 )); + transferFunctions.emplace_back("Ice Fire", colors, opacity, 0, 1, 1); + colors.clear(); + + colors.push_back(vec3f(0.231373, 0.298039 , 0.752941)); + colors.push_back(vec3f(0.865003, 0.865003 , 0.865003)); + colors.push_back(vec3f(0.705882, 0.0156863, 0.14902)); + transferFunctions.emplace_back("Cool Warm", colors, opacity, 0, 1, 1); + colors.clear(); + + colors.push_back(vec3f(0, 0, 1)); + colors.push_back(vec3f(1, 0, 0)); + transferFunctions.emplace_back("Blue Red", colors, opacity, 0, 1, 1); + colors.clear(); + + colors.push_back(vec3f(0)); + colors.push_back(vec3f(1)); + transferFunctions.emplace_back("Grayscale", colors, opacity, 0, 1, 1); + colors.clear(); +} + +}// ::ospray + diff --git a/apps/exampleViewer/widgets/transferFunction.h b/apps/exampleViewer/widgets/transferFunction.h new file mode 100644 index 0000000000..186c9dda12 --- /dev/null +++ b/apps/exampleViewer/widgets/transferFunction.h @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include +#include +#include +#ifdef _WIN32 +#undef APIENTRY +#define GLFW_EXPOSE_NATIVE_WIN32 +#define GLFW_EXPOSE_NATIVE_WGL +#include +#endif + +#include +#include "Imgui3dExport.h" +#include +#include "common/sg/transferFunction/TransferFunction.h" + +namespace ospray { + + class OSPRAY_IMGUI3D_INTERFACE TransferFunction + { + public: + + TransferFunction(std::shared_ptr &tfn); + ~TransferFunction(); + TransferFunction(const TransferFunction &t); + TransferFunction& operator=(const TransferFunction &t); + /* Draw the transfer function editor widget + */ + void drawUi(); + /* Render the transfer function to a 1D texture that can + * be applied to volume data + */ + void render(); + // Load the transfer function in the file passed and set it active + void load(const ospcommon::FileName &fileName); + // Save the current transfer function out to the file + void save(const ospcommon::FileName &fileName) const; + + struct Line + { + // A line is made up of points sorted by x, its coordinates are + // on the range [0, 1] + std::vector line; + int color; + + // TODO: Constructor that takes an existing line + // Construct a new diagonal line: [(0, 0), (1, 1)] + Line(); + /* Move a point on the line from start to end, if the line is + * not split at 'start' it will be split then moved + * TODO: Should we have some cap on the number of points? We should + * also track if you're actively dragging a point so we don't recreate + * points if you move the mouse too fast + */ + void movePoint(const float &startX, const ospcommon::vec2f &end); + // Remove a point from the line, merging the two segments on either side + void removePoint(const float &x); + }; + + private: + + // The indices of the transfer function color presets available + enum ColorMap { + JET, + ICE_FIRE, + COOL_WARM, + BLUE_RED, + GRAYSCALE, + }; + + // The scenegraph transfer function being manipulated by this widget + std::shared_ptr transferFcn; + // Lines for RGBA transfer function controls + std::array rgbaLines; + // The line currently being edited + int activeLine; + // The selected transfer function being shown + int tfcnSelection; + // If we're customizing the transfer function's RGB values + bool customizing; + // The list of avaliable transfer functions, both built-in and loaded + std::vector transferFunctions; + // The filename input text buffer + std::vector textBuffer; + + // Track if the function changed and must be re-uploaded. + // We start by marking it changed to upload the initial palette + bool fcnChanged; + // The 2d palette texture on the GPU for displaying the color map in the UI. + GLuint paletteTex; + + // Select the provided color map specified by tfcnSelection. useOpacity + // indicates if the transfer function's opacity values should be used if available + // overwriting the user's set opacity data. This is done when loading from a file + // to show the loaded tfcn, but not when switching from the preset picker. + void setColorMap(const bool useOpacity); + // Load up the preset color maps + void loadColorMapPresets(); + }; + +}// ::ospray + diff --git a/apps/glutViewer/GlutViewerScriptHandler.cpp b/apps/glutViewer/GlutViewerScriptHandler.cpp deleted file mode 100644 index 5a49dfa1e2..0000000000 --- a/apps/glutViewer/GlutViewerScriptHandler.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "GlutViewerScriptHandler.h" -#include "ScriptedOSPGlutViewer.h" - -#include -using std::endl; - -namespace ospray { - - GlutViewerScriptHandler::GlutViewerScriptHandler(OSPModel model, - OSPRenderer renderer, - OSPCamera camera, - ScriptedOSPGlutViewer *viewer) - : OSPRayScriptHandler(model, renderer, camera), - viewer(viewer) - { - registerScriptFunctions(); - - std::stringstream ss; - - ss << "Viewer functions available:" << endl << endl; - ss << "setRenderer(renderer) --> set the renderer in the viewer" << endl; - ss << "refresh() --> reset the accumulation buffer" << endl; - ss << "toggleFullScreen() --> toggle fullscreen mode" << endl; - ss << "resetView() --> reset camera view" << endl; - ss << "printViewport() --> print view params in the console" << endl; - ss << "renderFrame(n_frames) --> release the script lock and render 'n_frames' frames,\n" - << " then return to running the script." << endl; - ss << "setWorldBounds(bbox) --> set the world bounds to the specified box3f,\n" - << " repositioning the camera." << endl; - ss << "screenshot(filename) --> save a screenshot (adds '.ppm')" << endl; - - helpText += ss.str(); - } - - void GlutViewerScriptHandler::registerScriptFunctions() - { - auto &chai = this->scriptEngine(); - - // setRenderer() - auto setRenderer = [&](ospray::cpp::Renderer &r) { - viewer->setRenderer((OSPRenderer)r.handle()); - }; - - // refresh() - auto refresh = [&]() { - viewer->resetAccumulation(); - }; - - // toggleFullscreen() - auto toggleFullscreen = [&]() { - viewer->toggleFullscreen(); - }; - - // resetView() - auto resetView = [&]() { - viewer->resetView(); - }; - - // printViewport() - auto printViewport = [&]() { - viewer->printViewport(); - }; - - // renderFrame() - auto renderFrame = [&](const int n_frames) { - // Temporarily unlock the mutex and wait for the display - // loop to acquire it and render and wait til a n frames have finished - const int startFrame = viewer->getFrameID(); - lock.unlock(); - // Wait for n_frames to be rendered - while (startFrame + n_frames > viewer->getFrameID()); - lock.lock(); - }; - auto renderOneFrame = [&]() { - // Temporarily unlock the mutex and wait for the display - // loop to acquire it and render and wait til a n frames have finished - const int startFrame = viewer->getFrameID(); - lock.unlock(); - // Wait for n_frames to be rendered - while (startFrame + 1 > viewer->getFrameID()); - lock.lock(); - }; - - // setWorldBounds - auto setWorldBounds = [&](const ospcommon::box3f &box) { - viewer->setWorldBounds(box); - }; - - // screenshot() - auto screenshot = [&](const std::string &name) { - viewer->saveScreenshot(name); - }; - - chai.add(chaiscript::fun(setRenderer), "setRenderer" ); - chai.add(chaiscript::fun(refresh), "refresh" ); - chai.add(chaiscript::fun(toggleFullscreen), "toggleFullscreen"); - chai.add(chaiscript::fun(resetView), "resetView" ); - chai.add(chaiscript::fun(printViewport), "printViewport" ); - chai.add(chaiscript::fun(renderFrame), "renderFrame" ); - chai.add(chaiscript::fun(renderOneFrame), "renderFrame" ); - chai.add(chaiscript::fun(setWorldBounds), "setWorldBounds" ); - chai.add(chaiscript::fun(screenshot), "screenshot" ); - } - -}// namespace ospray diff --git a/apps/glutViewer/ScriptedOSPGlutViewer.cpp b/apps/glutViewer/ScriptedOSPGlutViewer.cpp deleted file mode 100644 index 93e207c529..0000000000 --- a/apps/glutViewer/ScriptedOSPGlutViewer.cpp +++ /dev/null @@ -1,137 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "ScriptedOSPGlutViewer.h" - -// MSGViewer definitions ////////////////////////////////////////////////////// - -namespace ospray { - - using std::cout; - using std::endl; - using std::string; - using std::lock_guard; - using std::mutex; - using namespace ospcommon; - - ScriptedOSPGlutViewer::ScriptedOSPGlutViewer(const std::deque &worldBounds, - std::deque model, - cpp::Renderer renderer, - cpp::Camera camera, - std::string scriptFileName) - : OSPGlutViewer(worldBounds, model, renderer, camera), - scriptHandler(model[0].handle(), renderer.handle(), camera.handle(), this), - frameID(0) - { - if (!scriptFileName.empty()) - scriptHandler.runScriptFromFile(scriptFileName); - glutViewPort = viewPort; - } - - int ScriptedOSPGlutViewer::getFrameID() const { - return frameID.load(); - } - - void ScriptedOSPGlutViewer::display() { - if (!frameBuffer.handle() || !renderer.handle()) return; - - // We need to synchronize with the scripting engine so we don't - // get our scene data trampled on if scripting is running. - std::lock_guard lock(scriptHandler.scriptMutex); - - //{ - // note that the order of 'start' and 'end' here is - // (intentionally) reversed: due to our asynchrounous rendering - // you cannot place start() and end() _around_ the renderframe - // call (which in itself will not do a lot other than triggering - // work), but the average time between the two calls is roughly the - // frame rate (including display overhead, of course) - if (frameID.load() > 0) fps.doneRender(); - - // NOTE: consume a new renderer if one has been queued by another thread - switchRenderers(); - - updateAnimation(ospcommon::getSysTime()-frameTimer); - frameTimer = ospcommon::getSysTime(); - - if (resetAccum) { - frameBuffer.clear(OSP_FB_ACCUM); - resetAccum = false; - } - - ++frameID; - - if (viewPort.modified) { - Assert2(camera.handle(),"ospray camera is null"); - camera.set("pos", viewPort.from); - auto dir = viewPort.at - viewPort.from; - camera.set("dir", dir); - camera.set("up", viewPort.up); - camera.set("aspect", viewPort.aspect); - camera.set("fovy", viewPort.openingAngle); - camera.commit(); - viewPort.modified = false; - frameBuffer.clear(OSP_FB_ACCUM); - } - fps.startRender(); - renderer.renderFrame(frameBuffer, OSP_FB_COLOR | OSP_FB_ACCUM); - - // set the glut3d widget's frame buffer to the opsray frame buffer, - // then display - ucharFB = (uint32_t *)frameBuffer.map(OSP_FB_COLOR); - frameBufferMode = Glut3DWidget::FRAMEBUFFER_UCHAR; - Glut3DWidget::display(); - - frameBuffer.unmap(ucharFB); - - // that pointer is no longer valid, so set it to null - ucharFB = nullptr; - - std::string title("OSPRay GLUT Viewer"); - - if (alwaysRedraw) { - title += " (" + std::to_string((long double)fps.getFPS()) + " fps)"; - setTitle(title); - forceRedraw(); - } else { - setTitle(title); - } - } - - void ScriptedOSPGlutViewer::keypress(char key, const vec2i &where) - { - switch (key) { - case ':': - if (!scriptHandler.running()) { - scriptHandler.start(); - } - break; - case 27 /*ESC*/: - case 'q': - case 'Q': - if (scriptHandler.running()) { - std::cout << "Please exit command mode before quitting" - << " to avoid messing up your terminal\n"; - break; - } else { - std::exit(0); - } - default: - OSPGlutViewer::keypress(key,where); - } - } - -}// namepace ospray diff --git a/apps/glutViewer/glutViewer.cpp b/apps/glutViewer/glutViewer.cpp deleted file mode 100644 index 5b717bb6af..0000000000 --- a/apps/glutViewer/glutViewer.cpp +++ /dev/null @@ -1,119 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include -#include "common/commandline/Utility.h" - -#ifdef OSPRAY_APPS_ENABLE_SCRIPTING -# include "ScriptedOSPGlutViewer.h" -#else -# include "common/widgets/OSPGlutViewer.h" -#endif - -ospcommon::vec3f translate; -ospcommon::vec3f scale; -bool lockFirstFrame = false; - -std::string scriptFileFromCommandLine(int ac, const char **&av) -{ - std::string scriptFileName; - - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--script" || arg == "-s") { - scriptFileName = av[++i]; - } - } - - return scriptFileName; -} - -void parseExtraParametersFromComandLine(int ac, const char **&av) -{ - for (int i = 1; i < ac; i++) { - const std::string arg = av[i]; - if (arg == "--translate") { - translate.x = atof(av[++i]); - translate.y = atof(av[++i]); - translate.z = atof(av[++i]); - } else if (arg == "--scale") { - scale.x = atof(av[++i]); - scale.y = atof(av[++i]); - scale.z = atof(av[++i]); - } else if (arg == "--lockFirstFrame") { - lockFirstFrame = true; - } - } -} - -int main(int ac, const char **av) -{ -#if 1 - ospInit(&ac,av); -#elif 1 - ospray::cpp::Device device("default"); - //ospray::cpp::Device device("mpi"); - - //device.set("numThreads", 1); - //device.set("logLevel", 2); - //device.set("debug", 1); - device.commit(); - - device.setCurrent(); -#else - auto device = ospCreateDevice(); - //auto device = ospCreateDevice("mpi"); - - // set device parameters... - //ospDeviceSet1i(device, "numThreads", 1); - //ospDeviceSet1i(device, "logLevel", 2); - //ospDeviceSet1i(device, "debug", 1); - ospDeviceCommit(device); - - ospSetCurrentDevice(device); -#endif - - ospray::glut3D::initGLUT(&ac,av); - - auto device = ospGetCurrentDevice(); - ospDeviceSetErrorMsgFunc(device, [](const char *msg) { std::cout << msg; }); - - auto ospObjs = parseWithDefaultParsers(ac, av); - - std::deque bbox; - std::deque model; - ospray::cpp::Renderer renderer; - ospray::cpp::Camera camera; - - std::tie(bbox, model, renderer, camera) = ospObjs; - - parseExtraParametersFromComandLine(ac, av); - -#ifdef OSPRAY_APPS_ENABLE_SCRIPTING - auto scriptFileName = scriptFileFromCommandLine(ac, av); - - ospray::ScriptedOSPGlutViewer window(bbox, model, renderer, - camera, scriptFileName); -#else - ospray::OSPGlutViewer window(bbox, model, renderer, camera); -#endif - window.setScale(scale); - window.setLockFirstAnimationFrame(lockFirstFrame); - window.setTranslation(translate); - window.create("ospGlutViewer: OSPRay Mini-Scene Graph test viewer"); - - ospray::glut3D::runGLUT(); -} diff --git a/apps/ospTutorial.c b/apps/ospTutorial.c index 6dfecfb1c6..504baf5b29 100644 --- a/apps/ospTutorial.c +++ b/apps/ospTutorial.c @@ -79,7 +79,9 @@ int main(int argc, const char **argv) { // initialize OSPRay; OSPRay parses (and removes) its commandline parameters, e.g. "--osp:debug" - ospInit(&argc, argv); + int init_error = ospInit(&argc, argv); + if (init_error != OSP_NO_ERROR) + return init_error; // create and setup camera OSPCamera camera = ospNewCamera("perspective"); @@ -123,6 +125,7 @@ int main(int argc, const char **argv) { // complete setup of renderer ospSet1i(renderer, "aoSamples", 1); + ospSet1f(renderer, "bgColor", 1.0f); // white, transparent ospSetObject(renderer, "model", world); ospSetObject(renderer, "camera", camera); ospSetObject(renderer, "lights", lights); diff --git a/apps/ospTutorial.cpp b/apps/ospTutorial.cpp index 682fc0c36d..f6f8b9a736 100644 --- a/apps/ospTutorial.cpp +++ b/apps/ospTutorial.cpp @@ -79,7 +79,9 @@ int main(int argc, const char **argv) { // initialize OSPRay; OSPRay parses (and removes) its commandline parameters, e.g. "--osp:debug" - ospInit(&argc, argv); + int init_error = ospInit(&argc, argv); + if (init_error != OSP_NO_ERROR) + return init_error; // create and setup camera OSPCamera camera = ospNewCamera("perspective"); @@ -123,6 +125,7 @@ int main(int argc, const char **argv) { // complete setup of renderer ospSet1i(renderer, "aoSamples", 1); + ospSet1f(renderer, "bgColor", 1.0f); // white, transparent ospSetObject(renderer, "model", world); ospSetObject(renderer, "camera", camera); ospSetObject(renderer, "lights", lights); diff --git a/apps/qtViewer/ModelViewer.cpp b/apps/qtViewer/ModelViewer.cpp deleted file mode 100644 index b038298bf4..0000000000 --- a/apps/qtViewer/ModelViewer.cpp +++ /dev/null @@ -1,475 +0,0 @@ -// ======================================================================== // -// Copyright 2016 SURVICE Engineering Company // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "ModelViewer.h" -#include "widgets/affineSpaceManipulator/HelperGeometry.h" -#include "FPSCounter.h" -// sg -#include "sg/SceneGraph.h" -#include "sg/Renderer.h" - -#include "sg/common/FrameBuffer.h" -// std -#include - -namespace ospray { - namespace viewer { - - using std::cout; - using std::endl; - - float autoRotateSpeed = 0.f; - - FPSCounter fps; - - OSPRayRenderWidget::OSPRayRenderWidget(std::shared_ptr renderer) - : QAffineSpaceManipulator(QAffineSpaceManipulator::INSPECT), - sgRenderer(renderer) - { - if (renderer->world) { - box3f worldBounds = renderer->world->getBounds(); - if (!worldBounds.empty()) { - float moveSpeed = .25*length(worldBounds.size()); - QAffineSpaceManipulator::setMoveSpeed(moveSpeed); - } - } - std::shared_ptr camera - = std::dynamic_pointer_cast(renderer->camera); - if (camera) { - frame->sourcePoint = camera->getFrom(); - frame->targetPoint = camera->getAt(); - frame->upVector = camera->getUp(); - frame->orientation.vz = normalize(camera->getUp()); - frame->orientation.vy = normalize(camera->getAt() - camera->getFrom()); - frame->orientation.vx = normalize(cross(frame->orientation.vy,frame->orientation.vz)); - } - } - - void OSPRayRenderWidget::setWorld(std::shared_ptr world) - { - assert(sgRenderer); - sgRenderer->setWorld(world); - cout << "#ospQTV: world set, found " - << sgRenderer->allNodes.size() << " nodes" << endl; - } - - //! the QT callback that tells us that we have to redraw - void OSPRayRenderWidget::redraw() - { - if (!sgRenderer) return; - if (!sgRenderer->frameBuffer) return; - if (!sgRenderer->camera) return; - - if (showFPS) { - static int frameID = 0; - if (frameID > 0) { - fps.doneRender(); - printf("fps: %7.3f (smoothed: %7.3f)\n",fps.getFPS(),fps.getSmoothFPS()); - } - fps.startRender(); - ++frameID; - } - - sgRenderer->renderFrame(); - - - vec2i size = sgRenderer->frameBuffer->getSize(); - unsigned char *fbMem = sgRenderer->frameBuffer->map(); - glDrawPixels(size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, fbMem); - - sgRenderer->frameBuffer->unmap(fbMem); - - if (autoRotateSpeed != 0.f) { - rotateAroundTarget(autoRotateSpeed,0.f); - // sgRenderer->resetAccumulation(); - updateOSPRayCamera(); - update(); - } - // accumulate, but only up to 32 frames - if (sgRenderer->accumID < 32) { - update(); - } - } - - //! the QT callback that tells us that the image got resize - void OSPRayRenderWidget::resize(int width, int height) - { - sgRenderer->frameBuffer = std::make_shared(vec2i(width,height)); - sgRenderer->resetAccumulation(); - - std::shared_ptr camera - = std::dynamic_pointer_cast(sgRenderer->camera); - - camera->setAspect(width/float(height)); - camera->commit(); - } - - //! update the ospray camera (ospCamera) from the widget camera (this->camera) - void OSPRayRenderWidget::updateOSPRayCamera() - { - if (!sgRenderer) return; - if (!sgRenderer->camera) return; - - sgRenderer->resetAccumulation(); - std::shared_ptr camera - = std::dynamic_pointer_cast(sgRenderer->camera); - - assert(camera); - const vec3f from = frame->sourcePoint; - const vec3f at = frame->targetPoint; - vec2i size = sgRenderer->frameBuffer->getSize(); - - camera->setFrom(from); - camera->setAt(at); - camera->setUp(frame->orientation.vz); - camera->setAspect(size.x/float(size.y)); - - camera->commit(); - } - - //! create the lower-side time step slider (for models that have - //! time steps; won't do anything for models that don't) - void ModelViewer::createTimeSlider() - { - } - - //! create the widet on the side that'll host all the editing widgets - void ModelViewer::createEditorWidgetStack() - { - editorWidgetStack = new EditorWidgetStack; - editorWidgetDock = new QDockWidget(this); - editorWidgetDock->setWindowTitle("Editors"); - editorWidgetDock->setWidget(editorWidgetStack); - editorWidgetDock->setFeatures(0); - addDockWidget(Qt::RightDockWidgetArea,editorWidgetDock); - } - - void ModelViewer::createTransferFunctionEditor() - { - QWidget *xfEditorsPage = NULL; - // make a list of all transfer function nodes in the scene graph - std::vector > xferFuncs; - for (int i=0;iuniqueNodes.size();i++) { - std::shared_ptr xf = - std::dynamic_pointer_cast(sgRenderer->uniqueNodes.object[i]->node); - if (xf) xferFuncs.push_back(xf); - } - std::cout << "#osp:qtv: found " << xferFuncs.size() - << " transfer function nodes" << std::endl; - - if (xferFuncs.empty()) { - xfEditorsPage = new QLabel("(no xfer fcts found)"); - } else { - // ------------------------------------------------------- - // found some transfer functions - create a stacked widget - // with an editor for each - // ------------------------------------------------------- - xfEditorsPage = new QWidget; - QVBoxLayout *layout = new QVBoxLayout; - xfEditorsPage->setLayout(layout); - - QStackedWidget *stackedWidget = new QStackedWidget; - QComboBox *pageComboBox = new QComboBox; - QObject::connect(pageComboBox, SIGNAL(activated(int)), - stackedWidget, SLOT(setCurrentIndex(int))); - - layout->addWidget(pageComboBox); - layout->addWidget(stackedWidget); - - // now, create widgets for all of them - for (int i=0;iname; - if (name == "") { - std::stringstream ss; - ss << "(unnamed xfr fct #" << i << ")"; - name = ss.str(); - } - // add combo box and stacked widget entries - pageComboBox->addItem(tr(name.c_str())); - - std::shared_ptr xf = xferFuncs[i]; - assert(xf); - // create a transfer function editor for this transfer function node - QOSPTransferFunctionEditor *xfEd - = new QOSPTransferFunctionEditor(xf); - const std::vector > &alpha = xf->getAlphaArray(); - if (!alpha.empty()) { - std::vector points; - for (int i=0;isetOpacityPoints(points); - } - stackedWidget->addWidget(xfEd); - connect(xfEd, SIGNAL(transferFunctionChanged()), - this, SLOT(render())); - } - } - editorWidgetStack->addPage("Transfer Functions",xfEditorsPage); - } - - void ModelViewer::createLightManipulator() - { - QWidget *lmEditorsPage = new QWidget; - QVBoxLayout *layout = new QVBoxLayout; - lmEditorsPage->setLayout(layout); - - QStackedWidget *stackedWidget = new QStackedWidget; - layout->addWidget(stackedWidget); - - std::shared_ptr camera - = std::dynamic_pointer_cast(renderWidget->sgRenderer->camera); - QLightManipulator *lManipulator = new QLightManipulator(sgRenderer, camera->getUp()); - //stackedWidget->addWidget(lManipulator); - layout->addWidget(lManipulator); - - editorWidgetStack->addPage("Light Editor", lManipulator); - - connect(lManipulator, SIGNAL(lightsChanged()), this, SLOT(render())); - } - - void ModelViewer::toggleUpAxis(int axis) - { - std::cout << "#osp:QTV: new upvector is " << renderWidget->getFrame()->upVector << std::endl; - } - - void ModelViewer::keyPressEvent(QKeyEvent *event) { - switch (event->key()) { - case Qt::Key_Escape: { - QApplication::quit(); - } break; - case '{': { - if (autoRotateSpeed == 0.f) { - autoRotateSpeed = -1.f; - } else if (autoRotateSpeed < 0) { - autoRotateSpeed *= 1.5f; - } else { - autoRotateSpeed /= 1.5f; - if (autoRotateSpeed < 1.f) - autoRotateSpeed = 0.f; - } - printf("new auto-rotate speed %f\n",autoRotateSpeed); - update(); - } break; - case '}': { - if (autoRotateSpeed == 0.f) { - autoRotateSpeed = +1.f; - } else if (autoRotateSpeed < 0) { - autoRotateSpeed /= 1.5f; - if (autoRotateSpeed > -1.f) - autoRotateSpeed = 0.f; - } else { - autoRotateSpeed *= 1.5f; - } - printf("new auto-rotate speed %f\n",autoRotateSpeed); - update(); - } break; - case '|': { - printf("'|' key - stopping auto-rotation\n"); - autoRotateSpeed = 0.f; - update(); - } break; - case Qt::Key_C: { - // ------------------------------------------------------------------ - // 'C': - // - Shift-C print current camera - // ------------------------------------------------------------------ - if (event->modifiers() & Qt::ShiftModifier) { - // shift-f: switch to fly mode - std::cout << "Shift-C: Printing camera" << std::endl; - printCameraAction(); - } - } break; - case Qt::Key_F: { - // ------------------------------------------------------------------ - // 'F': - // - Shift-F enters fly mode - // - Ctrl-F toggles full-screen - // ------------------------------------------------------------------ - if (event->modifiers() & Qt::ShiftModifier) { - // shift-f: switch to fly mode - std::cout << "Shift-F: Entering 'Fly' mode" << std::endl; - renderWidget->setInteractionMode(QAffineSpaceManipulator::FLY); - } else if (event->modifiers() & Qt::ControlModifier) { - // ctrl-f: switch to full-screen - std::cout << "Ctrl-F: Toggling full-screen mode" << std::endl; - setWindowState(windowState() ^ Qt::WindowFullScreen); - if (windowState() & Qt::WindowFullScreen){ - toolBar->hide(); - editorWidgetDock->hide(); - } else { - toolBar->show(); - editorWidgetDock->show(); - } - } - } break; - case Qt::Key_I: { - // ------------------------------------------------------------------ - // 'I': - // - Ctrl-I switches to inspect mode - // ------------------------------------------------------------------ - if (event->modifiers() & Qt::ShiftModifier) { - // shift-f: switch to fly mode - std::cout << "Shift-I: Entering 'Inspect' mode" << std::endl; - renderWidget->setInteractionMode(QAffineSpaceManipulator::INSPECT); - } - } break; - case Qt::Key_Q: { - QApplication::quit(); - } break; - case Qt::Key_R: { - if (event->modifiers() & Qt::ShiftModifier) { - // shift-f: switch to fly mode - std::cout << "Shift-R: Entering Free-'Rotation' mode (no up-vector)" << std::endl; - renderWidget->setInteractionMode(QAffineSpaceManipulator::FREE_ROTATION); - } - } break; - case Qt::Key_X: { - // ------------------------------------------------------------------ - // 'X': - // - Ctrl-X switches to X-up/down for upvector - // ------------------------------------------------------------------ - if (event->modifiers() & Qt::ShiftModifier) { - // shift-x: switch to X-up - std::cout << "Shift-X: switching to X-up/down upvector " << std::endl; - renderWidget->toggleUp(0); - } - } break; - // ------------------------------------------------------------------ - // 'Y': - // - Ctrl-Y switches to Y-up/down for upvector - // ------------------------------------------------------------------ - case Qt::Key_Y: { - if (event->modifiers() & Qt::ShiftModifier) { - // shift-x: switch to X-up - std::cout << "Shift-Y: switching to Y-up/down upvector " << std::endl; - renderWidget->toggleUp(1); - } - } break; - case Qt::Key_Z: { - // ------------------------------------------------------------------ - // 'Z': - // - Ctrl-Z switches to Z-up/down for upvector - // ------------------------------------------------------------------ - if (event->modifiers() & Qt::ShiftModifier) { - // shift-x: switch to X-up - std::cout << "Shift-Z: switching to Z-up/down upvector " << std::endl; - renderWidget->toggleUp(2); - } - } break; - - default: - QMainWindow::keyPressEvent(event); - } - } - - ModelViewer::ModelViewer(std::shared_ptr sgRenderer, bool fullscreen) - : editorWidgetStack(NULL), - transferFunctionEditor(NULL), - lightEditor(NULL), - toolBar(NULL), - sgRenderer(sgRenderer) - { - // resize to default window size - setWindowTitle(tr("OSPRay Qt ModelViewer")); - resize(1024,768); - - // create GUI elements - toolBar = addToolBar("toolbar"); - - QAction *printCameraAction = new QAction("Print Camera", this); - connect(printCameraAction, SIGNAL(triggered()), this, SLOT(printCameraAction())); - toolBar->addAction(printCameraAction); - - QAction *screenShotAction = new QAction("Screenshot", this); - connect(screenShotAction, SIGNAL(triggered()), this, SLOT(screenShotAction())); - toolBar->addAction(screenShotAction); - - renderWidget = new OSPRayRenderWidget(sgRenderer); - connect(renderWidget,SIGNAL(affineSpaceChanged(QAffineSpaceManipulator *)), - this,SLOT(cameraChanged())); - - setCentralWidget(renderWidget); - - setFocusPolicy(Qt::StrongFocus); - - createTimeSlider(); - createEditorWidgetStack(); - createTransferFunctionEditor(); - createLightManipulator(); - - if (fullscreen) { - setWindowState(windowState() | Qt::WindowFullScreen); - toolBar->hide(); - editorWidgetDock->hide(); - } - } - - void ModelViewer::setWorld(std::shared_ptr world) - { - renderWidget->setWorld(world); - } - - void ModelViewer::render() - { - sgRenderer->resetAccumulation(); - if (renderWidget) renderWidget->updateGL(); - } - - // this is a incoming signal that the render widget changed the camera - void ModelViewer::cameraChanged() - { - renderWidget->updateOSPRayCamera(); - } - - //! print the camera on the command line (triggered by toolbar/menu). - void ModelViewer::printCameraAction() - { - std::shared_ptr camera - = std::dynamic_pointer_cast(renderWidget->sgRenderer->camera); - vec3f from = camera->getFrom(); - vec3f up = camera->getUp(); - vec3f at = camera->getAt(); - float fovy = camera->getFovy(); - std::cout << "#osp:qtv: camera is" - << " -vp " << from.x << " " << from.y << " " << from.z - << " -vi " << at.x << " " << at.y << " " << at.z - << " -vu " << up.x << " " << up.y << " " << up.z - << " -fv " << fovy - << std::endl; - } - - //! take a screen shot - void ModelViewer::screenShotAction() - { - vec2i size = sgRenderer->frameBuffer->getSize(); - unsigned char *fbMem = sgRenderer->frameBuffer->map(); - glDrawPixels(size.x, size.y, GL_RGBA, GL_UNSIGNED_BYTE, fbMem); - sgRenderer->frameBuffer->unmap(fbMem); - - QImage fb = QImage(fbMem,size.x,size.y,QImage::Format_RGB32).rgbSwapped().mirrored(); - const std::string fileName = "/tmp/ospQTV.screenshot.png"; - fb.save(fileName.c_str()); - std::cout << "screen shot saved in " << fileName << std::endl; - } - - void ModelViewer::lightChanged() - { - } - - } // ::ospray::viewer -} // ::ospray diff --git a/apps/qtViewer/ModelViewer.h b/apps/qtViewer/ModelViewer.h deleted file mode 100644 index 8d69e26d25..0000000000 --- a/apps/qtViewer/ModelViewer.h +++ /dev/null @@ -1,158 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#define WARN_ON_INCLUDING_OSPCOMMON 1 - -// widgets -#include "widgets/affineSpaceManipulator/QAffineSpaceManipulator.h" -#include "widgets/transferFunction/QTransferFunctionEditor.h" -#include "widgets/lightManipulator/QLightManipulator.h" -// scene graph -#include "sg/Renderer.h" - -namespace ospray { - namespace viewer { - - //! single widget that realizes the stack of editor widgets on the - //! right of the window. contains a set of individual editors, all - //! maintained in a stack - struct EditorWidgetStack : public QWidget { - - EditorWidgetStack() - { - pageComboBox = new QComboBox; - stackedWidget = new QStackedWidget; - QVBoxLayout *layout = new QVBoxLayout; - layout->addWidget(pageComboBox); - layout->addWidget(stackedWidget); - setLayout(layout); - } - - void addPage(const std::string &name, QWidget *widget) - { - assert(currentPages.find(name) == currentPages.end()); - currentPages[name] = widget; - stackedWidget->addWidget(widget); - pageComboBox->addItem(tr(name.c_str())); - connect(pageComboBox,SIGNAL(activated(int)),stackedWidget,SLOT(setCurrentIndex(int))); - } - - protected: - std::map currentPages; - QComboBox *pageComboBox; - QStackedWidget *stackedWidget; - }; - - - // ======================================================= - //! render widget that renders through ospray - // ======================================================= - struct OSPRayRenderWidget : public QAffineSpaceManipulator { - OSPRayRenderWidget(std::shared_ptr renderer); - - std::shared_ptr sgRenderer; - - // ------------------------------------------------------- - // render widget callbacks - // ------------------------------------------------------- - - // std::shared_ptr sgRenderer; - - virtual void redraw(); - virtual void resize(int width, int height); - - void setWorld(std::shared_ptr world); - - // ------------------------------------------------------- - // internal state - // ------------------------------------------------------- - - //! update the ospray camera (ospCamera) from the widget camera (this->camera) - void updateOSPRayCamera(); - - //! signals that the current ospray state is dirty (i.e., that - //! we can not accumulate) - bool isDirty; - - //! the world we're displaying - std::shared_ptr world; - - //! whether to display the frame rate - bool showFPS; - }; - - - /*! \brief main QT window of the model viewer */ - struct ModelViewer : public QMainWindow { - Q_OBJECT - - public: - ModelViewer(std::shared_ptr sgRenderer, bool fullscreen); - - void toggleUpAxis(int axis); - - public slots: - //! signals that the render widget changed one of the inputs - //! (most likely, that the camera position got changed) - void cameraChanged(); - - //! print the camera on the command line (triggered by toolbar/menu). - void printCameraAction(); - //! take a screen shot - void screenShotAction(); - - void render(); - - void setWorld(std::shared_ptr newWorld); - - /*! enable/disable display of frame rate */ - void showFrameRate(bool showFPS) { this->renderWidget->showFPS = showFPS; } - - //! Catches that the light manipulator has changed our default light - void lightChanged(); - - protected: - //! create the lower-side time step slider (for models that have - //! time steps; won't do anything for models that don't) - void createTimeSlider(); - //! create the widet on the side that'll host all the editing widgets - void createEditorWidgetStack(); - //! create a transfer fucntion editor - void createTransferFunctionEditor(); - //! create a light manipulator - void createLightManipulator(); - //! use escape to quit - virtual void keyPressEvent(QKeyEvent *event); - - // ------------------------------------------------------- - // qt gui components - // ------------------------------------------------------- - EditorWidgetStack *editorWidgetStack; - QDockWidget *editorWidgetDock; - QToolBar *toolBar; - QTransferFunctionEditor *transferFunctionEditor; - QLightManipulator *lightEditor; - - std::shared_ptr sgRenderer; - - public: - OSPRayRenderWidget *renderWidget; - }; - } -} - diff --git a/apps/qtViewer/main.cpp b/apps/qtViewer/main.cpp deleted file mode 100644 index fc2070d097..0000000000 --- a/apps/qtViewer/main.cpp +++ /dev/null @@ -1,339 +0,0 @@ -// ======================================================================== // -// Copyright 2016 SURVICE Engineering Company // -// Copyright 2009-2017 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. // -// ======================================================================== // - -// viewer -#include "ModelViewer.h" -// ospray -#include "ospray/ospray.h" -// qt -#include -#include -#include -// scene graph -#include "sg/module/Module.h" -#include "sg/importer/Importer.h" -#include "sg/common/FrameBuffer.h" - -#include -#include - -namespace ospray { - namespace viewer { - using std::cout; - using std::endl; - - /*! verbosity level */ - int verbosity = 0; - - static const std::string DEFAULT_INTEGRATOR_NAME = "scivis"; //ao2"; - // static const std::string DEFAULT_INTEGRATOR_NAME = "eyeLight_geomID"; - - - /*! @{ state to be set via commandline params */ - - /*! file we're saving the output to. If empty, we'll open a - interactive viewer; otherwise we'll render to this file and - exit. */ - std::string outFileName = ""; - - /*! size of frame (when rendering offline) resp render widget - window (when creating viewer) */ - vec2i frameResolution(-1,-1); - - /*! camera as specified on the command line */ - std::shared_ptr cameraFromCommandLine; - vec3f upFromCommandLine(0,0,0); - - /*! renderer as specified on the command line */ - std::string integratorFromCommandLine = ""; - - /*! whether we will display the frames per second */ - bool showFPS = false; - - /*! number of samples per pixel */ - int spp = 1; - - /*! @} */ - - void main(int argc, const char *argv[]) - { - // init ospray - ospInit(&argc,argv); - auto device = ospGetCurrentDevice(); - ospDeviceSetErrorMsgFunc(device, - [](const char *msg) { std::cout << msg; }); - // init qt - QApplication *app = new QApplication(argc, (char **)argv); - - auto world = std::make_shared(); - auto renderer = std::make_shared(); - bool fullscreen = false; - - for (int argID=1;argID(); - - auto x = 0.f; - auto y = 0.f; - auto z = 0.f; - - auto token = std::string(""); - while (fin >> token) - { - if (token == "-vp") - { - fin >> x >> y >> z; - cameraFromCommandLine->setFrom(vec3f(x,y,z)); - } - else if (token == "-vu") - { - fin >> x >> y >> z; - cameraFromCommandLine->setUp(vec3f(x,y,z)); - } - else if (token == "-vi") - { - fin >> x >> y >> z; - upFromCommandLine = vec3f(x, y, z); - cameraFromCommandLine->setAt(upFromCommandLine); - } - else if (token == "-fv") - { - fin >> x; - cameraFromCommandLine->setFovy(x); - } - else - { - throw std::runtime_error("Unrecognized token: \"" + token + - '\"'); - } - } - } else if (arg == "-vi") { - if (!cameraFromCommandLine) - cameraFromCommandLine = std::make_shared(); - assert(argID+3setAt(vec3f(x,y,z)); - } else if (arg == "-vp") { - if (!cameraFromCommandLine) - cameraFromCommandLine = std::make_shared(); - assert(argID+3setFrom(vec3f(x,y,z)); - } else if (arg == "-vu") { - assert(argID+3setUp(vec3f(x,y,z)); - } else if (arg == "--fullscreen" || arg == "-fs"){ - fullscreen = true; - } else { - throw std::runtime_error("#osp:qtv: unknown cmdline param '" + - arg + "'"); - } - } else { - FileName fn = arg; - if (fn.ext() == "osp" || fn.ext() == "osg" || fn.ext() == "pkd") { - world = sg::loadOSP(fn.str()); - // } else if (fn.ext() == "atom") { - // world = sg::AlphaSpheres::importOspAtomFile(fn.str()); - } else if ((fn.ext() == "ply") || - ((fn.ext() == "gz") && (fn.dropExt().ext() == "ply"))) { - sg::importPLY(world,fn); - } else if (fn.ext() == "obj") { - sg::importOBJ(world,fn); - } else if (fn.ext() == "xml") { - std::cout << "#osp:qtv: reading RIVL file " << arg << std::endl; - world = sg::importRIVL(arg); - } else - sg::importFile(world,fn); - } - } - - if (!world) { - std::cout << "#osp:qtv: no world defined. exiting." << std::endl; - exit(1); - } - // set the current world ... - std::cout << "#osp:qtv: setting world ..." << std::endl; - if (verbosity >= 1) { - std::cout << "#osp:qtv: world bounds is " << world->getBounds() - << std::endl; - } - renderer->setWorld(world); - - // ------------------------------------------------------- - // initialize renderer's integrator - // ------------------------------------------------------- - { - // first, check if one is specified in the scene file. - auto integrator = renderer->getLastDefinedIntegrator(); - if (!integrator) { - std::string integratorName = integratorFromCommandLine; - if (integratorName == "") - integratorName = DEFAULT_INTEGRATOR_NAME; - integrator = std::make_shared(integratorName); - } - renderer->setIntegrator(integrator); - integrator->setSPP(spp); - integrator->commit(); - } - - // ------------------------------------------------------- - // initialize renderer's camera - // ------------------------------------------------------- - { - // activate the last camera defined in the scene graph (if set) - if (cameraFromCommandLine) { - renderer->setCamera(std::dynamic_pointer_cast(cameraFromCommandLine)); - } else { - renderer->setCamera(renderer->getLastDefinedCamera()); - } - if (!renderer->camera) { - renderer->setCamera(renderer->createDefaultCamera(upFromCommandLine)); - } - } - - // ------------------------------------------------------- - // determine output method: offline to file, or interactive viewer - // ------------------------------------------------------- - if (outFileName == "") { - // create new modelviewer - cout << "#osp:qtv: setting up to open QT viewer window" << endl; - ModelViewer *modelViewer = new ModelViewer(renderer, fullscreen); - // modelViewer->setFixedSize(frameResolution.x,frameResolution.y); - modelViewer->showFrameRate(showFPS); - std::cout << "#osp:qtv: Press 'f' to toggle fullscreen rendering mode" << endl; - if (fullscreen){ - std::cout << "#osp:qtv: opening fullscreen viewer, press 'ESC' to quit" << endl; - modelViewer->showFullScreen(); - } else { - modelViewer->show(); - } - - if (frameResolution.x > 0) { - cout << "#osp:qtv: trying to set render size to " - << frameResolution.x << "x" << frameResolution.y << " pixels" - << endl; - - float frame_width = modelViewer->width() - - modelViewer->renderWidget->width(); - float frame_height = modelViewer->height() - - modelViewer->renderWidget->height(); - modelViewer->resize(frameResolution.x+frame_width, - frameResolution.y+frame_height); - cout << "#osp:qtv: now rendering at " - << modelViewer->renderWidget->width() - << "x" << modelViewer->renderWidget->height() << " pixels." - << endl; - } - - // let qt run... - app->exec(); - - // done, closing down. - delete modelViewer; - delete app; - } else { - cout << "#osp:qtv: setting up in render-to-file mode" << endl; - if (frameResolution == vec2i(-1, -1)) { - cout << "#osp:qtv: Warning! " - << "no resolution specified, defaulting to 1280x720" << endl; - frameResolution = vec2i(1280, 720); - } - if (!renderer->frameBuffer) { - cout << "#osp:qtv: creating default framebuffer (" - << frameResolution.x << "x" << frameResolution.y << ")" << endl; - renderer->frameBuffer = - std::make_shared(frameResolution); - } - renderer->frameBuffer->commit(); - renderer->frameBuffer->clear(); - - // output file specified - render to file - cout << "#osp:qtv: rendering frame" << endl; - renderer->renderFrame(); - - unsigned char *fbMem = renderer->frameBuffer->map(); - cout << "#osp:qtv: saving image" << endl; - // PRINT((int*)fbMem); - // PRINT(*(int**)fbMem); - QImage image = QImage(fbMem, - renderer->frameBuffer->getSize().x, - renderer->frameBuffer->getSize().y, - QImage::Format_ARGB32).rgbSwapped().mirrored(); - // QImage fb = QImage(fbMem,size.x,size.y,QImage::Format_RGB32).rgbSwapped().mirrored(); - image.save(outFileName.c_str()); - renderer->frameBuffer->unmap(fbMem); - cout << "#osp:qtv: rendered image saved to " << outFileName << endl; - cout << "#osp:qtv: done... exiting." << endl; - exit(0); - } - } - } -} - -int main(int argc, const char *argv[]) -{ - try { - ospray::viewer::main(argc,argv); - return 0; - } catch (std::runtime_error e) { - std::cerr << "#osp:qtv: fatal error: " << e.what() << std::endl; - return 1; - } -} - diff --git a/apps/qtViewer/testing/procVolume.osp b/apps/qtViewer/testing/procVolume.osp deleted file mode 100644 index a43f74f9ae..0000000000 --- a/apps/qtViewer/testing/procVolume.osp +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/apps/qtViewer/widgets/affineSpaceManipulator/HelperGeometry.cpp b/apps/qtViewer/widgets/affineSpaceManipulator/HelperGeometry.cpp deleted file mode 100644 index 9f4f7e8c79..0000000000 --- a/apps/qtViewer/widgets/affineSpaceManipulator/HelperGeometry.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -// viewer -#include "HelperGeometry.h" - -namespace ospray { - namespace viewer { - - void HelperGeometry::Mesh::addQuad(const affine3f &xfm, const Quad &quad) - { - vec3i idx0 = vec3i(vertex.size()); - for (int i=0;i<4;i++) { - vertex.push_back(vec3fa(xfmPoint(xfm,quad.vtx[i]))); - normal.push_back(vec3fa(normalize(xfmVector(xfm,quad.nor[i])))); - } - index.push_back(idx0+vec3i(0,1,2)); - index.push_back(idx0+vec3i(0,2,3)); - } - - CoordFrameGeometry::CoordFrameGeometry() - : shaftThickness(.1), headLength(.2f), numSegments(16) - { - makeArrow(arrow[0],vec3f(1,0,0)); - makeArrow(arrow[1],vec3f(0,1,0)); - makeArrow(arrow[2],vec3f(0,0,1)); - } - void CoordFrameGeometry::makeArrow(Mesh &mesh,const vec3f &axis) - { - mesh.color = axis; - affine3f xfm = frame(axis); - std::swap(xfm.l.vx,xfm.l.vz); - Quad quad; - for (int i=0;i - -namespace ospray { - namespace viewer { - using namespace sg; - - //! \brief Helper class for storing tessellated geometry (with - //! single color per mesh); that can be used to store 3D geometry - //! for 3D widgets - struct HelperGeometry { - //! a quad made up of four vertices with given vertex normal - struct Quad { - vec3f vtx[4], nor[4]; - }; - //! a mesh/indexed face set of triangles - struct Mesh { - //! base color for this mesh; shared among all vertices - vec3f color; - //! vertex array - std::vector vertex; - //! normal array - std::vector normal; - //! triangle/index array - std::vector index; - - //! \brief add a new quad with given vertices to this triangle mesh - void addQuad(const affine3f &xfm, const Quad &quad); - }; - }; - - //! \brief A triangle mesh representing a coordinate frame with - //! three tessellated arrows, with first pointing in X direction - //! (colored red), the second in Y (colored greed), the third in Z - //! (colored blue). */ - struct CoordFrameGeometry : public HelperGeometry { - Mesh arrow[3]; - float shaftThickness; - float headLength; - int numSegments; - - CoordFrameGeometry(); - void makeArrow(Mesh &mesh,const vec3f &axis); - }; - } // ::viewer -} // ::ospray - diff --git a/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.cpp b/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.cpp deleted file mode 100644 index be0d1fb288..0000000000 --- a/apps/qtViewer/widgets/affineSpaceManipulator/QAffineSpaceManipulator.cpp +++ /dev/null @@ -1,451 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -// viewer -#include "QAffineSpaceManipulator.h" -#include "HelperGeometry.h" -#include - -#if __APPLE__ -# include "glut.h" -#else -# include "GL/glut.h" -#endif - -namespace ospray { - namespace viewer { - - //! modifier to be used for detecting clicks as 'pick' events - const Qt::KeyboardModifier pickModifier = Qt::ShiftModifier; - //! modifier to be used for detecting clicks as 'strafe' (left/right/up/down) events - const Qt::KeyboardModifier strafeModifier = Qt::ControlModifier; - //! modifier to be used for detecting clicks as 'move' (forward/backward) events - const Qt::KeyboardModifier moveModifier = Qt::AltModifier; - - //! helper function that projects a given screen coordinate to a virtual sphere of given radius - vec3f projectToSphere(QPoint pt, vec2i size, float radius) - { - int h = std::min(size.x,size.y); - float fx = (pt.x()-size.x/2.f)/float(h/2.f) / radius; - float fz = -(pt.y()-size.y/2.f)/float(h/2.f) / radius; - float fy = 0.f; - float r = 1.f - fx*fx - fz*fz; - if (r >= 0.f) - fy = -sqrtf(r); - vec3f spherePos = normalize(vec3f(fx,fy,fz)); - return spherePos; - } - - QAffineSpaceManipulator::ReferenceFrame::ReferenceFrame() - : sourcePoint(0,-4,0), - targetPoint(0,0,0), - upVector(0,1,0), - orientation(ospcommon::one) - {} - - void QAffineSpaceManipulator::ReferenceFrame::snapUp() - { - if (dot(upVector,upVector) <= 1e-6f) return; - // vy stays - linear3f old_orientation = orientation; - orientation.vx = cross(orientation.vy,upVector); - orientation.vz = cross(orientation.vx,orientation.vy); - if (std::isnan(reduce_add(orientation.vx+orientation.vy))) - orientation = old_orientation; - } - - QAffineSpaceManipulator::QAffineSpaceManipulator(InteractionMode interactionMode) - : size(-1,-1), - frame(new ReferenceFrame), - // interactionMode(QAffineSpaceManipulator::FREE_ROTATION), - interactionMode(interactionMode), - motionSpeed(0.f) - {} - - //! the QT callback that tells us that we have to redraw - void QAffineSpaceManipulator::paintGL() - { - redraw(); - } - - //! the QT callback that tells us that the image got resize - void QAffineSpaceManipulator::resizeGL(int width, int height) - { - glViewport(0, 0, width, height); - size = vec2i(width,height); - resize(width,height); - } - - void QAffineSpaceManipulator::mousePressEvent(QMouseEvent * event) - { - lastMousePos = event->pos(); - orientationWhenMouseGotPressed = frame->orientation; - if (event->buttons() & Qt::LeftButton && - event->modifiers() & pickModifier) { - pick(event); - } - } - void QAffineSpaceManipulator::mouseReleaseEvent(QMouseEvent * event) - { - lastMousePos = event->pos(); - } - - inline std::ostream &operator<<(std::ostream &o, const QPoint &q) - { o << "(" << q.x() << "," << q.y() << ")"; return o; } - - - void QAffineSpaceManipulator::strafe(QMouseEvent * event) - { - QPoint newPos = event->pos(); - - const vec3f moveAxisU = frame->orientation.vx; - const float moveDistU = - 2 * motionSpeed * (newPos.x()-lastMousePos.x()) / float(size.x); - const vec3f moveAxisV = frame->orientation.vz; - const float moveDistV = + 2 * motionSpeed * (newPos.y()-lastMousePos.y()) / float(size.y); - const vec3f moveVec = moveDistU * moveAxisU + moveDistV * moveAxisV; - - frame->sourcePoint += moveVec; - frame->targetPoint += moveVec; - - lastMousePos = event->pos(); - emit affineSpaceChanged(this); - updateGL(); - } - - /*! rotate around target point, by given angles */ - void QAffineSpaceManipulator::rotateAroundTarget(float angle_x, float angle_y) - { - // axes we're rotating in u and v direction, respectively. - const vec3f uRotationAxis = frame->orientation.vz; - const vec3f vRotationAxis = frame->orientation.vx; - const float rotSpeed = 2.f; - const float du = - angle_x / 300.f; - const float dv = - angle_y / 300.f; - linear3f rot - = linear3f::rotate(vRotationAxis,dv) - * linear3f::rotate(uRotationAxis,du); - frame->orientation = rot * frame->orientation; - frame->snapUp(); - const vec3f vecToTarget = frame->targetPoint - frame->sourcePoint; - if (interactionMode == FLY) { - // in FLY mode, the SOURCE point stays, and the target point rotates - frame->targetPoint = frame->sourcePoint + xfmVector(rot,vecToTarget); - } else { - assert(interactionMode == INSPECT); - frame->sourcePoint = frame->targetPoint - xfmVector(rot,vecToTarget); - } - } - - void QAffineSpaceManipulator::rotate(QMouseEvent * event) - { - QPoint newPos = event->pos(); - - switch (interactionMode) { - case FLY: - case INSPECT: { - // axes we're rotating in u and v direction, respectively. - const vec3f uRotationAxis = frame->orientation.vz; - const vec3f vRotationAxis = frame->orientation.vx; - const float rotSpeed = 2.f; - const float du = - rotSpeed * (newPos.x()-lastMousePos.x()) / float(size.x); - const float dv = - rotSpeed * (newPos.y()-lastMousePos.y()) / float(size.y); - linear3f rot - = linear3f::rotate(vRotationAxis,dv) - * linear3f::rotate(uRotationAxis,du); - frame->orientation = rot * frame->orientation; - frame->snapUp(); - const vec3f vecToTarget = frame->targetPoint - frame->sourcePoint; - if (interactionMode == FLY) { - // in FLY mode, the SOURCE point stays, and the target point rotates - frame->targetPoint = frame->sourcePoint + xfmVector(rot,vecToTarget); - } else { - assert(interactionMode == INSPECT); - frame->sourcePoint = frame->targetPoint - xfmVector(rot,vecToTarget); - } - } break; - case FREE_ROTATION: { - vec3f oldVec = normalize(projectToSphere(lastMousePos,size,1.f)); - vec3f newVec = normalize(projectToSphere(newPos,size,1.f)); - oldVec = xfmVector((orientationWhenMouseGotPressed),oldVec); - newVec = xfmVector((orientationWhenMouseGotPressed),newVec); - - vec3f rotAxis = cross(oldVec,newVec); - if (dot(rotAxis,rotAxis) > 1e-6f) { - rotAxis = normalize(rotAxis); - float rotAngle = - acosf(dot(oldVec,newVec)); - linear3f rot = linear3f::rotate(rotAxis,rotAngle); - - frame->orientation = frame->orientation * rot; - const vec3f vecToTarget = frame->targetPoint - frame->sourcePoint; - frame->sourcePoint = frame->targetPoint - xfmVector(rot,vecToTarget); - } - - } break; - default: - std::cout << "no rotation implemented for this interaction mode" << std::endl; - }; - lastMousePos = event->pos(); - emit affineSpaceChanged(this); - updateGL(); - } - - void QAffineSpaceManipulator::move(QMouseEvent * event) - { - QPoint newPos = event->pos(); - - const vec3f moveAxis = frame->orientation.vy; - float moveDistance = - 10 * motionSpeed * (newPos.y()-lastMousePos.y()) / float(size.y); - switch (interactionMode) { - case FLY: { - /* fly mode: move BOTH source and target positions - forward/backward along move axis. Since we are moving - *forward* with mouse, we move in POSITIVE y distance */ - frame->sourcePoint -= moveDistance * moveAxis; - frame->targetPoint -= moveDistance * moveAxis; - } break; - case FREE_ROTATION: - case INSPECT: { - /* inspect mode: pull or push target towards/away from - viewer; but at most to a given minimum distance (can't - pull behind us). Since we are PULLING the target - TOWARDS us, we move with NEGATIVE distance */ - moveDistance = -moveDistance; - const float targetDistance = length(frame->targetPoint - frame->sourcePoint); - const float maxAllowedDistance = targetDistance - .1f * motionSpeed; - if (moveDistance > maxAllowedDistance) moveDistance = maxAllowedDistance; - frame->sourcePoint += moveDistance * moveAxis; - } break; - } - lastMousePos = event->pos(); - emit affineSpaceChanged(this); - updateGL(); - } - - - //! switch to requested interaction mode - void QAffineSpaceManipulator::setInteractionMode(QAffineSpaceManipulator::InteractionMode interactionMode) - { this->interactionMode = interactionMode; } - - /*! toggle-up: switch to given axis (x=0,y=1,z=2) as up-vectors for the rotation. - when _already_ in up-vector mode for this axis, switch to negative axis. */ - void QAffineSpaceManipulator::toggleUp(int axis) - { - vec3f newUp = vec3f(axis==0,axis==1,axis==2); - if (frame->upVector == newUp) - newUp = - newUp; - frame->upVector = newUp; - frame->snapUp(); - } - - - - void QAffineSpaceManipulator::mouseMoveEvent(QMouseEvent * event) - { - // ================================================================== - // P I C K I N G - // ================================================================== - if ( - // shift-left-click: - (event->buttons() & Qt::LeftButton) && (event->modifiers() & pickModifier) - ) - pick(event); - - // ================================================================== - // S T R A F I N G - straft left/right/up/down in image plane - // ================================================================== - if ( - // drag middle button (for mice that have one) - (event->buttons() & Qt::MidButton) - || - // drag mouse with control pressed (for mac-style trackpads) - ((event->buttons() & Qt::LeftButton) && (event->modifiers() & strafeModifier)) - ) - strafe(event); - - // ================================================================== - // M O V I N G - move forward/backward along camera axis - // ================================================================== - if ( - // drag right button (for mice that have one) - (event->buttons() & Qt::RightButton) - || - // drag mouse with alt-button pressed (for mac-style trackpads) - ((event->buttons() & Qt::LeftButton) && (event->modifiers() & moveModifier)) - ) - move(event); - - // ------------------------------------------------------- - // no modifiers: plain ol' rotate - // ------------------------------------------------------- - rotate(event); - return; - } - - - void QCoordAxisFrameEditor::redraw() - { - static CoordFrameGeometry coordFrameGeometry; - const vec3f color_bg(.5f); - // clear background - glClearColor(color_bg.x,color_bg.y,color_bg.z,0.f); - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); - - glEnable( GL_LIGHTING ); - - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHT0); - glColorMaterial(GL_FRONT_AND_BACK,GL_AMBIENT_AND_DIFFUSE ); - GLfloat lightPos[] = {-.2f, -2.f, 1.0f, 0.0f}; - glLightfv(GL_LIGHT0, GL_POSITION, lightPos); - GLfloat ambient[] = {0.4f, 0.4f, 0.4f, 1.0f}; - GLfloat diffuse[] = {1.f, 1.f, 1.0f, 1.0f}; - glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); - glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); - - glColor3f(1,1,1); - - glViewport(0, 0, (GLsizei) size.x, (GLsizei) size.y); - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(30, (GLfloat) size.x/(GLfloat) size.y, 1.0, 100.0); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - // gluLookAt(0, -5, 0, 0, 0, 0, 0, 0, 1); - vec3f up = frame->orientation.vz; - gluLookAt(frame->sourcePoint.x,frame->sourcePoint.y,frame->sourcePoint.z, - frame->targetPoint.x,frame->targetPoint.y,frame->targetPoint.z, - up.x,up.y,up.z); - - for (int axisID=0;axisID<3;axisID++) { - CoordFrameGeometry::Mesh &mesh = coordFrameGeometry.arrow[axisID]; - GLfloat ambient[3], diffuse[3], specular[3]; - for (int j=0;j<3;j++) { - ambient[j] = .2f*mesh.color[j]; - diffuse[j] = .9f*mesh.color[j]; - specular[j] = .2; - } - glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,ambient); - glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,diffuse); - glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,specular); - - glBegin(GL_TRIANGLES); - for (int i=0;iorientation.vx[j]; - matrix[1][j] = frame->orientation.vy[j]; - matrix[2][j] = frame->orientation.vz[j]; - } - glMultMatrixf(&matrix[0][0]); - // glPushMatrix(); - - - - glBegin(GL_QUADS); - for (int x=0;x -#include - -namespace ospray { - namespace viewer { - using namespace ospcommon; - typedef LinearSpace3f linear3f; - - // ================================================================== - //! \brief A QT Widget that allows mouse manipulation of a reference - /*! The Widget works by keeping and manipulating a reference frame - defined by two points (source and target), and a linear - space. THe y axis if the linear space always points from - source point to target point, thereby defining a natural - "towards" direction (such as camera pointing towards lookat - point, or spotlight pointing towards a point it's - illuminating), while source and target define some natural - pair of points associated with this direction (like said - camera origin and camera lookat, or spotlight position and - spotlight target position). By defining obvious directionality - (forward=='from source to target') and orientation ('frame.vy - points 'upwards'') this also allows for obvious manipulations - such as "move X units forward", or "move Y units to the left", - "snap frame to point upwards", etc, which this widget maps to mouse motion. - - How some given mouse motion affects the reference space is - goverennd by the 'interactionMode' that can be set to - different modes such as a 'fly' mode (representing the way a - 3D shooter game might allow a player to navigate in 3D space), - or a 'inspect' mode (rotating the camera around some point of - interest, etc. - */ - // ================================================================== - class QAffineSpaceManipulator : public QGLWidget { - Q_OBJECT; - - public: - //! camera manipulation mode we're in (determines how mouse motion influences the camera pose) - typedef enum { - //! mode in which the camera emulates a person 'flying' through the scene - FLY, - - //! mode in which the camera rotates ardound the center of - //! interest (the lookat). Free rotation will *NOT* apply - //! the lookup vector - ever. - FREE_ROTATION, - - //! mode in which the camera rotates ardound the center of - //! interest (the lookat), but where the user can also - //! change the center of inspection - INSPECT, - } InteractionMode; - - //! the reference frame we're manipulating - struct ReferenceFrame { - //! constructor - ReferenceFrame(); - - //! the position of the point of interest we're rotating around the pivot point - vec3f sourcePoint; - - //! the pivot point we're rotating around (e.g., the 'lookat' point for a camera rotation widget) - vec3f targetPoint; - - //! the normalized coordinate frame, with 'vy' pointing from - //! 'source' to 'target', and 'vz' pointing 'upward' (and 'vx' - //! being right-handed orthogonal to y and z) - linear3f orientation; - - //! look-up vector; can be '(0,0,0)' to indicate that no - //! lookup'ing will be done. if this vector is non-null, we - //! will always snap frame.vz to be co-planar with the plane - //! spanned by 'frame.vy' and 'up' - vec3f upVector; - - //! snap the orientation back to be aligned with the upvector. if upvector is (0,0,0), this fct will do nothing - void snapUp(); - }; - - signals: - //! the signal we're emitting to tell our listener(s) that something has changed - void affineSpaceChanged(QAffineSpaceManipulator *which); - - public: - //! constructor - QAffineSpaceManipulator(QAffineSpaceManipulator::InteractionMode interactionMode = FLY); - - //! switch to requested interaction mode - void setInteractionMode(QAffineSpaceManipulator::InteractionMode interactionMode); - - /*! toggle-up: switch to given axis (x=0,y=1,z=2) as up-vectors for the rotation. - when _already_ in up-vector mode for this axis, switch to negative axis. */ - void toggleUp(int axis); - - //! set the movement speed - void setMoveSpeed(const float moveSpeed) { this->motionSpeed = moveSpeed; } - - //! set camera to a default pose that overlooks the given bounding box - void setDefaultFrame(const box3f &worldBounds); - - //! return the camera - const ReferenceFrame *getFrame() const { return frame; } - - //! return size of widget in pixels - inline vec2i getSize() const { return size; } - - /*! rotate around target point, by given angles */ - void rotateAroundTarget(float angle_x, float angle_y); - - // ------------------------------------------------------- - // QT callbacks - // ------------------------------------------------------- - - //! the QT callback that tells us that we have to redraw - virtual void paintGL(); - //! the QT callback that tells us that the image got resize - virtual void resizeGL(int width, int height); - virtual void mousePressEvent(QMouseEvent * event); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - - - // ------------------------------------------------------- - // overloadable callbacks - // ------------------------------------------------------- - - //! the method our 'paintGL' callback calls to tell the widget to paint its geometry - virtual void redraw() {}; - - //! this method gets called whenever the wiget gets resized - virtual void resize(int width, int height) {}; - - //! this fct gets called when the user triggers a 'pick' event (by clicking with the pick modifier key pressed) - virtual void pick(QMouseEvent *event) - { std::cout << "pcking at " << event->pos().x() << "." << event->pos().y() << std::endl; } - - protected: - - virtual void strafe(QMouseEvent * event); - virtual void rotate(QMouseEvent * event); - virtual void move(QMouseEvent * event); - - //! size of window, in pixels - vec2i size; - //! viewport the user is modifying - ReferenceFrame *frame; - //! camera manipulation mode we're in (determines how mouse motion influences the camera pose) - InteractionMode interactionMode; - //! speed at which the frame gets *moved*. If set to 0, the frame will still rotate, but motion will be disabled. - float motionSpeed; - - QPoint lastMousePos; - //! this stores the orientation at the *START* of a dragging motion - linear3f orientationWhenMouseGotPressed; - }; - - // ======================================================= - //! a widget that allows for rotating a checkered sphere - // ======================================================= - struct QCheckeredBallFrameEditor : public QAffineSpaceManipulator { - QCheckeredBallFrameEditor(); - - // ------------------------------------------------------- - // render widget callbacks - // ------------------------------------------------------- - - virtual void redraw(); - // virtual void resize(int width, int height); - - // ------------------------------------------------------- - // internal state - // ------------------------------------------------------- - protected: - vec3f color_bright, color_dark, color_bg; - }; - - // ======================================================= - //! a widget that allows for rotating a coordiante frame made up of three arrows (in R,G,B) - // ======================================================= - struct QCoordAxisFrameEditor : public QAffineSpaceManipulator { - QCoordAxisFrameEditor(QAffineSpaceManipulator::InteractionMode interactionMode = FLY) - : QAffineSpaceManipulator(interactionMode) - {} - - // ------------------------------------------------------- - // render widget callbacks - // ------------------------------------------------------- - - virtual void redraw(); - - // ------------------------------------------------------- - // internal state - // ------------------------------------------------------- - }; - - - } -} - diff --git a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.cpp b/apps/qtViewer/widgets/lightManipulator/QLightManipulator.cpp deleted file mode 100644 index 103e218603..0000000000 --- a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -// viewer -#include "QLightManipulator.h" -// std -#include -#include - -namespace ospray { - namespace viewer { - - QLightManipulator::QLightManipulator(std::shared_ptr renderer, vec3f up) - { - sgRenderer = renderer; - upVector = up; - - lightInfo.ospLight = ospNewLight(sgRenderer->integrator->ospRenderer, "DirectionalLight"); - lightInfo.color = vec3f(1.f, 1.f, 1.f); - lightInfo.direction = vec3f(0.f, -1.f, 0.f); - lightInfo.intensity = 1.f; - ospCommit(lightInfo.ospLight); - - OSPData lightArray = ospNewData(1, OSP_OBJECT, &lightInfo.ospLight, 0); - ospSetData(sgRenderer->integrator->ospRenderer, "lights", lightArray); - ospCommit(sgRenderer->integrator->ospRenderer); - - //Set up all QT bells and whistles - QVBoxLayout *layout = new QVBoxLayout; - setLayout(layout); - intensityLabel = new QLabel(tr("Intensity:")); - colorLabel = new QLabel(tr("Light Color (RGB):")); - directionVectorLabel = new QLabel(tr("Light Direction Vector (XYZ):")); - upVectorLabel = new QLabel(tr("World Up Vector (XYZ):")); - - layout->addWidget(intensityLabel); - intensityValue = new QLineEdit("1.0"); - layout->addWidget(intensityValue); - - colorRedValue = new QLineEdit("1.0"); - colorGreenValue = new QLineEdit("1.0"); - colorBlueValue = new QLineEdit("1.0"); - layout->addWidget(colorLabel); - layout->addWidget(colorRedValue); - layout->addWidget(colorGreenValue); - layout->addWidget(colorBlueValue); - - directionVectorXValue = new QLineEdit("0.0"); - directionVectorYValue = new QLineEdit("-1.0"); - directionVectorZValue = new QLineEdit("0.0"); - - layout->addWidget(directionVectorLabel); - layout->addWidget(directionVectorXValue); - layout->addWidget(directionVectorYValue); - layout->addWidget(directionVectorZValue); - - //Need to make these read only - //Need to make them update with the up vector of the camera - std::stringstream floatstream; - floatstream << up.x; - upVectorXValue = new QLineEdit(floatstream.str().c_str()); - floatstream.str(""); - floatstream << up.y; - upVectorYValue = new QLineEdit(floatstream.str().c_str()); - floatstream.str(""); - floatstream << up.z; - upVectorZValue = new QLineEdit(floatstream.str().c_str()); - - layout->addWidget(upVectorLabel); - layout->addWidget(upVectorXValue); upVectorXValue->setReadOnly(true); - layout->addWidget(upVectorYValue); upVectorYValue->setReadOnly(true); - layout->addWidget(upVectorZValue); upVectorZValue->setReadOnly(true); - - //Add a spacer taking up the remaining vertical area so that thigns aren't oddly separated. - layout->addStretch(); - - applyButton = new QPushButton("Apply"); - layout->addWidget(applyButton); - - connect(applyButton, SIGNAL(clicked()), this, SLOT(apply())); - - apply(); - } - - QLightManipulator::~QLightManipulator() - { - } - - void QLightManipulator::apply() { - ospSet3f(lightInfo.ospLight, - "direction", - directionVectorXValue->text().toFloat(), - directionVectorYValue->text().toFloat(), - directionVectorZValue->text().toFloat() ); - - ospSet3f(lightInfo.ospLight, - "color", - colorRedValue->text().toFloat(), - colorGreenValue->text().toFloat(), - colorBlueValue->text().toFloat() ); - - ospSet1f(lightInfo.ospLight, - "intensity", - intensityValue->text().toFloat()); - - ospCommit(lightInfo.ospLight); - - emit lightsChanged(); - } - - } // ::viewer -} // ::ospray - diff --git a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h b/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h deleted file mode 100644 index 7d9a2860f5..0000000000 --- a/apps/qtViewer/widgets/lightManipulator/QLightManipulator.h +++ /dev/null @@ -1,112 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "sg/SceneGraph.h" -// ospray public api -#include "ospray/ospray.h" -// qt -#include -//sg -#include "sg/Renderer.h" -// ospcomon -#include "ospcommon/common.h" - -namespace ospray { - namespace viewer { - using namespace ospcommon; - - // ================================================================== - //! \brief A QT Widget that allows mouse manipulation of a reference - /*! The Widget works by keeping and manipulating a reference frame - defined by two points (source and target), and a linear - space. THe y axis if the linear space always points from - source point to target point, thereby defining a natural - "towards" direction (such as camera pointing towards lookat - point, or spotlight pointing towards a point it's - illuminating), while source and target define some natural - pair of points associated with this direction (like said - camera origin and camera lookat, or spotlight position and - spotlight target position). By defining obvious directionality - (forward=='from source to target') and orientation ('frame.vy - points 'upwards'') this also allows for obvious manipulations - such as "move X units forward", or "move Y units to the left", - "snap frame to point upwards", etc, which this widget maps to mouse motion. - - How some given mouse motion affects the reference space is - goverennd by the 'interactionMode' that can be set to - different modes such as a 'fly' mode (representing the way a - 3D shooter game might allow a player to navigate in 3D space), - or a 'inspect' mode (rotating the camera around some point of - interest, etc. - */ - // ================================================================== - struct LightInfo { - OSPLight ospLight; - vec3f color; - vec3f direction; - float intensity; - }; - - class QLightManipulator : public QWidget { - Q_OBJECT; - - - public slots: - void apply(); - signals: - void lightsChanged(); - - public: - //! constructor - QLightManipulator(std::shared_ptr renderer, vec3f up); - QLightManipulator(){} - ~QLightManipulator(); - - protected: - LightInfo lightInfo; - //The renderer we'll be using. Non-owning pointer - std::shared_ptr sgRenderer; - - //All text boxes and labels - QLabel *intensityLabel; - QLineEdit *intensityValue; - - QLabel *colorLabel; - QLineEdit *colorRedValue; - QLineEdit *colorGreenValue; - QLineEdit *colorBlueValue; - - QLabel *directionVectorLabel; - QLineEdit *directionVectorXValue; - QLineEdit *directionVectorYValue; - QLineEdit *directionVectorZValue; - - QLabel *upVectorLabel; - QLineEdit *upVectorXValue; - QLineEdit *upVectorYValue; - QLineEdit *upVectorZValue; - - //Apply any changes - QPushButton *applyButton; - - private: - vec3f upVector; - }; - } -} - diff --git a/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.cpp b/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.cpp deleted file mode 100644 index c98f55e554..0000000000 --- a/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.cpp +++ /dev/null @@ -1,420 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // - -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "QTransferFunctionEditor.h" -#include -// scene graph components -#include "sg/transferFunction/TransferFunction.h" - -namespace ospray { - namespace viewer { - - float QTransferFunctionAlphaEditor::pointPixelRadius = 8.; - float QTransferFunctionAlphaEditor::linePixelWidth = 2.; - - bool comparePointsByX(const ospcommon::vec2f &i, const ospcommon::vec2f &j) - { - return (i.x < j.x); - } - - QTransferFunctionAlphaEditor::QTransferFunctionAlphaEditor() - : selectedPointIndex(-1) - { - // set colorMap image to widget size - colorMapImage = QImage(size(), QImage::Format_ARGB32_Premultiplied); - - // default colorMap color - colorMapImage.fill(QColor::fromRgbF(1,1,1,1).rgb()); - - // default transfer function points - points.push_back(ospcommon::vec2f(0.,0.)); - points.push_back(ospcommon::vec2f(1.,1.)); - } - - void QTransferFunctionAlphaEditor::setColorMapImage(const QImage &image) - { - colorMapImage = image; - - repaint(); - } - - void QTransferFunctionAlphaEditor::paintEvent(QPaintEvent * event) - { - QWidget::paintEvent(event); - - QPainter painter(this); - - painter.setRenderHint(QPainter::Antialiasing, true); - - // colorMap image - // clip to region below the transfer function lines - QPainterPath clipPath; - QPolygonF clipPolygon; - - for(unsigned int i=0; ibutton() == Qt::LeftButton) { - // ------------------------------------------------------- - // LEFT BUTTON - add new point or move existing one - // ------------------------------------------------------- - - // either select an existing point, or create a new one at this location - QPointF widgetClickPoint = event->posF(); - - selectedPointIndex = getSelectedPointIndex(widgetClickPoint); - - if(selectedPointIndex == -1) { - // no point selected, create a new one - ospcommon::vec2f newPoint = widgetPointToPoint(widgetClickPoint); - - // insert into points vector and sort ascending by x - points.push_back(newPoint); - - std::stable_sort(points.begin(), points.end(), comparePointsByX); - - // set selected point index for the new point - selectedPointIndex - = std::find(points.begin(), points.end(), newPoint) - - points.begin(); - - if(selectedPointIndex >= points.size()) - throw std::runtime_error("QTransferFunctionAlphaEditor::mousePressEvent(): " - "selected point index out of range"); - - // emit signal - emit transferFunctionChanged(); - - // trigger repaint - repaint(); - } - } else if(event->button() == Qt::RightButton) { - // ------------------------------------------------------- - // RIGHT BUTTON - remove points - // ------------------------------------------------------- - - // delete a point if selected (except for first and last points!) - QPointF widgetClickPoint = event->posF(); - - selectedPointIndex = getSelectedPointIndex(widgetClickPoint); - - if(selectedPointIndex != -1 /* no mathcing point found */ && - selectedPointIndex != 0 /* found, but first point (can't delete this) */ && - selectedPointIndex != points.size()-1 /* last point, can't delete */) - { - // erase the point - points.erase(points.begin() + selectedPointIndex); - - // trigger repaint - repaint(); - - // emit signal - emit transferFunctionChanged(); - } - - // no point selected now - selectedPointIndex = -1; - } - } - - void QTransferFunctionAlphaEditor::mouseReleaseEvent(QMouseEvent * event) - { - QWidget::mouseReleaseEvent(event); - - selectedPointIndex = -1; - } - - void QTransferFunctionAlphaEditor::mouseMoveEvent(QMouseEvent * event) - { - QWidget::mouseMoveEvent(event); - - if(selectedPointIndex == -1) - // no point selected - return; - - QPointF widgetMousePoint = event->posF(); - ospcommon::vec2f mousePoint = widgetPointToPoint(widgetMousePoint); - - // clamp x value - if(selectedPointIndex == 0) { - // the first point must have x == 0 - mousePoint.x = 0.; - } else if(selectedPointIndex == points.size() - 1) { - // the last point must have x == 1 - mousePoint.x = 1.; - } else { - // intermediate points must have x between their neighbors - mousePoint.x = std::max(mousePoint.x, points[selectedPointIndex - 1].x); - mousePoint.x = std::min(mousePoint.x, points[selectedPointIndex + 1].x); - } - - // clamp y value - mousePoint.y = std::min(mousePoint.y, 1.f); - mousePoint.y = std::max(mousePoint.y, 0.f); - - points[selectedPointIndex] = mousePoint; - - repaint(); - - // emit signal - emit transferFunctionChanged(); - } - - QPointF QTransferFunctionAlphaEditor::pointToWidgetPoint(const ospcommon::vec2f &point) - { - return QPointF(point.x * float(width()), - (1.f - point.y) * float(height())); - } - ospcommon::vec2f QTransferFunctionAlphaEditor::widgetPointToPoint(const QPointF &widgetPoint) - { - return ospcommon::vec2f(float(widgetPoint.x()) / float(width()), - 1.f - float(widgetPoint.y()) / float(height())); - } - - int QTransferFunctionAlphaEditor::getSelectedPointIndex(const QPointF &widgetClickPoint) - { - for(unsigned int i=0; i= 1.) - // return points[points.size()-1].y; - - // // we could make this more efficient... - // for(unsigned int i=0; iaddWidget(colorMapComboBox); - - transferFunctionAlphaEditor = new QTransferFunctionAlphaEditor; - - // opacity transfer function widget - layout->addWidget(transferFunctionAlphaEditor); - - setDefaultColorMaps(); - - // //! The Qt 4.8 documentation says: "by default, for every connection you - // //! make, a signal is emitted". But this isn't happening here (Qt 4.8.5, - // //! Mac OS 10.9.4) so we manually invoke these functions so the transfer - // //! function is fully populated before the first render call. - // //! - // //! Unfortunately, each invocation causes all transfer function fields to - // //! be rewritten on the ISPC side (due to repeated recommits of the OSPRay - // //! transfer function object). - // //! - // setColorMapIndex(0); - // transferFunctionAlphasChanged(); - - - selectColorMap(0); - connect(colorMapComboBox, SIGNAL(currentIndexChanged(int)), - this, SLOT(selectColorMap(int))); - connect(transferFunctionAlphaEditor, SIGNAL(transferFunctionChanged()), - this, SLOT(transferFunctionAlphasChanged())); - - // emit selectColorMap(0); - } - - void QTransferFunctionEditor::addColorMap(const ColorMap *colorMap) - { - colorMaps.push_back(colorMap); - colorMapComboBox->addItem(tr(colorMap->getName().c_str())); - } - - void QTransferFunctionEditor::setDefaultColorMaps() - { - // color maps based on ParaView default color maps - std::vector colors; - - // jet - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 0.562493 )); - colors.push_back(ospcommon::vec3f(0 , 0 , 1 )); - colors.push_back(ospcommon::vec3f(0 , 1 , 1 )); - colors.push_back(ospcommon::vec3f(0.500008 , 1 , 0.500008 )); - colors.push_back(ospcommon::vec3f(1 , 1 , 0 )); - colors.push_back(ospcommon::vec3f(1 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0.500008 , 0 , 0 )); - addColorMap(new ColorMap("Jet", colors)); - - // ice / fire - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0 , 0.120394 , 0.302678 )); - colors.push_back(ospcommon::vec3f(0 , 0.216587 , 0.524575 )); - colors.push_back(ospcommon::vec3f(0.0552529 , 0.345022 , 0.659495 )); - colors.push_back(ospcommon::vec3f(0.128054 , 0.492592 , 0.720287 )); - colors.push_back(ospcommon::vec3f(0.188952 , 0.641306 , 0.792096 )); - colors.push_back(ospcommon::vec3f(0.327672 , 0.784939 , 0.873426 )); - colors.push_back(ospcommon::vec3f(0.60824 , 0.892164 , 0.935546 )); - colors.push_back(ospcommon::vec3f(0.881376 , 0.912184 , 0.818097 )); - colors.push_back(ospcommon::vec3f(0.9514 , 0.835615 , 0.449271 )); - colors.push_back(ospcommon::vec3f(0.904479 , 0.690486 , 0 )); - colors.push_back(ospcommon::vec3f(0.854063 , 0.510857 , 0 )); - colors.push_back(ospcommon::vec3f(0.777096 , 0.330175 , 0.000885023 )); - colors.push_back(ospcommon::vec3f(0.672862 , 0.139086 , 0.00270085 )); - colors.push_back(ospcommon::vec3f(0.508812 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0.299413 , 0.000366217 , 0.000549325 )); - colors.push_back(ospcommon::vec3f(0.0157473 , 0.00332647 , 0 )); - addColorMap(new ColorMap("Ice / Fire", colors)); - - // cool to warm - colors.clear(); - colors.push_back(ospcommon::vec3f(0.231373 , 0.298039 , 0.752941 )); - colors.push_back(ospcommon::vec3f(0.865003 , 0.865003 , 0.865003 )); - colors.push_back(ospcommon::vec3f(0.705882 , 0.0156863 , 0.14902 )); - addColorMap(new ColorMap("Cool to Warm", colors)); - - // blue to red rainbow - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 1 )); - colors.push_back(ospcommon::vec3f(1 , 0 , 0 )); - addColorMap(new ColorMap("Blue to Red Rainbow", colors)); - - // grayscale - colors.clear(); - colors.push_back(ospcommon::vec3f(0.)); - colors.push_back(ospcommon::vec3f(1.)); - addColorMap(new ColorMap("Grayscale", colors)); - } - - - void QTransferFunctionEditor::transferFunctionAlphasChanged() - { - emit transferFunctionChanged(); - updateAlphaMap(); - } - - void QTransferFunctionEditor::selectColorMap(int index) - { - activeColorMap = colorMaps[index]; - - transferFunctionAlphaEditor->setColorMapImage(activeColorMap->getRepresentativeImage()); - - updateColorMap(); - emit transferFunctionChanged(); - } - - QTransferFunctionEditor::ColorMap::ColorMap(const std::string &name, - const std::vector &colors) - : name(name), colors(colors) - {} - - QImage QTransferFunctionEditor::ColorMap::getRepresentativeImage() const - { - int numRows = 1; - - QImage image = QImage(int(colors.size()), numRows, - QImage::Format_ARGB32_Premultiplied); - - for(unsigned int i=0; isetColorMap(activeColorMap->getColors()); - sgNode->commit(); - } - - const std::vector &QTransferFunctionAlphaEditor::getPoints() const - { - return points; - } - - void QTransferFunctionAlphaEditor::setPoints(const std::vector &points) - { - this->points = points; - } - - - void QOSPTransferFunctionEditor::updateAlphaMap() - { - sgNode->setAlphaMap(transferFunctionAlphaEditor->getPoints()); - sgNode->commit(); - } - - } // ::ospray::viewer -} // ::ospray diff --git a/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.h b/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.h deleted file mode 100644 index 10e1b9b226..0000000000 --- a/apps/qtViewer/widgets/transferFunction/QTransferFunctionEditor.h +++ /dev/null @@ -1,178 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -// scene graph -#include "sg/SceneGraph.h" -// ospray, PUBLIC -#include -// stl -#include -#include -// qt -#include -// ospcommon - -namespace ospray { - namespace viewer { - using namespace ospcommon; - - /*! the editor widget to edit a transfer function's alpha - values. This will show the current color map as a background - image, and allow to edit a set of points defining the alpha - values */ - class QTransferFunctionAlphaEditor : public QWidget - { - Q_OBJECT; - - public: - - QTransferFunctionAlphaEditor(); - - /*! set background image for a given color map */ - void setColorMapImage(const QImage &image); - const std::vector &getPoints() const; - void setPoints(const std::vector &points); - signals: - - void transferFunctionChanged(); - public: - - // // get y value based on linear interpolation of the points values for x in [0, 1] - // float getInterpolatedValue(float x); - - protected: - - //! @{ callbacks to intercept qt events - virtual void paintEvent(QPaintEvent * event); - virtual void mousePressEvent(QMouseEvent * event); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - //! @} - - // transform normalized coordinates to widget coordinates - QPointF pointToWidgetPoint(const ospcommon::vec2f &point); - - // transform widget coordinates to normalized coordinates - ospcommon::vec2f widgetPointToPoint(const QPointF &widgetPoint); - - // get the index of the selected point based on the clicked point in widget coordinates - // returns -1 if no selected point - int getSelectedPointIndex(const QPointF &widgetClickPoint); - - // color map image used as the widget background - QImage colorMapImage; - - //! the points that define the transfer function, in normalize - //! coordinates ([0,0], [1,1]) - std::vector points; - - //! currently selected point - int selectedPointIndex; - - //! \{ drawing properties - static float pointPixelRadius; - static float linePixelWidth; - //! \} - - }; - - - /*! \brief simple editor widget that allows for editing a tranfer fcuntion's color map and alpha mapping */ - class QTransferFunctionEditor : public QWidget - { - Q_OBJECT - - /*! color map component of the transfer function. */ - class ColorMap - { - public: - - //! construct a new color map from given colors - ColorMap(const std::string &name, const std::vector &colors); - - //! return the name of this color map - inline std::string getName() const { return name; }; - - /*! return a QImage that represents the color map (this image - can be used in the transfer function editor */ - QImage getRepresentativeImage() const; - - //! query function that returns the current color map (to be - //! used in a scene graph node, for example - std::vector getColors() const { return colors; }; - - protected: - - const std::string name; - const std::vector colors; - }; - - public: - // constructor - QTransferFunctionEditor(); - - // add a new color map to the list of selectable color maps - void addColorMap(const ColorMap *colorMap); - void setOpacityPoints(const std::vector &points) - { transferFunctionAlphaEditor->setPoints(points); } - - signals: - void transferFunctionChanged(); - public slots: - void transferFunctionAlphasChanged(); - protected slots: - - void selectColorMap(int index); - - protected: - - const ColorMap *activeColorMap; - void setDefaultColorMaps(); - - // color maps - std::vector colorMaps; - - //! the combo box we use for selecting the color map - QComboBox *colorMapComboBox; - - // transfer function widget for opacity - QTransferFunctionAlphaEditor *transferFunctionAlphaEditor; - - virtual void updateColorMap() {}; - virtual void updateAlphaMap() {}; - }; - - /*! a transfer fucntion editor that automatically updates an - ospray::sg::TransferFunction node */ - struct QOSPTransferFunctionEditor : public QTransferFunctionEditor - { - QOSPTransferFunctionEditor(std::shared_ptr sgNode) - : sgNode(sgNode) - { - // sgNode->render(); - } - - virtual void updateColorMap(); - virtual void updateAlphaMap(); - - //! the node we are editing - std::shared_ptr sgNode; - }; - - } -} diff --git a/apps/volumeViewer/CMakeLists.txt b/apps/volumeViewer/CMakeLists.txt deleted file mode 100644 index ef1d297c1b..0000000000 --- a/apps/volumeViewer/CMakeLists.txt +++ /dev/null @@ -1,83 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2017 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/ospray/include) - -ADD_SUBDIRECTORY(glUtil) - -# setup dependencies -SET(LIBS ${LIBS} ospray) -SET(LIBS ${LIBS} ospray_common) -SET(LIBS ${LIBS} ospray_importer) -SET(LIBS ${LIBS} ospray_opengl_util) -SET(LIBS ${LIBS} ospray_tfn) -SET(LIBS ${LIBS} ospray_minisg) -SET(LIBS ${LIBS} ospray_commandline) - -# link optional loaders-based modules if built -IF (OSPRAY_MODULE_SEISMIC) - SET(LIBS ${LIBS} ospray_module_seismic) -ENDIF (OSPRAY_MODULE_SEISMIC) - -FIND_PACKAGE(Qt4 REQUIRED QtGui QtOpenGL) -SET(QT_USE_QTOPENGL TRUE) -INCLUDE(${QT_USE_FILE}) -SET(LIBS ${LIBS} Qt4::QtGui Qt4::QtOpenGL) - -FIND_PACKAGE(OpenGL REQUIRED) -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIRS}) -SET(LIBS ${LIBS} ${OPENGL_LIBRARIES} ${TBB_LIBRARY_MALLOC} ${TBB_LIBRARY}) - -SET(SRCS ${SRCS} - main.cpp - ColorMap.cpp - IsosurfaceEditor.cpp - IsovalueWidget.cpp - LightEditor.cpp - LinearTransferFunctionWidget.cpp - OpenGLAnnotationRenderer.cpp - PreferencesDialog.cpp - ProbeWidget.cpp - QOSPRayWindow.cpp - SliceEditor.cpp - SliceWidget.cpp - TransferFunctionEditor.cpp - VolumeViewer.cpp -) - -SET(MOC_HEADERS - IsosurfaceEditor.h - IsovalueWidget.h - LightEditor.h - LinearTransferFunctionWidget.h - OpenGLAnnotationRenderer.h - PreferencesDialog.h - ProbeWidget.h - QOSPRayWindow.h - SliceEditor.h - SliceWidget.h - TransferFunctionEditor.h - VolumeViewer.h -) - -QT4_WRAP_CPP(MOC_OUTFILES ${MOC_HEADERS}) - -OSPRAY_CREATE_APPLICATION(ospVolumeViewer - ${SRCS} - ${MOC_OUTFILES} -LINK - ${LIBS} -) diff --git a/apps/volumeViewer/ColorMap.cpp b/apps/volumeViewer/ColorMap.cpp deleted file mode 100644 index c670f5b762..0000000000 --- a/apps/volumeViewer/ColorMap.cpp +++ /dev/null @@ -1,50 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "ColorMap.h" - -ColorMap::ColorMap(std::string name, std::vector colors) -{ - this->name = name; - this->colors = colors; -} - -std::string ColorMap::getName() const -{ - return name; -} - -std::vector ColorMap::getColors() const -{ - return colors; -} - -QImage ColorMap::getImage() -{ - int numRows = 1; - - QImage image(int(colors.size()), numRows, QImage::Format_ARGB32_Premultiplied); - - for(unsigned int i=0; idataValueRange = dataValueRange; - - for (unsigned int i=0; isetDataValueRange(dataValueRange); -} - -void IsosurfaceEditor::apply() -{ - std::vector isovalues; - - for (unsigned int i=0; igetIsovalueEnabled()) - isovalues.push_back(isovalueWidgets[i]->getIsovalue()); - } - - emit(isovaluesChanged(isovalues)); -} - -void IsosurfaceEditor::addIsovalue() -{ - IsovalueWidget *isovalueWidget = new IsovalueWidget(this); - - isovalueWidgets.push_back(isovalueWidget); - layout.addWidget(isovalueWidget); - - isovalueWidget->setDataValueRange(dataValueRange); -} diff --git a/apps/volumeViewer/IsovalueWidget.cpp b/apps/volumeViewer/IsovalueWidget.cpp deleted file mode 100644 index f904e5cfd9..0000000000 --- a/apps/volumeViewer/IsovalueWidget.cpp +++ /dev/null @@ -1,104 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "IsovalueWidget.h" -#include "IsosurfaceEditor.h" - -IsovalueWidget::IsovalueWidget(IsosurfaceEditor *isosurfaceEditor) : dataRangeSet(false) -{ - QHBoxLayout * layout = new QHBoxLayout(); - layout->setSizeConstraint(QLayout::SetMinimumSize); - setLayout(layout); - - layout->addWidget(&isovalueCheckBox); - - // Isovalue slider, defaults to median value in range. - isovalueSlider.setValue(int(0.5f * (isovalueSlider.minimum() + isovalueSlider.maximum()))); - isovalueSlider.setOrientation(Qt::Horizontal); - - layout->addWidget(&isovalueSlider); - - // Isovalue spin box. - isovalueSpinBox.setDecimals(6); - isovalueSpinBox.setValue(0.5f); - layout->addWidget(&isovalueSpinBox); - - // Connect signals and slots. - connect(&isovalueCheckBox, SIGNAL(toggled(bool)), this, SLOT(apply())); - connect(&isovalueSlider, SIGNAL(valueChanged(int)), this, SLOT(apply())); - connect(&isovalueSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - - connect(this, SIGNAL(isovalueChanged()), isosurfaceEditor, SLOT(apply())); -} - -void IsovalueWidget::setDataValueRange(ospcommon::vec2f dataValueRange) -{ - this->dataValueRange = dataValueRange; - - if(!dataRangeSet) { - isovalueSpinBox.blockSignals(true); - isovalueSpinBox.setRange(dataValueRange.x, dataValueRange.y); - isovalueSpinBox.blockSignals(false); - - // Get isovalue based on slider position. - float sliderPosition = float(isovalueSlider.value() - isovalueSlider.minimum()) / float(isovalueSlider.maximum() - isovalueSlider.minimum()); - float isovalue = dataValueRange.x + sliderPosition * (dataValueRange.y - dataValueRange.x); - - // Update spin box value. - isovalueSpinBox.setValue(isovalue); - - dataRangeSet = true; - } - else { - - // Expand the spin box range if the range has already been set (for appropriate time series behavior). - isovalueSpinBox.setRange(std::min((double)dataValueRange.x, isovalueSpinBox.minimum()), std::max((double)dataValueRange.y, isovalueSpinBox.maximum())); - - // Update slider position for the new range. - float isovalue = isovalueSpinBox.value(); - - float sliderPosition = float(isovalueSlider.minimum()) + (isovalue - dataValueRange.x) / (dataValueRange.y - dataValueRange.x) * float(isovalueSlider.maximum() - isovalueSlider.minimum()); - - isovalueSlider.blockSignals(true); - isovalueSlider.setValue(int(sliderPosition)); - isovalueSlider.blockSignals(false); - } - - apply(); -} - -void IsovalueWidget::apply() -{ - if(sender() == &isovalueSlider) { - float sliderPosition = float(isovalueSlider.value() - isovalueSlider.minimum()) / float(isovalueSlider.maximum() - isovalueSlider.minimum()); - float isovalue = dataValueRange.x + sliderPosition * (dataValueRange.y - dataValueRange.x); - - isovalueSpinBox.blockSignals(true); - isovalueSpinBox.setValue(isovalue); - isovalueSpinBox.blockSignals(false); - } - else if(sender() == &isovalueSpinBox) { - float isovalue = isovalueSpinBox.value(); - - float sliderPosition = float(isovalueSlider.minimum()) + (isovalue - dataValueRange.x) / (dataValueRange.y - dataValueRange.x) * float(isovalueSlider.maximum() - isovalueSlider.minimum()); - - isovalueSlider.blockSignals(true); - isovalueSlider.setValue(int(sliderPosition)); - isovalueSlider.blockSignals(false); - } - - emit isovalueChanged(); -} diff --git a/apps/volumeViewer/IsovalueWidget.h b/apps/volumeViewer/IsovalueWidget.h deleted file mode 100644 index 16f2fe5a62..0000000000 --- a/apps/volumeViewer/IsovalueWidget.h +++ /dev/null @@ -1,57 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include "ospcommon/vec.h" -#include - -class IsosurfaceEditor; - -class IsovalueWidget : public QWidget -{ -Q_OBJECT - -public: - - IsovalueWidget(IsosurfaceEditor *isosurfaceEditor); - - bool getIsovalueEnabled() { return isovalueCheckBox.isChecked(); } - float getIsovalue() { return isovalueSpinBox.value(); } - - void setDataValueRange(ospcommon::vec2f dataValueRange); - -signals: - - void isovalueChanged(); - -protected slots: - - void apply(); - -protected: - - ospcommon::vec2f dataValueRange; - - //! Indicates if the data value range has been set; we don't automatically set the isovalue after the first time it's set. - bool dataRangeSet; - - QCheckBox isovalueCheckBox; - QSlider isovalueSlider; - QDoubleSpinBox isovalueSpinBox; - -}; diff --git a/apps/volumeViewer/LightEditor.cpp b/apps/volumeViewer/LightEditor.cpp deleted file mode 100644 index b73c20814f..0000000000 --- a/apps/volumeViewer/LightEditor.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "LightEditor.h" - -LightEditor::LightEditor(OSPLight ambientLight, OSPLight directionalLight) : ambientLight(ambientLight), - directionalLight(directionalLight) -{ - // Make sure we have existing lights. - if (!ambientLight || !directionalLight) - throw std::runtime_error("LightEditor: must be constructed with an existing lights"); - - // Setup UI elments. - QVBoxLayout * layout = new QVBoxLayout(); - setLayout(layout); - - // Form layout. - QWidget * formWidget = new QWidget(); - QFormLayout * formLayout = new QFormLayout(); - formWidget->setLayout(formLayout); - layout->addWidget(formWidget); - - // Ambient light intensity. - ambientLightIntensitySpinBox.setRange(0.0, 3.0); - ambientLightIntensitySpinBox.setSingleStep(0.01); - formLayout->addRow("Ambient light intensity", &ambientLightIntensitySpinBox); - connect(&ambientLightIntensitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(ambientLightChanged())); - - // Directional light intensity. - directionalLightIntensitySpinBox.setRange(0.0, 3.0); - directionalLightIntensitySpinBox.setSingleStep(0.01); - formLayout->addRow("Directional light intensity", &directionalLightIntensitySpinBox); - connect(&directionalLightIntensitySpinBox, SIGNAL(valueChanged(double)), this, SLOT(directionalLightChanged())); - - // Directional light: azimuth and elevation angles for direction. - directionalLightAzimuthSlider.setOrientation(Qt::Horizontal); - directionalLightElevationSlider.setOrientation(Qt::Horizontal); - formLayout->addRow("Directional light azimuth", &directionalLightAzimuthSlider); - formLayout->addRow("Directional light elevation", &directionalLightElevationSlider); - connect(&directionalLightAzimuthSlider, SIGNAL(valueChanged(int)), this, SLOT(directionalLightChanged())); - connect(&directionalLightElevationSlider, SIGNAL(valueChanged(int)), this, SLOT(directionalLightChanged())); - - // Set default light parameters. - ambientLightIntensitySpinBox.setValue(0.1); //doesn't seem to fire if it's 0 first - ambientLightIntensitySpinBox.setValue(0.2); - - directionalLightIntensitySpinBox.setValue(1.7); - directionalLightAzimuthSlider.setValue(0.8 * (directionalLightAzimuthSlider.minimum() + directionalLightAzimuthSlider.maximum())); // 45 degrees azimuth - directionalLightElevationSlider.setValue(0.65 * (directionalLightElevationSlider.minimum() + directionalLightElevationSlider.maximum())); // 45 degrees elevation -} - -void LightEditor::ambientLightChanged() -{ - ospSet1f(ambientLight, "intensity", float(ambientLightIntensitySpinBox.value()*3.14)); - ospCommit(ambientLight); - emit lightsChanged(); -} - -void LightEditor::directionalLightChanged() -{ - ospSet1f(directionalLight, "intensity", float(directionalLightIntensitySpinBox.value()*3.14)); - - // Get alpha value in [-180, 180] degrees. - float alpha = -180.0f + float(directionalLightAzimuthSlider.value() - directionalLightAzimuthSlider.minimum()) / float(directionalLightAzimuthSlider.maximum() - directionalLightAzimuthSlider.minimum()) * 360.0f; - - // Get beta value in [-90, 90] degrees. - float beta = -90.0f + float(directionalLightElevationSlider.value() - directionalLightElevationSlider.minimum()) / float(directionalLightElevationSlider.maximum() - directionalLightElevationSlider.minimum()) * 180.0f; - - // Compute unit vector. - float lightX = cos(alpha * M_PI/180.0f) * cos(beta * M_PI/180.0f); - float lightY = sin(alpha * M_PI/180.0f) * cos(beta * M_PI/180.0f); - float lightZ = sin(beta * M_PI/180.0f); - - // Update OSPRay light direction. - ospSet3f(directionalLight, "direction", lightX, lightY, lightZ); - - // Commit and emit signal. - ospCommit(directionalLight); - emit lightsChanged(); -} diff --git a/apps/volumeViewer/LightEditor.h b/apps/volumeViewer/LightEditor.h deleted file mode 100644 index fe466d38e2..0000000000 --- a/apps/volumeViewer/LightEditor.h +++ /dev/null @@ -1,68 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include -#include -#include - -class LightEditor : public QWidget { - -Q_OBJECT - -public: - - LightEditor(OSPLight ambientLight, OSPLight directionalLight); - - friend std::ostream& operator<< (std::ostream& out, LightEditor& v) - { - return out << "--ambientLight " << v.ambientLightIntensitySpinBox.value() << " --directionalLight " - << v.directionalLightIntensitySpinBox.value() << " " << v.directionalLightAzimuthSlider.value() - << " " << v.directionalLightElevationSlider.value(); - } - void setAmbientLightIntensity(float v) { ambientLightIntensitySpinBox.setValue(v); } - void setDirectionalLightIntensity(float v) { directionalLightIntensitySpinBox.setValue(v); } - void setDirectionalLightAzimuth(float v) { directionalLightAzimuthSlider.setValue(v); } - void setDirectionalLightElevation(float v) { directionalLightElevationSlider.setValue(v); } - -signals: - - void lightsChanged(); - -protected slots: - - void ambientLightChanged(); - void directionalLightChanged(); - -public: - - //! OSPRay ambient light. - OSPLight ambientLight; - - //! OSPRay directional light. - OSPLight directionalLight; - - // Ambient light UI elements. - QDoubleSpinBox ambientLightIntensitySpinBox; - - // Directional light UI elements. - QDoubleSpinBox directionalLightIntensitySpinBox; - QSlider directionalLightAzimuthSlider; - QSlider directionalLightElevationSlider; - -}; diff --git a/apps/volumeViewer/LinearTransferFunctionWidget.cpp b/apps/volumeViewer/LinearTransferFunctionWidget.cpp deleted file mode 100644 index 7f7e7dbfd7..0000000000 --- a/apps/volumeViewer/LinearTransferFunctionWidget.cpp +++ /dev/null @@ -1,255 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "LinearTransferFunctionWidget.h" -#include - -float LinearTransferFunctionWidget::pointPixelRadius = 8.; -float LinearTransferFunctionWidget::linePixelWidth = 2.; - -bool LinearTransferFunctionWidget::updateDuringChange = true; - -bool comparePointsByX(const QPointF &i, const QPointF &j) { - return (i.x() < j.x()); -} - -LinearTransferFunctionWidget::LinearTransferFunctionWidget() : selectedPointIndex(-1) -{ - // Set background image to widget size - backgroundImage = QImage(size(), QImage::Format_ARGB32_Premultiplied); - - // Default background color - backgroundImage.fill(QColor::fromRgbF(1,1,1,1).rgb()); - - // Default transfer function points - points.push_back(QPointF(0.,0.)); - points.push_back(QPointF(1.,1.)); -} - -std::vector LinearTransferFunctionWidget::getInterpolatedValuesOverInterval(const unsigned int &numValues) -{ - std::vector yValues; - - for(unsigned int i=0; ibutton() == Qt::LeftButton) { - - // Either select an existing point, or create a new one at this location. - QPointF widgetClickPoint = event->posF(); - - selectedPointIndex = getSelectedPointIndex(widgetClickPoint); - - if(selectedPointIndex == -1) { - - // No point selected, create a new one. - QPointF newPoint = widgetPointToPoint(widgetClickPoint); - - // Insert into points vector and sort ascending by x. - points.push_back(newPoint); - - std::stable_sort(points.begin(), points.end(), comparePointsByX); - - // Set selected point index for the new point. - selectedPointIndex = std::find(points.begin(), points.end(), newPoint) - points.begin(); - - if(selectedPointIndex >= points.size()) - throw std::runtime_error("LinearTransferFunctionWidget::mousePressEvent(): selected point index out of range"); - - // Trigger repaint. - repaint(); - } - } - else if(event->button() == Qt::RightButton) { - - // Delete a point if selected (except for first and last points!). - QPointF widgetClickPoint = event->posF(); - - selectedPointIndex = getSelectedPointIndex(widgetClickPoint); - - if(selectedPointIndex != -1 && selectedPointIndex != 0 && selectedPointIndex != points.size() - 1) { - - points.erase(points.begin() + selectedPointIndex); - - // Trigger repaint. - repaint(); - - // Emit signal. - emit updated(); - } - - selectedPointIndex = -1; - } -} - -void LinearTransferFunctionWidget::mouseReleaseEvent(QMouseEvent * event) -{ - QWidget::mouseReleaseEvent(event); - - // Emit signal if we were manipulating a point. - if(selectedPointIndex != -1) { - - selectedPointIndex = -1; - - emit updated(); - } -} - -void LinearTransferFunctionWidget::mouseMoveEvent(QMouseEvent * event) -{ - QWidget::mouseMoveEvent(event); - - if(selectedPointIndex != -1) { - QPointF widgetMousePoint = event->posF(); - QPointF mousePoint = widgetPointToPoint(widgetMousePoint); - - // Clamp x value. - if(selectedPointIndex == 0) { - - // the first point must have x == 0. - mousePoint.rx() = 0.; - } - else if(selectedPointIndex == points.size() - 1) { - - // The last point must have x == 1. - mousePoint.rx() = 1.; - } - else { - - // Intermediate points must have x between their neighbors. - mousePoint.rx() = std::max(mousePoint.x(), points[selectedPointIndex - 1].x()); - mousePoint.rx() = std::min(mousePoint.x(), points[selectedPointIndex + 1].x()); - } - - // Clamp y value. - mousePoint.ry() = std::min(mousePoint.y(), 1.); - mousePoint.ry() = std::max(mousePoint.y(), 0.); - - points[selectedPointIndex] = mousePoint; - - repaint(); - - if(updateDuringChange == true) - emit updated(); - } -} - -QPointF LinearTransferFunctionWidget::pointToWidgetPoint(const QPointF &point) -{ - return QPointF(point.x() * float(width()), (1. - point.y()) * float(height())); -} - -QPointF LinearTransferFunctionWidget::widgetPointToPoint(const QPointF &widgetPoint) -{ - return QPointF(float(widgetPoint.x()) / float(width()), 1. - float(widgetPoint.y()) / float(height())); -} - -int LinearTransferFunctionWidget::getSelectedPointIndex(const QPointF &widgetClickPoint) -{ - for(unsigned int i=0; i= 1.) - return points[points.size() - 1].y(); - - // We could make this more efficient... - for(unsigned int i=0; i -#include -#include - -class LinearTransferFunctionWidget : public QWidget -{ - Q_OBJECT - - public: - - LinearTransferFunctionWidget(); - - //! Minimum size of the widget. - virtual QSize minimumSizeHint() const { return QSize(240, 180); } - - //! Get numValues of the transfer function regularly spaced over the entire interval [0, 1]. - std::vector getInterpolatedValuesOverInterval(const unsigned int &numValues); - - //! Get transfer function control points. - QVector getPoints() { return points; } - - //! Set transfer function control points. - void setPoints(const QVector &points) { this->points = points; repaint(); emit(updated()); } - - //! Set background image for the widget (for example to show a color map). - void setBackgroundImage(const QImage &image); - -signals: - - //! Signal emitted when this widget is updated. - void updated(); - -protected: - - virtual void paintEvent(QPaintEvent * event); - virtual void mousePressEvent(QMouseEvent * event); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - - //! Transform normalized coordinates to widget coordinates. - QPointF pointToWidgetPoint(const QPointF &point); - - //! Transform widget coordinates to normalized coordinates. - QPointF widgetPointToPoint(const QPointF &widgetPoint); - - //! Get the index of the selected point based on the clicked point in widget coordinates; returns -1 if no selected point. - int getSelectedPointIndex(const QPointF &widgetClickPoint); - - //! Get y value based on linear interpolation of the points values for x in [0, 1]. - float getInterpolatedValue(float x); - - //! Background image for the widget (for example to show a color map). - QImage backgroundImage; - - //! The points that define the transfer function, in normalize coordinates ([0,1], [0,1]). - QVector points; - - //! Currently selected point. - int selectedPointIndex; - - //! Drawing properties: point pixel radius. - static float pointPixelRadius; - - //! Drawing properties: line pixel width. - static float linePixelWidth; - - //! If true, will emit update signals during transfer function change; otherwise, will wait until change is complete (mouse release). - static bool updateDuringChange; -}; diff --git a/apps/volumeViewer/OpenGLAnnotationRenderer.cpp b/apps/volumeViewer/OpenGLAnnotationRenderer.cpp deleted file mode 100644 index dc40c5853b..0000000000 --- a/apps/volumeViewer/OpenGLAnnotationRenderer.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "VolumeViewer.h" -#include "OpenGLAnnotationRenderer.h" - -#ifdef __APPLE__ - #include -#else - #include -#endif - -void OpenGLAnnotationRenderer::render() -{ - glPushAttrib(GL_ENABLE_BIT); - glEnable(GL_DEPTH_TEST); - - // render volume bounding box - ospcommon::box3f boundingBox = volumeViewer->getBoundingBox(); - - ospcommon::vec3f size(boundingBox.upper - boundingBox.lower); - - glPushMatrix(); - glTranslatef(boundingBox.lower.x, boundingBox.lower.y, boundingBox.lower.z); - glScalef(size.x, size.y, size.z); - - glColor4f(0.5f, 0.5f, 0.5f, 1.f); - - glBegin(GL_LINES); - - float v1[3], v2[3]; - - for (int dim1=0; dim1<3; dim1++) { - int dim2 = (dim1+1) % 3; - int dim3 = (dim1+2) % 3; - - for (int i=0; i<=1; i++) { - for (int j=0; j<=1; j++) { - v1[dim1] = 0.f; - v2[dim1] = 1.f; - v1[dim2] = v2[dim2] = i; - v1[dim3] = v2[dim3] = j; - - glVertex3fv(v1); - glVertex3fv(v2); - } - } - } - - glEnd(); - - glPopMatrix(); - - glPopAttrib(); -} diff --git a/apps/volumeViewer/PreferencesDialog.cpp b/apps/volumeViewer/PreferencesDialog.cpp deleted file mode 100644 index d82b10bd5f..0000000000 --- a/apps/volumeViewer/PreferencesDialog.cpp +++ /dev/null @@ -1,217 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "VolumeViewer.h" -#include "PreferencesDialog.h" - -PreferencesDialog::PreferencesDialog(VolumeViewer *volumeViewer, ospcommon::box3f boundingBox) : QDialog(volumeViewer), volumeViewer(volumeViewer) -{ - setWindowTitle("Preferences"); - - QFormLayout *formLayout = new QFormLayout(); - setLayout(formLayout); - - // render annotations flag - QCheckBox *renderAnnotationsEnabledCheckBox = new QCheckBox(); - connect(renderAnnotationsEnabledCheckBox, SIGNAL(toggled(bool)), volumeViewer, SLOT(setRenderAnnotationsEnabled(bool))); - formLayout->addRow("Render annotations", renderAnnotationsEnabledCheckBox); - - // subsampling during interaction flag - QCheckBox *subsamplingInteractionEnabledCheckBox = new QCheckBox(); - connect(subsamplingInteractionEnabledCheckBox, SIGNAL(toggled(bool)), volumeViewer, SLOT(setSubsamplingInteractionEnabled(bool))); - formLayout->addRow("Subsample during interaction", subsamplingInteractionEnabledCheckBox); - - // gradient shading flag - gradientShadingEnabledCheckBox = new QCheckBox(); - connect(gradientShadingEnabledCheckBox, SIGNAL(toggled(bool)), volumeViewer, SLOT(setGradientShadingEnabled(bool))); - formLayout->addRow("Volume gradient shading (lighting)", gradientShadingEnabledCheckBox); - - adaptiveSamplingCB = new QCheckBox(); - connect(adaptiveSamplingCB, SIGNAL(toggled(bool)), volumeViewer, SLOT(setAdaptiveSampling(bool))); - formLayout->addRow("Adaptive sampling", adaptiveSamplingCB); - - preIntegrationCB = new QCheckBox(); - connect(preIntegrationCB, SIGNAL(toggled(bool)), volumeViewer, SLOT(setPreIntegration(bool))); - formLayout->addRow("PreIntegration", preIntegrationCB); - - QCheckBox *singleShadeCB = new QCheckBox(); - connect(singleShadeCB, SIGNAL(toggled(bool)), volumeViewer, SLOT(setSingleShade(bool))); - formLayout->addRow("Single Shading Calculation", singleShadeCB); - - shadowsCB = new QCheckBox(); - connect(shadowsCB, SIGNAL(toggled(bool)), volumeViewer, SLOT(setShadows(bool))); - formLayout->addRow("Shadows", shadowsCB); - - planeCB = new QCheckBox(); - connect(planeCB, SIGNAL(toggled(bool)), volumeViewer, SLOT(setPlane(bool))); - formLayout->addRow("Plane", planeCB); - - - QDoubleSpinBox *adaptiveScalarSB = new QDoubleSpinBox(); - adaptiveScalarSB->setDecimals(4); - adaptiveScalarSB->setRange(1.0, 1000.0); - adaptiveScalarSB->setSingleStep(1.0); - connect(adaptiveScalarSB, SIGNAL(valueChanged(double)), volumeViewer, SLOT(setAdaptiveScalar(double))); - formLayout->addRow("Adaptive scalar", adaptiveScalarSB); - - adaptiveMaxSamplingRateSB = new QDoubleSpinBox(); - adaptiveMaxSamplingRateSB->setDecimals(3); - adaptiveMaxSamplingRateSB->setRange(0.01, 16.0); - adaptiveMaxSamplingRateSB->setSingleStep(0.01); - connect(adaptiveMaxSamplingRateSB, SIGNAL(valueChanged(double)), volumeViewer, SLOT(setAdaptiveMaxSamplingRate(double))); - formLayout->addRow("Adaptive max sampling rate", adaptiveMaxSamplingRateSB); - - QDoubleSpinBox *adaptiveBacktrackSB = new QDoubleSpinBox(); - adaptiveBacktrackSB->setDecimals(4); - adaptiveBacktrackSB->setRange(0.0001, 2.0f); - adaptiveBacktrackSB->setSingleStep(0.0001); - connect(adaptiveBacktrackSB, SIGNAL(valueChanged(double)), volumeViewer, SLOT(setAdaptiveBacktrack(double))); - formLayout->addRow("Adaptive backtrack", adaptiveBacktrackSB); - - // sampling rate selection - samplingRateSpinBox = new QDoubleSpinBox(); - samplingRateSpinBox->setDecimals(3); - samplingRateSpinBox->setRange(0.01, 16.0); - samplingRateSpinBox->setSingleStep(0.01); - connect(samplingRateSpinBox, SIGNAL(valueChanged(double)), volumeViewer, SLOT(setSamplingRate(double))); - formLayout->addRow("Sampling rate", samplingRateSpinBox); - - QDoubleSpinBox *aoWeightSB = new QDoubleSpinBox(); - aoWeightSB->setDecimals(3); - aoWeightSB->setRange(0.0, 4.0); - aoWeightSB->setSingleStep(0.1); - connect(aoWeightSB, SIGNAL(valueChanged(double)), volumeViewer, SLOT(setAOWeight(double))); - formLayout->addRow("AOWeight", aoWeightSB); - - aoSamplesSB = new QSpinBox(); - aoSamplesSB->setRange(0, 64); - connect(aoSamplesSB, SIGNAL(valueChanged(int)), volumeViewer, SLOT(setAOSamples(int))); - formLayout->addRow("AOSamples", aoSamplesSB); - - sppSB = new QSpinBox(); - sppSB->setRange(-4, 2048); - connect(sppSB, SIGNAL(valueChanged(int)), volumeViewer, SLOT(setSPP(int))); - formLayout->addRow("spp", sppSB); - - // volume clipping box - for(size_t i=0; i<6; i++) { - volumeClippingBoxSpinBoxes.push_back(new QDoubleSpinBox()); - - volumeClippingBoxSpinBoxes[i]->setDecimals(3); - volumeClippingBoxSpinBoxes[i]->setRange(boundingBox.lower[i % 3], boundingBox.upper[i % 3]); - volumeClippingBoxSpinBoxes[i]->setSingleStep(0.01 * (boundingBox.upper[i % 3] - boundingBox.lower[i % 3])); - - if(i < 3) - volumeClippingBoxSpinBoxes[i]->setValue(boundingBox.lower[i % 3]); - else - volumeClippingBoxSpinBoxes[i]->setValue(boundingBox.upper[i % 3]); - - connect(volumeClippingBoxSpinBoxes[i], SIGNAL(valueChanged(double)), this, SLOT(updateVolumeClippingBox())); - } - - QWidget *volumeClippingBoxLowerWidget = new QWidget(); - QHBoxLayout *hBoxLayout = new QHBoxLayout(); - volumeClippingBoxLowerWidget->setLayout(hBoxLayout); - - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[0]); - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[1]); - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[2]); - - formLayout->addRow("Volume clipping box: lower", volumeClippingBoxLowerWidget); - - QWidget *volumeClippingBoxUpperWidget = new QWidget(); - hBoxLayout = new QHBoxLayout(); - volumeClippingBoxUpperWidget->setLayout(hBoxLayout); - - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[3]); - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[4]); - hBoxLayout->addWidget(volumeClippingBoxSpinBoxes[5]); - - formLayout->addRow("Volume clipping box: upper", volumeClippingBoxUpperWidget); - - // set default values. this will trigger signal / slot executions. - subsamplingInteractionEnabledCheckBox->setChecked(false); - gradientShadingEnabledCheckBox->setChecked(volumeViewer->getGradientShadingEnabled()); - singleShadeCB->setChecked(true); - adaptiveSamplingCB->setChecked(volumeViewer->getAdaptiveSampling()); - preIntegrationCB->setChecked(volumeViewer->getPreIntegration()); - shadowsCB->setChecked(volumeViewer->getShadows()); - planeCB->setChecked(volumeViewer->getPlane()); - adaptiveScalarSB->setValue(15.f); - adaptiveMaxSamplingRateSB->setValue(volumeViewer->getAdaptiveMaxSamplingRate()); - adaptiveBacktrackSB->setValue(0.02f); - samplingRateSpinBox->setValue(volumeViewer->getSamplingRate()); - aoWeightSB->setValue(0.4f); - aoSamplesSB->setValue(volumeViewer->getAOSamples()); - sppSB->setValue(volumeViewer->getSPP()); -} - -void PreferencesDialog::setGradientShadingEnabled(bool v) -{ - if (gradientShadingEnabledCheckBox) - gradientShadingEnabledCheckBox->setChecked(v); -} - -void PreferencesDialog::setSPP(int v) -{ - sppSB->setValue(v); -} - -void PreferencesDialog::setAOSamples(int v) -{ - aoSamplesSB->setValue(v); -} - -void PreferencesDialog::setPreIntegration(bool v) -{ - preIntegrationCB->setChecked(v); -} - -void PreferencesDialog::setShadows(bool v) -{ - shadowsCB->setChecked(v); -} - -void PreferencesDialog::setAdaptiveSampling(bool v) -{ - adaptiveSamplingCB->setChecked(v); -} - -void PreferencesDialog::setPlane(bool v) -{ - planeCB->setChecked(v); -} - -void PreferencesDialog::setSamplingRate(float v) -{ - samplingRateSpinBox->setValue(v); -} -void PreferencesDialog::setAdaptiveMaxSamplingRate(float v) -{ - adaptiveMaxSamplingRateSB->setValue(v); -} - -void PreferencesDialog::updateVolumeClippingBox() -{ - ospcommon::vec3f lower(volumeClippingBoxSpinBoxes[0]->value(), - volumeClippingBoxSpinBoxes[1]->value(), - volumeClippingBoxSpinBoxes[2]->value()); - ospcommon::vec3f upper(volumeClippingBoxSpinBoxes[3]->value(), - volumeClippingBoxSpinBoxes[4]->value(), - volumeClippingBoxSpinBoxes[5]->value()); - - volumeViewer->setVolumeClippingBox(ospcommon::box3f(lower, upper)); -} diff --git a/apps/volumeViewer/PreferencesDialog.h b/apps/volumeViewer/PreferencesDialog.h deleted file mode 100644 index 97a0a629fd..0000000000 --- a/apps/volumeViewer/PreferencesDialog.h +++ /dev/null @@ -1,65 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include "ospcommon/box.h" -#include -#include - -class VolumeViewer; - -class PreferencesDialog : public QDialog -{ -Q_OBJECT - -public: - - PreferencesDialog(VolumeViewer *volumeViewer, ospcommon::box3f boundingBox); - - void setSamplingRate(float v); - void setAdaptiveMaxSamplingRate(float v); -public slots: - -void setGradientShadingEnabled(bool v); -void setSPP(int v); -void setAOSamples(int v); -void setPreIntegration(bool v); -void setShadows(bool v); -void setAdaptiveSampling(bool v); -void setPlane(bool v); - - -protected slots: - - void updateVolumeClippingBox(); - -protected: - - VolumeViewer *volumeViewer; - QDoubleSpinBox* samplingRateSpinBox; - QDoubleSpinBox* adaptiveMaxSamplingRateSB; - QSpinBox* sppSB; - QSpinBox* aoSamplesSB; - QCheckBox* preIntegrationCB; - QCheckBox* adaptiveSamplingCB; - QCheckBox* shadowsCB; - QCheckBox* gradientShadingEnabledCheckBox; - QCheckBox* planeCB; - - std::vector volumeClippingBoxSpinBoxes; -}; diff --git a/apps/volumeViewer/ProbeWidget.cpp b/apps/volumeViewer/ProbeWidget.cpp deleted file mode 100644 index 9a654fdc28..0000000000 --- a/apps/volumeViewer/ProbeWidget.cpp +++ /dev/null @@ -1,184 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "VolumeViewer.h" -#include "ProbeWidget.h" -#include "QOSPRayWindow.h" - -#ifdef __APPLE__ - #include -#else - #include -#endif - -ProbeCoordinateWidget::ProbeCoordinateWidget(const std::string &label, ospcommon::vec2f range) : range(range) -{ - QHBoxLayout *layout = new QHBoxLayout(); - setLayout(layout); - - spinBox.setRange(range.x, range.y); - - slider.setOrientation(Qt::Horizontal); - - layout->addWidget(new QLabel(label.c_str())); - layout->addWidget(&slider); - layout->addWidget(&spinBox); - - connect(&slider, SIGNAL(valueChanged(int)), this, SLOT(updateValue())); - connect(&spinBox, SIGNAL(valueChanged(double)), this, SLOT(updateValue())); -} - -void ProbeCoordinateWidget::updateValue() -{ - if (sender() == &slider) { - float value = range.x + float(slider.value() - slider.minimum()) / float(slider.maximum() - slider.minimum()) * (range.y - range.x); - - spinBox.blockSignals(true); - spinBox.setValue(value); - spinBox.blockSignals(false); - } - else if (sender() == &spinBox) { - float value = spinBox.value(); - - slider.blockSignals(true); - slider.setValue(slider.minimum() + (value - range.x) / (range.y - range.x) * (slider.maximum() - slider.minimum())); - slider.blockSignals(false); - } - else - throw std::runtime_error("slot executed from unknown sender"); - - emit probeCoordinateChanged(); -} - - -ProbeWidget::ProbeWidget(VolumeViewer *volumeViewer) : volumeViewer(volumeViewer), isEnabled(false) -{ - if (!volumeViewer) - throw std::runtime_error("ProbeWidget: NULL volumeViewer"); - - osprayWindow = volumeViewer->getWindow(); - boundingBox = volumeViewer->getBoundingBox(); - - if (!osprayWindow) - throw std::runtime_error("ProbeWidget: NULL osprayWindow"); - - // Setup UI elments. - QVBoxLayout *layout = new QVBoxLayout(); - layout->setAlignment(Qt::AlignTop); - setLayout(layout); - - QCheckBox *enabledCheckBox = new QCheckBox("Enable probe"); - layout->addWidget(enabledCheckBox); - connect(enabledCheckBox, SIGNAL(toggled(bool)), this, SLOT(setEnabled(bool))); - - QGroupBox *coordinatesGroupBox = new QGroupBox("Coordinates"); - QVBoxLayout *coordinatesLayout = new QVBoxLayout(); - coordinatesGroupBox->setLayout(coordinatesLayout); - connect(this, SIGNAL(enabled(bool)), coordinatesGroupBox, SLOT(setEnabled(bool))); - - probeCoordinateWidgets.push_back(new ProbeCoordinateWidget("x", ospcommon::vec2f(boundingBox.lower.x, boundingBox.upper.x))); - probeCoordinateWidgets.push_back(new ProbeCoordinateWidget("y", ospcommon::vec2f(boundingBox.lower.y, boundingBox.upper.y))); - probeCoordinateWidgets.push_back(new ProbeCoordinateWidget("z", ospcommon::vec2f(boundingBox.lower.z, boundingBox.upper.z))); - - for (int i=0; i<3; i++) { - coordinatesLayout->addWidget(probeCoordinateWidgets[i]); - connect(probeCoordinateWidgets[i], SIGNAL(probeCoordinateChanged()), this, SIGNAL(probeChanged())); - } - - layout->addWidget(coordinatesGroupBox); - - layout->addWidget(&sampleValueLabel); - connect(this, SIGNAL(enabled(bool)), &sampleValueLabel, SLOT(setEnabled(bool))); - - connect(this, SIGNAL(probeChanged()), this, SLOT(updateProbe()), Qt::UniqueConnection); - - // Probe disabled by default - emit enabled(false); -} - -void ProbeWidget::setEnabled(bool value) -{ - isEnabled = value; - - // Connect or disconnect render() slot as needed - if (value) - connect(osprayWindow, SIGNAL(renderGLComponents()), this, SLOT(render()), Qt::UniqueConnection); - else - disconnect(osprayWindow, SIGNAL(renderGLComponents()), this, SLOT(render())); - - emit probeChanged(); - emit enabled(value); -} - -void ProbeWidget::updateProbe() -{ - if (!isEnabled) { - // force re-render even if disabled: if probe has just been disabled we - // need to re-render to get rid of the rendered probe - volumeViewer->render(); - return; - } - - coordinate = osp::vec3f{probeCoordinateWidgets[0]->getValue(), probeCoordinateWidgets[1]->getValue(), probeCoordinateWidgets[2]->getValue()}; - - if (volume) { - - float *results = NULL; - ospSampleVolume(&results, volume, coordinate, 1); - - if (!results) - std::cout << "error calling ospSampleVolume()" << std::endl; - else - sampleValueLabel.setText("Sampled volume value: " + QString::number(results[0])); - } - else - sampleValueLabel.setText(""); - - // Force render to update rendered probe - volumeViewer->render(); -} - -void ProbeWidget::render() -{ - const float size = 0.25f * reduce_max(boundingBox.upper - boundingBox.lower); - - glPushAttrib(GL_ENABLE_BIT); - glDisable(GL_LIGHTING); - glEnable(GL_DEPTH_TEST); - - glPushMatrix(); - glTranslatef(coordinate.x, coordinate.y, coordinate.z); - glScalef(size, size, size); - - glBegin(GL_LINES); - - glColor3f(1.f, 0.f, 0.f); - glVertex3f(-0.5f, 0.f, 0.f); - glVertex3f( 0.5f, 0.f, 0.f); - - glColor3f(0.f, 1.f, 0.f); - glVertex3f(0.f, -0.5f, 0.f); - glVertex3f(0.f, 0.5f, 0.f); - - glColor3f(0.f, 0.f, 1.f); - glVertex3f(0.f, 0.f, -0.5f); - glVertex3f(0.f, 0.f, 0.5f); - - glEnd(); - - glPopMatrix(); - glPopAttrib(); -} diff --git a/apps/volumeViewer/ProbeWidget.h b/apps/volumeViewer/ProbeWidget.h deleted file mode 100644 index 1ee0f3c7d2..0000000000 --- a/apps/volumeViewer/ProbeWidget.h +++ /dev/null @@ -1,97 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include "ospcommon/box.h" -#include -#include - -class VolumeViewer; -class QOSPRayWindow; - -class ProbeCoordinateWidget : public QWidget { - -Q_OBJECT - -public: - - ProbeCoordinateWidget(const std::string &label, ospcommon::vec2f range); - - float getValue() { return spinBox.value(); } - -signals: - - void probeCoordinateChanged(); - -protected slots: - - void updateValue(); - -protected: - - ospcommon::vec2f range; - - QSlider slider; - QDoubleSpinBox spinBox; -}; - - -class ProbeWidget : public QWidget { - -Q_OBJECT - -public: - - ProbeWidget(VolumeViewer *volumeViewer); - -signals: - - void enabled(bool); - void probeChanged(); - -public slots: - - void setEnabled(bool value); - void setVolume(OSPVolume volume) { this->volume = volume; emit(probeChanged()); } - void updateProbe(); - void render(); - -protected: - - //! The parent volume viewer. - VolumeViewer *volumeViewer; - - //! The OSPRay window. - QOSPRayWindow *osprayWindow; - - //! Bounding box to consider. - ospcommon::box3f boundingBox; - - //! Active volume to probe. - OSPVolume volume; - - //! Whether the probe is enabled. - bool isEnabled; - - //! The probe coordinate. - osp::vec3f coordinate; - - // UI elements. - std::vector probeCoordinateWidgets; - QLabel sampleValueLabel; -}; diff --git a/apps/volumeViewer/QOSPRayWindow.cpp b/apps/volumeViewer/QOSPRayWindow.cpp deleted file mode 100644 index 0f7248ac87..0000000000 --- a/apps/volumeViewer/QOSPRayWindow.cpp +++ /dev/null @@ -1,406 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include - -#include "QOSPRayWindow.h" -#include "glUtil/util.h" - -#include "ospcommon/common.h" - -#ifdef __APPLE__ - #include -#else - #include -#endif - -std::ostream &operator<<(std::ostream &o, const Viewport &viewport) -{ - o << "-vp " << viewport.from.x << " " << viewport.from.y << " " << viewport.from.z - << " -vi " << viewport.at.x << " " << viewport.at.y << " " << viewport.at.z - << " -vu " << viewport.up.x << " " << viewport.up.y << " " << viewport.up.z - << std::endl; - - return o; -} - - -QOSPRayWindow::QOSPRayWindow(QMainWindow *parent, - OSPRenderer renderer, - bool showFrameRate, - std::string writeFramesFilename) - : parent(parent), - showFrameRate(showFrameRate), - frameCount(0), - renderingEnabled(false), - rotationRate(0.f), - benchmarkWarmUpFrames(0), - benchmarkFrames(0), - frameBuffer(NULL), - renderer(NULL), - camera(NULL), - maxDepthTexture(NULL), - writeFramesFilename(writeFramesFilename) -{ - // assign renderer - if(!renderer) - throw std::runtime_error("QOSPRayWindow: must be constructed with an existing renderer"); - - this->renderer = renderer; - - // setup camera - auto cameraFromEnv = ospcommon::getEnvVar("OSPRAY_USE_CAMERA_TYPE"); - - if (cameraFromEnv.first) { - camera = ospNewCamera(cameraFromEnv.second.c_str()); - } else { - camera = ospNewCamera("perspective"); - } - - if(!camera) - throw std::runtime_error("QOSPRayWindow: could not create camera type 'perspective'"); - - ospCommit(camera); - - ospSetObject(renderer, "camera", camera); - - // connect signals and slots - connect(&renderTimer, SIGNAL(timeout()), this, SLOT(updateGL())); - connect(&renderRestartTimer, SIGNAL(timeout()), &renderTimer, SLOT(start())); -} - -QOSPRayWindow::~QOSPRayWindow() -{ - // free the frame buffer and camera - // we don't own the renderer! - if(frameBuffer) - ospFreeFrameBuffer(frameBuffer); - - if(camera) - ospRelease(camera); -} - -void QOSPRayWindow::setRenderingEnabled(bool renderingEnabled) -{ - this->renderingEnabled = renderingEnabled; - - // trigger render if true - if(renderingEnabled == true) - renderTimer.start(); - else - renderTimer.stop(); -} - -void QOSPRayWindow::setRotationRate(float rotationRate) -{ - this->rotationRate = rotationRate; -} - -void QOSPRayWindow::setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames, - const std::string &benchmarkFilename) -{ - this->benchmarkWarmUpFrames = benchmarkWarmUpFrames; - this->benchmarkFrames = benchmarkFrames; - this->benchmarkFilename = benchmarkFilename; -} - -void QOSPRayWindow::setWorldBounds(const ospcommon::box3f &worldBounds) -{ - this->worldBounds = worldBounds; - - // set viewport look at point to center of world bounds - viewport.at = center(worldBounds); - - // set viewport from point relative to center of world bounds - viewport.from = viewport.at - 1.5f * length(worldBounds.size()) * viewport.frame.l.vy; - - updateGL(); -} - -void QOSPRayWindow::paintGL() -{ - if(!renderingEnabled || !frameBuffer || !renderer || !QApplication::activeWindow()) - return; - - // if we're benchmarking and we've completed the required number of warm-up frames, start the timer - if(benchmarkFrames > 0 && frameCount == benchmarkWarmUpFrames) { - std::cout << "starting benchmark timer" << std::endl; - benchmarkTimer.start(); - } - - // update OSPRay camera if viewport has been modified - if(viewport.modified) { - const ospcommon::vec3f dir = viewport.at - viewport.from; - ospSetVec3f(camera,"pos" ,(const osp::vec3f&)viewport.from); - ospSetVec3f(camera,"dir" ,(const osp::vec3f&)dir); - ospSetVec3f(camera,"up", (const osp::vec3f&)viewport.up); - ospSetf(camera,"aspect", viewport.aspect); - ospSetf(camera,"fovy", viewport.fovY); - - ospCommit(camera); - - viewport.modified = false; - } - - renderFrameTimer.start(); - - // we have OpenGL components if any slots are connected to the renderGLComponents() signal - // if so, render these first and then composite the OSPRay-rendered content on top - bool haveOpenGLComponents = receivers(SIGNAL(renderGLComponents())) > 0; - - if (haveOpenGLComponents) { - - // setup OpenGL view to match current view and render all OpenGL components - renderGL(); - - // generate max depth texture for early ray termination - if (maxDepthTexture) - ospRelease(maxDepthTexture); - - maxDepthTexture = ospray::opengl::getOSPDepthTextureFromOpenGLPerspective(); - ospSetObject(renderer, "maxDepthTexture", maxDepthTexture); - - // disable OSPRay background rendering since we're compositing - ospSet1i(renderer, "backgroundEnabled", 0); - - ospCommit(renderer); - - // disable OpenGL depth testing and enable blending for compositing - glDisable(GL_DEPTH_TEST); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - } - else { - - // unset any maximum depth texture and enable OSPRay background rendering - ospSetObject(renderer, "maxDepthTexture", NULL); - ospSet1i(renderer, "backgroundEnabled", 1); - ospCommit(renderer); - - // disable OpenGL blending - glDisable(GL_BLEND); - } - - ospRenderFrame(frameBuffer, renderer, OSP_FB_COLOR | OSP_FB_ACCUM); - double framesPerSecond = 1000.0 / renderFrameTimer.elapsed(); - char title[1024]; sprintf(title, "OSPRay Volume Viewer (%.4f fps)", framesPerSecond); - if (showFrameRate == true) parent->setWindowTitle(title); - - uint32_t *mappedFrameBuffer = (unsigned int *) ospMapFrameBuffer(frameBuffer); - - glDrawPixels(windowSize.x, windowSize.y, GL_RGBA, GL_UNSIGNED_BYTE, mappedFrameBuffer); - if (writeFramesFilename.length()) { - std::string filename = writeFramesFilename + std::to_string(static_cast(frameCount)); - writeFrameBufferToFile(filename, mappedFrameBuffer); - } - - ospUnmapFrameBuffer(mappedFrameBuffer, frameBuffer); - - // automatic rotation - if(rotationRate != 0.f) { - resetAccumulationBuffer(); - rotateCenter(rotationRate, 0.f); - } - - // increment frame counter - frameCount++; - - // quit if we're benchmarking and have exceeded the needed number of frames - if(benchmarkFrames > 0 && frameCount >= benchmarkWarmUpFrames + benchmarkFrames) { - - float elapsedSeconds = float(benchmarkTimer.elapsed()) / 1000.f; - - std::cout << "benchmark: " << elapsedSeconds << " elapsed seconds ==> " - << float(benchmarkFrames) / elapsedSeconds << " fps" << std::endl; - - uint32_t *mappedFrameBuffer = (unsigned int *) ospMapFrameBuffer(frameBuffer); - - writeFrameBufferToFile(benchmarkFilename, mappedFrameBuffer); - - QCoreApplication::quit(); - } -} - -void QOSPRayWindow::resizeGL(int width, int height) -{ - windowSize = ospcommon::vec2i(width, height); - - // reallocate OSPRay framebuffer for new size - if(frameBuffer) - ospFreeFrameBuffer(frameBuffer); - - frameBuffer = ospNewFrameBuffer((const osp::vec2i&)windowSize, OSP_FB_SRGBA, OSP_FB_COLOR | OSP_FB_ACCUM); - - resetAccumulationBuffer(); - - // update viewport aspect ratio - viewport.aspect = float(width) / float(height); - viewport.modified = true; - - // update OpenGL viewport and force redraw - glViewport(0, 0, width, height); - updateGL(); -} - -void QOSPRayWindow::mousePressEvent(QMouseEvent * event) -{ - lastMousePosition = event->pos(); -} - -void QOSPRayWindow::mouseReleaseEvent(QMouseEvent * event) -{ - lastMousePosition = event->pos(); - - // restart continuous rendering immediately - renderTimer.start(); -} - -void QOSPRayWindow::mouseMoveEvent(QMouseEvent * event) -{ - // pause continuous rendering during interaction and cancel any render restart timers. - // this keeps interaction more responsive (especially with low frame rates). - renderTimer.stop(); - renderRestartTimer.stop(); - - resetAccumulationBuffer(); - - int dx = event->x() - lastMousePosition.x(); - int dy = event->y() - lastMousePosition.y(); - - if(event->buttons() & Qt::LeftButton) { - - // camera rotation about center point - const float rotationSpeed = 0.003f; - - float du = dx * rotationSpeed; - float dv = dy * rotationSpeed; - - rotateCenter(du, dv); - } - else if(event->buttons() & Qt::MidButton) { - - // camera strafe of from / at point - const float strafeSpeed = 0.001f * length(worldBounds.size()); - - float du = dx * strafeSpeed; - float dv = dy * strafeSpeed; - - strafe(du, dv); - } - else if(event->buttons() & Qt::RightButton) { - - // camera distance from center point - const float motionSpeed = 0.012f; - - float forward = dy * motionSpeed * length(worldBounds.size()); - float oldDistance = length(viewport.at - viewport.from); - float newDistance = oldDistance - forward; - - if(newDistance < 1e-3f) - return; - - viewport.from = viewport.at - newDistance * viewport.frame.l.vy; - viewport.frame.p = viewport.from; - - viewport.modified = true; - } - - lastMousePosition = event->pos(); - - updateGL(); - - // after a 0.5s delay, restart continuous rendering. - renderRestartTimer.setSingleShot(true); - renderRestartTimer.start(500); -} - -void QOSPRayWindow::rotateCenter(float du, float dv) -{ - const ospcommon::vec3f pivot = viewport.at; - - ospcommon::affine3f xfm = ospcommon::affine3f::translate(pivot) - * ospcommon::affine3f::rotate(viewport.frame.l.vx, -dv) - * ospcommon::affine3f::rotate(viewport.frame.l.vz, -du) - * ospcommon::affine3f::translate(-pivot); - - viewport.frame = xfm * viewport.frame; - viewport.from = xfmPoint(xfm, viewport.from); - viewport.at = xfmPoint(xfm, viewport.at); - viewport.snapUp(); - - viewport.modified = true; -} - -void QOSPRayWindow::strafe(float du, float dv) -{ - ospcommon::affine3f xfm = ospcommon::affine3f::translate(dv * viewport.frame.l.vz) - * ospcommon::affine3f::translate(-du * viewport.frame.l.vx); - - viewport.frame = xfm * viewport.frame; - viewport.from = xfmPoint(xfm, viewport.from); - viewport.at = xfmPoint(xfm, viewport.at); - viewport.modified = true; - - viewport.modified = true; -} - -void QOSPRayWindow::renderGL() -{ - // setup OpenGL state to match OSPRay view - const ospcommon::vec3f bgColor = ospcommon::vec3f(1.f); - glClearColor(bgColor.x, bgColor.y, bgColor.z, 1.f); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - float zNear = 0.1f; - float zFar = 100000.f; - gluPerspective(viewport.fovY, viewport.aspect, zNear, zFar); - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - gluLookAt(viewport.from.x, viewport.from.y, viewport.from.z, - viewport.at.x, viewport.at.y, viewport.at.z, - viewport.up.x, viewport.up.y, viewport.up.z); - - // emit signal to render all OpenGL components; the slots will execute in the order they were registered - emit(renderGLComponents()); -} - -void QOSPRayWindow::writeFrameBufferToFile(std::string filename, const uint32_t *pixelData) -{ - filename += ".ppm"; - FILE *file = fopen(filename.c_str(), "wb"); - if (!file) { - std::cerr << "unable to write to file '" << filename << "'" << std::endl; - return; - } - fprintf(file, "P6\n%i %i\n255\n", windowSize.x, windowSize.y); - unsigned char *out = (unsigned char *)alloca(3 * windowSize.x); - for (int y=0 ; y < windowSize.y ; y++) { - const unsigned char *in = (const unsigned char *) &pixelData[(windowSize.y - 1 - y) * windowSize.x]; - for (int x=0 ; x < windowSize.x ; x++) { - out[3 * x + 0] = in[4 * x + 0]; - out[3 * x + 1] = in[4 * x + 1]; - out[3 * x + 2] = in[4 * x + 2]; - } - fwrite(out, 3 * windowSize.x, sizeof(char), file); - } - fprintf(file, "\n"); - fclose(file); -} diff --git a/apps/volumeViewer/QOSPRayWindow.h b/apps/volumeViewer/QOSPRayWindow.h deleted file mode 100644 index 2e7992f9b9..0000000000 --- a/apps/volumeViewer/QOSPRayWindow.h +++ /dev/null @@ -1,172 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -// ospray public -#include -// ospcommon -#include "ospcommon/AffineSpace.h" -// qt -#include -#include - -struct Viewport -{ - Viewport() : from(0,-1,0), - at(0,0,0), - up(0,0,-1), - aspect(1.f), - fovY(60.f), - modified(true) - { - frame = ospcommon::affine3f::translate(from) * ospcommon::affine3f(ospcommon::one); - } - - ospcommon::vec3f from; - ospcommon::vec3f at; - ospcommon::vec3f up; - - /*! aspect ratio (width / height) */ - float aspect; - - /*! vertical field of view (degrees) */ - float fovY; - - /*! this flag should be set every time the viewport is modified */ - bool modified; - - /*! camera frame in which the Y axis is the depth axis, and X - and Z axes are parallel to the screen X and Y axis. The frame - itself remains normalized. */ - ospcommon::affine3f frame; - - /*! set up vector */ - void setUp(const ospcommon::vec3f &vec) - { - up = vec; - snapUp(); - modified = true; - } - - /*! set frame 'up' vector. if this vector is (0,0,0) the window will - *not* apply the up-vector after camera manipulation */ - void snapUp() - { - if(fabsf(dot(up,frame.l.vz)) < 1e-3f) - return; - - frame.l.vx = normalize(cross(frame.l.vy,up)); - frame.l.vz = normalize(cross(frame.l.vx,frame.l.vy)); - frame.l.vy = normalize(cross(frame.l.vz,frame.l.vx)); - } -}; - -std::ostream &operator<<(std::ostream &o, const Viewport &viewport); - - -class QOSPRayWindow : public QGLWidget -{ -Q_OBJECT - -public: - - QOSPRayWindow(QMainWindow *parent, OSPRenderer renderer, bool showFrameRate, std::string writeFramesFilename); - virtual ~QOSPRayWindow(); - - void setRenderingEnabled(bool renderingEnabled); - void setRotationRate(float rotationRate); - void setBenchmarkParameters(int benchmarkWarmUpFrames, int benchmarkFrames, const std::string &benchmarkFilename); - virtual void setWorldBounds(const ospcommon::box3f &worldBounds); - - Viewport * getViewport() { return &viewport; } - - OSPFrameBuffer getFrameBuffer() { return frameBuffer; } - - void resetAccumulationBuffer() { ospFrameBufferClear(frameBuffer, OSP_FB_ACCUM); } - -signals: - - /*! slots can be connected to this signal to enable rendering of OpenGL components of the scene */ - void renderGLComponents(); - -protected: - - /*! Parent Qt window. */ - QMainWindow *parent; - - /*! Display the frame rate in the main window title bar. */ - bool showFrameRate; - - virtual void paintGL(); - virtual void resizeGL(int width, int height); - virtual void mousePressEvent(QMouseEvent * event); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - - /*! rotate about center point */ - virtual void rotateCenter(float du, float dv); - - /*! strafe the camera from / at point */ - virtual void strafe(float du, float dv); - - /*! render any OpenGL components of the scene */ - virtual void renderGL(); - - /*! frame counter */ - long frameCount; - - /*! only render when this flag is true. this allows the window to be created before all required components are ospCommit()'d. */ - bool renderingEnabled; - - /*! rotation rate to automatically rotate view. */ - float rotationRate; - - /*! timer used to trigger continuous re-renders (for progressive refinement, automatic rotation, etc.). */ - QTimer renderTimer; - - /*! timer used to restart continuous rendering after a delay during interaction. */ - QTimer renderRestartTimer; - - /*! benchmarking: number of warm-up frames */ - int benchmarkWarmUpFrames; - - /*! benchmarking: number of frames over which to measure frame rate */ - int benchmarkFrames; - - /*! benchmarking: file name to save the final image to */ - std::string benchmarkFilename; - - /*! benchmarking: timer to measure elapsed time over benchmark frames */ - QTime benchmarkTimer; - - /*! Timer to measure elapsed time over a single frame. */ - QTime renderFrameTimer; - - ospcommon::vec2i windowSize; - Viewport viewport; - ospcommon::box3f worldBounds; - QPoint lastMousePosition; - - OSPFrameBuffer frameBuffer; - OSPRenderer renderer; - OSPCamera camera; - OSPTexture2D maxDepthTexture; - - std::string writeFramesFilename; - void writeFrameBufferToFile(std::string filename, const uint32_t *pixelData); - -}; diff --git a/apps/volumeViewer/SliceEditor.cpp b/apps/volumeViewer/SliceEditor.cpp deleted file mode 100644 index 3661a17cc5..0000000000 --- a/apps/volumeViewer/SliceEditor.cpp +++ /dev/null @@ -1,64 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "SliceEditor.h" -#include - -SliceEditor::SliceEditor(ospcommon::box3f boundingBox) - : boundingBox(boundingBox) -{ - // Setup UI elements. - layout.setSizeConstraint(QLayout::SetMinimumSize); - layout.setAlignment(Qt::AlignTop); - setLayout(&layout); - - QPushButton *addSliceButton = new QPushButton("Add slice"); - layout.addWidget(addSliceButton); - connect(addSliceButton, SIGNAL(clicked()), this, SLOT(addSlice())); -} - -void SliceEditor::addSlice(std::string filename) -{ - SliceWidget *sliceWidget = new SliceWidget(this, boundingBox); - - sliceWidgets.push_back(sliceWidget); - layout.addWidget(sliceWidget); - - if (!filename.empty()) - sliceWidget->load(filename); -} - -void SliceEditor::apply() -{ - std::vector sliceParameters; - - for (unsigned int i=0; igetSliceParameters()); - } - - emit(slicesChanged(sliceParameters)); -} - -void SliceEditor::deleteSlice(SliceWidget *sliceWidget) -{ - // This is triggered from the SliceWidget destructor, so no need to delete... - std::vector::iterator position = std::find(sliceWidgets.begin(), sliceWidgets.end(), sliceWidget); - - if (position != sliceWidgets.end()) - sliceWidgets.erase(position); - - apply(); -} diff --git a/apps/volumeViewer/SliceEditor.h b/apps/volumeViewer/SliceEditor.h deleted file mode 100644 index 2cf61efdce..0000000000 --- a/apps/volumeViewer/SliceEditor.h +++ /dev/null @@ -1,53 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include - -#include "SliceWidget.h" -#include -#include -#include - -class SliceEditor : public QWidget -{ -Q_OBJECT - -public: - - SliceEditor(ospcommon::box3f boundingBox); - -signals: - - void slicesChanged(std::vector sliceParameters); - -public slots: - - void addSlice(std::string filename = std::string()); - void apply(); - void deleteSlice(SliceWidget *sliceWidget); - -protected: - - //! Bounding box of the volume. - ospcommon::box3f boundingBox; - - //! UI elements. - QVBoxLayout layout; - std::vector sliceWidgets; - -}; diff --git a/apps/volumeViewer/SliceWidget.cpp b/apps/volumeViewer/SliceWidget.cpp deleted file mode 100644 index 9930a1cbbc..0000000000 --- a/apps/volumeViewer/SliceWidget.cpp +++ /dev/null @@ -1,311 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "SliceWidget.h" -#include "SliceEditor.h" - -SliceWidget::SliceWidget(SliceEditor *sliceEditor, ospcommon::box3f boundingBox) - : boundingBox(boundingBox), - originSliderAnimationDirection(1) -{ - // Check parameters. - if(ospcommon::volume(boundingBox) <= 0.f) - throw std::runtime_error("invalid volume bounds"); - - // Setup UI elements. - QVBoxLayout * layout = new QVBoxLayout(); - setLayout(layout); - - // Save and load buttons. - QWidget * saveLoadWidget = new QWidget(); - QHBoxLayout * hboxLayout = new QHBoxLayout(); - saveLoadWidget->setLayout(hboxLayout); - - QPushButton * saveButton = new QPushButton("Save"); - connect(saveButton, SIGNAL(clicked()), this, SLOT(save())); - hboxLayout->addWidget(saveButton); - - QPushButton * loadButton = new QPushButton("Load"); - connect(loadButton, SIGNAL(clicked()), this, SLOT(load())); - hboxLayout->addWidget(loadButton); - - layout->addWidget(saveLoadWidget); - - // Form layout for the rest of the UI elements. - QWidget * formWidget = new QWidget(); - QFormLayout * formLayout = new QFormLayout(); - formWidget->setLayout(formLayout); - QMargins margins = formLayout->contentsMargins(); - margins.setTop(0); margins.setBottom(0); - formLayout->setContentsMargins(margins); - layout->addWidget(formWidget); - - // Origin parameters with default values. - QWidget * originWidget = new QWidget(); - hboxLayout = new QHBoxLayout(); - originWidget->setLayout(hboxLayout); - margins = hboxLayout->contentsMargins(); - margins.setTop(0); margins.setBottom(0); - hboxLayout->setContentsMargins(margins); - - originXSpinBox.setRange(boundingBox.lower.x, boundingBox.upper.x); - originYSpinBox.setRange(boundingBox.lower.y, boundingBox.upper.y); - originZSpinBox.setRange(boundingBox.lower.z, boundingBox.upper.z); - - originXSpinBox.setValue(0.5 * (boundingBox.lower.x + boundingBox.upper.x)); - originYSpinBox.setValue(0.5 * (boundingBox.lower.y + boundingBox.upper.y)); - originZSpinBox.setValue(0.5 * (boundingBox.lower.z + boundingBox.upper.z)); - - connect(&originXSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - connect(&originYSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - connect(&originZSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - - hboxLayout->addWidget(&originXSpinBox); - hboxLayout->addWidget(&originYSpinBox); - hboxLayout->addWidget(&originZSpinBox); - - formLayout->addRow("Origin", originWidget); - - // Normal parameters with default values. - QWidget * normalWidget = new QWidget(); - hboxLayout = new QHBoxLayout(); - normalWidget->setLayout(hboxLayout); - margins = hboxLayout->contentsMargins(); - margins.setTop(0); margins.setBottom(0); - hboxLayout->setContentsMargins(margins); - - normalXSpinBox.setRange(-1., 1.); - normalYSpinBox.setRange(-1., 1.); - normalZSpinBox.setRange(-1., 1.); - - normalXSpinBox.setValue(1.); - normalYSpinBox.setValue(0.); - normalZSpinBox.setValue(0.); - - connect(&normalXSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - connect(&normalYSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - connect(&normalZSpinBox, SIGNAL(valueChanged(double)), this, SLOT(apply())); - - hboxLayout->addWidget(&normalXSpinBox); - hboxLayout->addWidget(&normalYSpinBox); - hboxLayout->addWidget(&normalZSpinBox); - - formLayout->addRow("Normal", normalWidget); - - // Add a slider and animate button for the origin location along the normal. - // Defaults to mid-point (matching the origin widgets). - QWidget * originSliderWidget = new QWidget(); - hboxLayout = new QHBoxLayout(); - originSliderWidget->setLayout(hboxLayout); - margins = hboxLayout->contentsMargins(); - margins.setTop(0); margins.setBottom(0); - hboxLayout->setContentsMargins(margins); - - originSlider.setOrientation(Qt::Horizontal); - originSlider.setValue(0.5 * (originSlider.maximum() - originSlider.minimum())); - connect(&originSlider, SIGNAL(valueChanged(int)), this, SLOT(originSliderValueChanged(int))); - hboxLayout->addWidget(&originSlider); - - originSliderAnimateButton.setText("Animate"); - originSliderAnimateButton.setCheckable(true); - connect(&originSliderAnimateButton, SIGNAL(toggled(bool)), this, SLOT(setAnimation(bool))); - hboxLayout->addWidget(&originSliderAnimateButton); - - formLayout->addRow("", originSliderWidget); - - // Connect animation timer signal / slot. - connect(&originSliderAnimationTimer, SIGNAL(timeout()), this, SLOT(animate())); - - // Delete button. - QWidget * buttonsWidget = new QWidget(); - hboxLayout = new QHBoxLayout(); - buttonsWidget->setLayout(hboxLayout); - - QPushButton * deleteButton = new QPushButton("Delete"); - connect(deleteButton, SIGNAL(clicked()), this, SLOT(deleteLater())); - hboxLayout->addWidget(deleteButton); - - layout->addWidget(buttonsWidget); - - // Frame style for the widget. - setFrameStyle(QFrame::Panel | QFrame::Raised); - - // Minimum width for the widget. - setMinimumWidth(240); - - // Connect signals to trigger updates in the slice editor. - connect(this, SIGNAL(sliceChanged()), sliceEditor, SLOT(apply())); - connect(this, SIGNAL(sliceDeleted(SliceWidget *)), sliceEditor, SLOT(deleteSlice(SliceWidget *))); - - // Apply slice once event loop continues (allows SliceWidget to be added to SliceEditor list first). - QTimer::singleShot(0, this, SLOT(apply())); -} - -SliceWidget::~SliceWidget() -{ - emit(sliceDeleted(this)); -} - -SliceParameters SliceWidget::getSliceParameters() -{ - SliceParameters sliceParameters; - - // Get the origin and normal values. - sliceParameters.origin = ospcommon::vec3f(float(originXSpinBox.value()), float(originYSpinBox.value()), float(originZSpinBox.value())); - sliceParameters.normal = ospcommon::vec3f(float(normalXSpinBox.value()), float(normalYSpinBox.value()), float(normalZSpinBox.value())); - - return sliceParameters; -} - -void SliceWidget::load(std::string filename) -{ - // Get filename if not specified. - if(filename.empty()) - filename = QFileDialog::getOpenFileName(this, tr("Load slice"), ".", "Slice files (*.slc)").toStdString(); - - if(filename.empty()) - return; - - // Get serialized slice state from file. - QFile file(filename.c_str()); - bool success = file.open(QIODevice::ReadOnly); - - if(!success) - { - std::cerr << "unable to open " << filename << std::endl; - return; - } - - QDataStream in(&file); - - double originX, originY, originZ; - in >> originX >> originY >> originZ; - - double normalX, normalY, normalZ; - in >> normalX >> normalY >> normalZ; - - int sliderPosition; - in >> sliderPosition; - - bool sliderAnimating; - in >> sliderAnimating; - - in >> originSliderAnimationDirection; - - // Update slice state. Update values of the UI elements directly to signal appropriate slots. - originXSpinBox.setValue(originX); - originYSpinBox.setValue(originY); - originZSpinBox.setValue(originZ); - normalXSpinBox.setValue(normalX); - normalYSpinBox.setValue(normalY); - normalZSpinBox.setValue(normalZ); - originSlider.setValue(sliderPosition); - originSliderAnimateButton.setChecked(sliderAnimating); -} - -void SliceWidget::apply() -{ - emit(sliceChanged()); -} - -void SliceWidget::save() { - - // Get filename. - QString filename = QFileDialog::getSaveFileName(this, "Save slice", ".", "Slice files (*.slc)"); - - if(filename.isNull()) - return; - - // Make sure the filename has the proper extension. - if(filename.endsWith(".slc") != true) - filename += ".slc"; - - // Serialize slice state to file. - QFile file(filename); - bool success = file.open(QIODevice::WriteOnly); - - if(!success) - { - std::cerr << "unable to open " << filename.toStdString() << std::endl; - return; - } - - QDataStream out(&file); - - out << originXSpinBox.value() << originXSpinBox.value() << originXSpinBox.value(); - out << normalXSpinBox.value() << normalYSpinBox.value() << normalZSpinBox.value(); - out << originSlider.value(); - out << originSliderAnimateButton.isChecked(); - out << originSliderAnimationDirection; -} - -void SliceWidget::originSliderValueChanged(int value) { - - // Slider position in [0, 1]. - float sliderPosition = float(value) / float(originSlider.maximum() - originSlider.minimum()); - - // Get origin and (normalized) normal vectors. - ospcommon::vec3f origin(originXSpinBox.value(), originYSpinBox.value(), originZSpinBox.value()); - ospcommon::vec3f normal = normalize(ospcommon::vec3f(normalXSpinBox.value(), normalYSpinBox.value(), normalZSpinBox.value())); - - // Compute allowed range along normal for the volume bounds. - ospcommon::vec3f upper(normal.x >= 0.f ? boundingBox.upper.x : boundingBox.lower.x, - normal.y >= 0.f ? boundingBox.upper.y : boundingBox.lower.y, - normal.z >= 0.f ? boundingBox.upper.z : boundingBox.lower.z); - - ospcommon::vec3f lower(normal.x >= 0.f ? boundingBox.lower.x : boundingBox.upper.x, - normal.y >= 0.f ? boundingBox.lower.y : boundingBox.upper.y, - normal.z >= 0.f ? boundingBox.lower.z : boundingBox.upper.z); - - float tMax = dot(abs(upper - origin), abs(normal)); - float tMin = -dot(abs(lower - origin), abs(normal)); - - // Get t value within this range. - float t = tMin + sliderPosition * (tMax - tMin); - - // Clamp t within epsilon of the minimum and maximum, to prevent artifacts rendering near the bounds of the volume. - const float epsilon = 0.01; - t = std::max(std::min(t, tMax-epsilon), tMin+epsilon); - - // Compute updated origin, clamped within the volume bounds. - ospcommon::vec3f updatedOrigin = clamp(origin + t*normal, boundingBox.lower, boundingBox.upper); - - // Finally, set the new origin value. - originXSpinBox.setValue(updatedOrigin.x); - originYSpinBox.setValue(updatedOrigin.y); - originZSpinBox.setValue(updatedOrigin.z); -} - -void SliceWidget::setAnimation(bool set) { - - // Start or stop the animation timer. - if(set) - originSliderAnimationTimer.start(100); - else - originSliderAnimationTimer.stop(); -} - -void SliceWidget::animate() { - - int value = originSlider.value(); - - // Check if we need to reverse direction. - if(value + originSliderAnimationDirection < originSlider.minimum() || value + originSliderAnimationDirection > originSlider.maximum()) - originSliderAnimationDirection *= -1; - - // Increment slider position. - originSlider.setValue(value + originSliderAnimationDirection); -} diff --git a/apps/volumeViewer/SliceWidget.h b/apps/volumeViewer/SliceWidget.h deleted file mode 100644 index 08068170e8..0000000000 --- a/apps/volumeViewer/SliceWidget.h +++ /dev/null @@ -1,79 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include "ospcommon/box.h" -#include - -struct SliceParameters -{ - ospcommon::vec3f origin; - ospcommon::vec3f normal; -}; - -class SliceEditor; - - -class SliceWidget : public QFrame -{ -Q_OBJECT - -public: - - SliceWidget(SliceEditor *sliceEditor, ospcommon::box3f boundingBox); - ~SliceWidget(); - - SliceParameters getSliceParameters(); - -signals: - - void sliceChanged(); - void sliceDeleted(SliceWidget *); - -public slots: - - void load(std::string filename = std::string()); - -protected slots: - - void apply(); - void save(); - void originSliderValueChanged(int value); - void setAnimation(bool set); - void animate(); - -protected: - - //! Bounding box of the volume. - ospcommon::box3f boundingBox; - - //! UI elements. - QDoubleSpinBox originXSpinBox; - QDoubleSpinBox originYSpinBox; - QDoubleSpinBox originZSpinBox; - - QDoubleSpinBox normalXSpinBox; - QDoubleSpinBox normalYSpinBox; - QDoubleSpinBox normalZSpinBox; - - QSlider originSlider; - QPushButton originSliderAnimateButton; - QTimer originSliderAnimationTimer; - int originSliderAnimationDirection; - -}; diff --git a/apps/volumeViewer/TransferFunctionEditor.cpp b/apps/volumeViewer/TransferFunctionEditor.cpp deleted file mode 100644 index 61be49b3b9..0000000000 --- a/apps/volumeViewer/TransferFunctionEditor.cpp +++ /dev/null @@ -1,362 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "TransferFunctionEditor.h" -#include -#include -#include - -TransferFunctionEditor::TransferFunctionEditor(OSPTransferFunction transferFunction) - : transferFunction(transferFunction), dataRangeSet(false) -{ - // Make sure we have an existing transfer function. - if(!transferFunction) - throw std::runtime_error("must be constructed with an existing transfer function"); - - // Load color maps. - loadColorMaps(); - - // Setup UI elements. - QVBoxLayout * layout = new QVBoxLayout(); - layout->setSizeConstraint(QLayout::SetMinimumSize); - setLayout(layout); - - // Save and load buttons. - QWidget * saveLoadWidget = new QWidget(); - QHBoxLayout * hboxLayout = new QHBoxLayout(); - saveLoadWidget->setLayout(hboxLayout); - - QPushButton * saveButton = new QPushButton("Save"); - connect(saveButton, SIGNAL(clicked()), this, SLOT(save())); - hboxLayout->addWidget(saveButton); - - QPushButton * loadButton = new QPushButton("Load"); - connect(loadButton, SIGNAL(clicked()), this, SLOT(load())); - hboxLayout->addWidget(loadButton); - - layout->addWidget(saveLoadWidget); - - // Form layout. - QWidget * formWidget = new QWidget(); - QFormLayout * formLayout = new QFormLayout(); - formWidget->setLayout(formLayout); - QMargins margins = formLayout->contentsMargins(); - margins.setTop(0); margins.setBottom(0); - formLayout->setContentsMargins(margins); - layout->addWidget(formWidget); - - // Color map choice. - for(unsigned int i=0; iaddRow("Color map", &colorMapComboBox); - - // Data value range, used as the domain for both color and opacity components of the transfer function. - dataValueMinSpinBox.setValue(0.); - dataValueMaxSpinBox.setValue(1.); - dataValueMinSpinBox.setRange(-999999., 999999.); - dataValueMaxSpinBox.setRange(-999999., 999999.); - dataValueScaleSpinBox.setRange(-100, 100); - dataValueMinSpinBox.setDecimals(6); - dataValueMaxSpinBox.setDecimals(6); - - connect(&dataValueMinSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateDataValueRange())); - connect(&dataValueMaxSpinBox, SIGNAL(valueChanged(double)), this, SLOT(updateDataValueRange())); - connect(&dataValueScaleSpinBox, SIGNAL(valueChanged(int)), this, SLOT(updateDataValueRange())); - - formLayout->addRow("Data value min", &dataValueMinSpinBox); - formLayout->addRow("Data value max", &dataValueMaxSpinBox); - formLayout->addRow("Data value scale (10^X)", &dataValueScaleSpinBox); - - // Widget containing all opacity-related widgets. - QWidget * opacityGroup = new QWidget(); - QGridLayout * gridLayout = new QGridLayout(); - opacityGroup->setLayout(gridLayout); - - // Vertical axis label. - QLabel * verticalAxisLabel = new QLabel("O\np\na\nc\ni\nt\ny"); - verticalAxisLabel->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); - gridLayout->addWidget(verticalAxisLabel, 0, 0); - - // Opacity values widget. - connect(&opacityValuesWidget, SIGNAL(updated()), this, SLOT(updateOpacityValues())); - gridLayout->addWidget(&opacityValuesWidget, 0, 1); - - // Opacity scaling slider, defaults to median value in range. - opacityScalingSlider.setValue(int(0.5f * (opacityScalingSlider.minimum() + opacityScalingSlider.maximum()))); - opacityScalingSlider.setOrientation(Qt::Vertical); - - connect(&opacityScalingSlider, SIGNAL(valueChanged(int)), this, SLOT(updateOpacityValues())); - - gridLayout->addWidget(&opacityScalingSlider, 0, 2); - - // Horizontal axis label. - QLabel * horizontalAxisLabel = new QLabel("Data value"); - horizontalAxisLabel->setAlignment(Qt::AlignHCenter); - gridLayout->addWidget(horizontalAxisLabel, 1, 1); - - layout->addWidget(opacityGroup); - - // Set defaults. - setColorMapIndex(0); - updateOpacityValues(); -} - -void TransferFunctionEditor::load(std::string filename) -{ - // Get filename if not specified. - if(filename.empty()) { - filename = QFileDialog::getOpenFileName(this, tr("Load transfer function"), - ".", "Transfer function files (*.tfn)").toStdString(); - } - - if(filename.empty()) - return; - - // Get serialized transfer function state from file. - bool invalidIdentificationHeader = false; - try { - tfn::TransferFunction loadedTfn{ospcommon::FileName(filename)}; - - // Update transfer function state. Update values of the UI elements directly to signal appropriate slots. - std::vector::iterator fnd = std::find_if(colorMaps.begin(), colorMaps.end(), - [&](const ColorMap &m) { - return m.getName() == loadedTfn.name; - }); - int index = 0; - if (fnd != colorMaps.end()) { - *fnd = ColorMap(loadedTfn.name, loadedTfn.rgbValues); - index = std::distance(colorMaps.begin(), fnd); - } else { - colorMaps.push_back(ColorMap(loadedTfn.name, loadedTfn.rgbValues)); - colorMapComboBox.addItem(loadedTfn.name.c_str()); - index = colorMaps.size() - 1; - } - - // Commit and emit signal. - setDataValueRange(ospcommon::vec2f(loadedTfn.dataValueMin, loadedTfn.dataValueMax), true); - // Convert over to QPointF to pass to the widget - QVector points; - for (const auto &x : loadedTfn.opacityValues) { - points.push_back(QPointF(x.x, x.y)); - } - opacityValuesWidget.setPoints(points); - opacityScalingSlider.setValue(loadedTfn.opacityScaling - * (opacityScalingSlider.maximum() - opacityScalingSlider.minimum())); - updateOpacityValues(); - - colorMapComboBox.setCurrentIndex(index); - } catch (const std::runtime_error &e) { - const std::string errMsg = e.what(); - std::cout << "#ospVolumeViewer error loading transferfunction: " << errMsg << "\n"; - // If it's an invalid identification header error we can try loading an old style - // transfer function, otherwise something else we can't handle is wrong. - if (errMsg.find("Read invalid identification header") != std::string::npos) { - // Get serialized transfer function state from file. - QFile file(filename.c_str()); - bool success = file.open(QIODevice::ReadOnly); - - if (!success) { - std::cerr << "unable to open " << filename << std::endl; - return; - } - - QDataStream in(&file); - int colorMapIndex; - in >> colorMapIndex; - double dataValueMin, dataValueMax; - in >> dataValueMin >> dataValueMax; - QVector points; - in >> points; - int opacityScalingIndex; - in >> opacityScalingIndex; - - // Update transfer function state. Update values of the UI elements directly to signal appropriate slots. - colorMapComboBox.setCurrentIndex(colorMapIndex); - setDataValueRange(ospcommon::vec2f(dataValueMin, dataValueMax), true); - opacityValuesWidget.setPoints(points); - opacityScalingSlider.setValue(opacityScalingIndex); - - std::cout << "#ospVolumeViewer WARNING: using old-style transfer function, save the loaded function " - << "out to switch to the new format\n"; - } else { - throw e; - } - } - ospCommit(transferFunction); - emit committed(); -} - -void TransferFunctionEditor::setDataValueRange(ospcommon::vec2f dataValueRange, bool force) -{ - // Only update widget values the first time. - if(dataRangeSet && !force) - return; - - dataRangeSet = true; - - // Determine appropriate scaling exponent (base 10) for the data value range in the widget. - int scaleExponent = round(log10f(0.5f * (dataValueRange.y - dataValueRange.x))); - - // Don't use a scaling exponent <= 5. - if(abs(scaleExponent) <= 5) - scaleExponent = 0; - - // Set widget values. - dataValueMinSpinBox.setValue(dataValueRange.x / powf(10.f, scaleExponent)); - dataValueMaxSpinBox.setValue(dataValueRange.y / powf(10.f, scaleExponent)); - dataValueScaleSpinBox.setValue(scaleExponent); -} - -void TransferFunctionEditor::updateOpacityValues() -{ - // Default to 256 discretizations of the opacities over the domain. - std::vector opacityValues = opacityValuesWidget.getInterpolatedValuesOverInterval(256); - - // Opacity scaling factor (normalized in [0, 1]). - const float opacityScalingNormalized = float(opacityScalingSlider.value() - opacityScalingSlider.minimum()) - / float(opacityScalingSlider.maximum() - opacityScalingSlider.minimum()); - - // Scale opacity values. - for (unsigned int i=0; i < opacityValues.size(); i++) - opacityValues[i] *= opacityScalingNormalized; - - // Update OSPRay transfer function. - OSPData opacityValuesData = ospNewData(opacityValues.size(), OSP_FLOAT, opacityValues.data()); - ospSetData(transferFunction, "opacities", opacityValuesData); - - // Commit and emit signal. - ospCommit(transferFunction); - emit committed(); -} - -void TransferFunctionEditor::save() -{ - // Get filename. - QString filename = QFileDialog::getSaveFileName(this, "Save transfer function", ".", "Transfer function files (*.tfn)"); - - if(filename.isNull()) - return; - - // Make sure the filename has the proper extension. - if(filename.endsWith(".tfn") != true) - filename += ".tfn"; - - const std::vector colors = colorMaps[colorMapComboBox.currentIndex()].getColors(); - const QVector opacityPts = opacityValuesWidget.getPoints(); - std::vector opacityValues; - for (const auto &x : opacityPts) { - opacityValues.push_back(ospcommon::vec2f(x.x(), x.y())); - } - const float dataValueScale = powf(10.f, float(dataValueScaleSpinBox.value())); - const float valMin = dataValueScale * dataValueMinSpinBox.value(); - const float valMax = dataValueScale * dataValueMaxSpinBox.value(); - const float opacityScaling = opacityScalingSlider.value() - / float(opacityScalingSlider.maximum() - opacityScalingSlider.minimum()); - ospcommon::FileName ospFname{filename.toStdString()}; - tfn::TransferFunction saveTfn(ospFname.name(), colors, opacityValues, valMin, valMax, opacityScaling); - saveTfn.save(ospFname); -} - -void TransferFunctionEditor::setColorMapIndex(int index) -{ - // Set transfer function color properties for this color map. - std::vector colors = colorMaps[index].getColors(); - - OSPData colorsData = ospNewData(colors.size(), OSP_FLOAT3, colors.data()); - ospSetData(transferFunction, "colors", colorsData); - - // Set transfer function widget background image. - opacityValuesWidget.setBackgroundImage(colorMaps[index].getImage()); - - // Commit and emit signal. - ospCommit(transferFunction); - emit committed(); -} - -void TransferFunctionEditor::updateDataValueRange() -{ - // Data value scale. - float dataValueScale = powf(10.f, float(dataValueScaleSpinBox.value())); - - // Set the minimum and maximum values in the domain for both color and opacity components of the transfer function. - ospcommon::vec2f range(dataValueScale * float(dataValueMinSpinBox.value()), dataValueScale * float(dataValueMaxSpinBox.value())); - ospSet2f(transferFunction, "valueRange", range.x, range.y); - - // Commit and emit signal. - ospCommit(transferFunction); - emit committed(); -} - -void TransferFunctionEditor::loadColorMaps() -{ - // Color maps based on ParaView default color maps. - - std::vector colors; - - // Jet. - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 0.562493 )); - colors.push_back(ospcommon::vec3f(0 , 0 , 1 )); - colors.push_back(ospcommon::vec3f(0 , 1 , 1 )); - colors.push_back(ospcommon::vec3f(0.500008 , 1 , 0.500008 )); - colors.push_back(ospcommon::vec3f(1 , 1 , 0 )); - colors.push_back(ospcommon::vec3f(1 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0.500008 , 0 , 0 )); - colorMaps.push_back(ColorMap("Jet", colors)); - - // Ice / fire. - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0 , 0.120394 , 0.302678 )); - colors.push_back(ospcommon::vec3f(0 , 0.216587 , 0.524575 )); - colors.push_back(ospcommon::vec3f(0.0552529 , 0.345022 , 0.659495 )); - colors.push_back(ospcommon::vec3f(0.128054 , 0.492592 , 0.720287 )); - colors.push_back(ospcommon::vec3f(0.188952 , 0.641306 , 0.792096 )); - colors.push_back(ospcommon::vec3f(0.327672 , 0.784939 , 0.873426 )); - colors.push_back(ospcommon::vec3f(0.60824 , 0.892164 , 0.935546 )); - colors.push_back(ospcommon::vec3f(0.881376 , 0.912184 , 0.818097 )); - colors.push_back(ospcommon::vec3f(0.9514 , 0.835615 , 0.449271 )); - colors.push_back(ospcommon::vec3f(0.904479 , 0.690486 , 0 )); - colors.push_back(ospcommon::vec3f(0.854063 , 0.510857 , 0 )); - colors.push_back(ospcommon::vec3f(0.777096 , 0.330175 , 0.000885023 )); - colors.push_back(ospcommon::vec3f(0.672862 , 0.139086 , 0.00270085 )); - colors.push_back(ospcommon::vec3f(0.508812 , 0 , 0 )); - colors.push_back(ospcommon::vec3f(0.299413 , 0.000366217 , 0.000549325 )); - colors.push_back(ospcommon::vec3f(0.0157473 , 0.00332647 , 0 )); - colorMaps.push_back(ColorMap("Ice / Fire", colors)); - - // Cool to warm. - colors.clear(); - colors.push_back(ospcommon::vec3f(0.231373 , 0.298039 , 0.752941 )); - colors.push_back(ospcommon::vec3f(0.865003 , 0.865003 , 0.865003 )); - colors.push_back(ospcommon::vec3f(0.705882 , 0.0156863 , 0.14902 )); - colorMaps.push_back(ColorMap("Cool to Warm", colors)); - - // Blue to red rainbow. - colors.clear(); - colors.push_back(ospcommon::vec3f(0 , 0 , 1 )); - colors.push_back(ospcommon::vec3f(1 , 0 , 0 )); - colorMaps.push_back(ColorMap("Blue to Red Rainbow", colors)); - - // Grayscale. - colors.clear(); - colors.push_back(ospcommon::vec3f(0.)); - colors.push_back(ospcommon::vec3f(1.)); - colorMaps.push_back(ColorMap("Grayscale", colors)); -} diff --git a/apps/volumeViewer/TransferFunctionEditor.h b/apps/volumeViewer/TransferFunctionEditor.h deleted file mode 100644 index 7d14a3a71f..0000000000 --- a/apps/volumeViewer/TransferFunctionEditor.h +++ /dev/null @@ -1,92 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "ColorMap.h" -#include "LinearTransferFunctionWidget.h" -#include -#include - -class TransferFunctionEditor : public QWidget -{ - Q_OBJECT - - public: - - //! Construct the transfer function editor with the given OSPRay transfer function object. The editor will manipulate the OSPRay object directly. - TransferFunctionEditor(OSPTransferFunction transferFunction); - -signals: - - //! Signal emitted whenever the transfer function has been changed and committed. - void committed(); - -public slots: - - //! Load transfer function from file. If no filename given, a file selection dialog is created. - void load(std::string filename = std::string()); - - //! Set the data value range for the transfer function editor. - void setDataValueRange(ospcommon::vec2f dataValueRange, bool force=false); - - //! Slot called to update transfer function opacity values based on widget values. - void updateOpacityValues(); - -protected slots: - - //! Save transfer function to file. - void save(); - - //! Change active color map. - void setColorMapIndex(int index); - - //! Slot called to update data value range based on widget values. - void updateDataValueRange(); - -protected: - - //! Load default color maps. - void loadColorMaps(); - - //! OSPRay transfer function object. - OSPTransferFunction transferFunction; - - //! Available color maps. - std::vector colorMaps; - - //! Color map selection widget. - QComboBox colorMapComboBox; - - //! Indicates if the data value range has been set; we don't update the min / max widget values after the first time it's set. - bool dataRangeSet; - - //! Data value range minimum widget. - QDoubleSpinBox dataValueMinSpinBox; - - //! Data value range maximum widget. - QDoubleSpinBox dataValueMaxSpinBox; - - //! Data value range scale (exponent, base 10) widget. - QSpinBox dataValueScaleSpinBox; - - //! Linear transfer function widget for opacity values. - LinearTransferFunctionWidget opacityValuesWidget; - - //! Opacity scaling slider. - QSlider opacityScalingSlider; - -}; diff --git a/apps/volumeViewer/VolumeViewer.cpp b/apps/volumeViewer/VolumeViewer.cpp deleted file mode 100644 index e08044ea79..0000000000 --- a/apps/volumeViewer/VolumeViewer.cpp +++ /dev/null @@ -1,966 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include -#include "VolumeViewer.h" -#include "TransferFunctionEditor.h" -#include "IsosurfaceEditor.h" -#include "SliceEditor.h" -#include "PreferencesDialog.h" -#include "ProbeWidget.h" -#include "OpenGLAnnotationRenderer.h" -#include "commandline/SceneParser/trianglemesh/TriangleMeshSceneParser.h" -#include -#include "ospcommon/FileName.h" - -#include "importer/Importer.h" - -using namespace ospray; -using namespace ospcommon; - -VolumeViewer::VolumeViewer(const std::vector &objectFileFilenames, - std::string renderer_type, - bool ownModelPerObject, - bool showFrameRate, - bool fullScreen, - std::string writeFramesFilename) - : objectFileFilenames(objectFileFilenames), - modelIndex(0), - ownModelPerObject(ownModelPerObject), - boundingBox(ospcommon::vec3f(0.f), ospcommon::vec3f(1.f)), - renderer(NULL), - rendererInitialized(false), - transferFunction(NULL), - ambientLight(NULL), - directionalLight(NULL), - osprayWindow(NULL), - annotationRenderer(NULL), - transferFunctionEditor(NULL), - isosurfaceEditor(NULL), - autoRotateAction(NULL), - autoRotationRate(0.025f), - usePlane(-1), - samplingRate(-1), - adaptiveMaxSamplingRate(-1), - preferencesDialog(nullptr), - spp(-1), - shadows(-1), - preIntegration(-1), - aoSamples(-1), - adaptiveSampling(-1), - gradientShadingEnabled(-1) -{ - // Default window size. - resize(1024, 768); - - // Create and configure the OSPRay state. - initObjects(renderer_type); - - // Create an OSPRay window and set it as the central widget, but don't let it start rendering until we're done with setup. - osprayWindow = new QOSPRayWindow(this, this->renderer, - showFrameRate, writeFramesFilename); - setCentralWidget(osprayWindow); - - // Set the window bounds based on the OSPRay world bounds. - osprayWindow->setWorldBounds(boundingBox); - - // Configure the user interface widgets and callbacks. - initUserInterfaceWidgets(); - - if (fullScreen) - setWindowState(windowState() | Qt::WindowFullScreen); - - // Show the window. - show(); - - setGradientShadingEnabled(true); - setAOSamples(1); - setAdaptiveSampling(true); - setPreIntegration(true); - setShadows(true); - setSPP(1); - setPlane(1); - -} - -ospcommon::box3f VolumeViewer::getBoundingBox() -{ - return boundingBox; -} - -QOSPRayWindow *VolumeViewer::getWindow() -{ - return osprayWindow; -} - -TransferFunctionEditor *VolumeViewer::getTransferFunctionEditor() -{ - return transferFunctionEditor; -} - -void VolumeViewer::setModel(size_t index) -{ - modelIndex = index; - - // Set current model on the OSPRay renderer. - ospSetObject(renderer, "model", modelStates[index].model); - ospCommit(renderer); - rendererInitialized = true; - - PRINT(modelStates[index].volumes.size()); - if (!modelStates[index].volumes.empty()) { - // Update transfer function and isosurface editor data value range with the voxel range of the current model's first volume. - OSPVolume volume = modelStates[index].volumes[0]->handle; - ospcommon::vec2f voxelRange = modelStates[index].volumes[0]->voxelRange; - - if(voxelRange != ospcommon::vec2f(0.f)) { - transferFunctionEditor->setDataValueRange(voxelRange); - isosurfaceEditor->setDataValueRange(voxelRange); - } - - // Update active volume on probe widget. - probeWidget->setVolume(modelStates[index].volumes[0]->handle); - - // Update current filename information label. - if (ownModelPerObject) - currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": Data value range: [") + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); - else - currentFilenameInfoLabel.setText("Timestep " + QString::number(index) + QString(": ") + QString(objectFileFilenames[index].c_str()).split('/').back() + ". Data value range: [" + QString::number(voxelRange.x) + ", " + QString::number(voxelRange.y) + "]"); - } - // Enable rendering on the OSPRay window. - osprayWindow->setRenderingEnabled(true); -} - -std::string VolumeViewer::toString() const -{ - return("VolumeViewer"); -} - -void VolumeViewer::autoRotate(bool set) -{ - if(osprayWindow == NULL) - return; - - if(autoRotateAction != NULL) - autoRotateAction->setChecked(set); - - if(set) { - osprayWindow->setRotationRate(autoRotationRate); - osprayWindow->updateGL(); - } - else - osprayWindow->setRotationRate(0.); -} - -void VolumeViewer::setAutoRotationRate(float rate) -{ - autoRotationRate = rate; -} - -void VolumeViewer::nextTimeStep() -{ - modelIndex = (modelIndex + 1) % modelStates.size(); - setModel(modelIndex); - render(); -} - -void VolumeViewer::playTimeSteps(bool animate) -{ - if (animate == true) - playTimeStepsTimer.start(500); - else - playTimeStepsTimer.stop(); -} - -void VolumeViewer::addSlice(std::string filename) -{ - sliceEditor->addSlice(filename); -} - -void VolumeViewer::addGeometry(std::string filename) -{ - // For now we assume PLY geometry files. Later we can support other geometry formats. - - // Get filename if not specified. - if(filename.empty()) - filename = QFileDialog::getOpenFileName(this, tr("Load geometry"), ".", "Geometry files (*)").toStdString(); - - if(filename.empty()) - return; - - // Attempt to load the geometry through the TriangleMeshFile loader. - // OSPGeometry triangleMesh = ospNewGeometry("trianglemesh"); - - // If successful, commit the triangle mesh and add it to all models. - // if(TriangleMeshFile::importTriangleMesh(filename, triangleMesh) != NULL) { - ospcommon::FileName fn = filename; - ospray::miniSG::Model* msgModel = new miniSG::Model; - bool loadedSGScene = false; - if (fn.ext() == "stl") { - miniSG::importSTL(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "msg") { - miniSG::importMSG(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "tri") { - miniSG::importTRI(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "xml") { - miniSG::importRIVL(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "obj") { - miniSG::importOBJ(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "hbp") { - miniSG::importHBP(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "x3d") { - miniSG::importX3D(*msgModel,fn); - loadedSGScene = true; - } else if (fn.ext() == "astl") { - // miniSG::importSTL(msgAnimation,fn); - // loadedSGScene = true; - } else { - ospray::importer::Group *newStuff = ospray::importer::import(filename); - if (!newStuff) return; - if (newStuff->geometry.size() != 1) return; - - OSPGeometry triangleMesh = newStuff->geometry[0]->handle; - // For now: if this is a DDS geometry, assume it is a horizon and its color should be mapped through the first volume's transfer function. - if(QString(filename.c_str()).endsWith(".dds") && modelStates.size() > 0 && modelStates[0].volumes.size() > 0) { - - OSPMaterial material = ospNewMaterial(renderer, "default"); - ospSet3f(material, "Kd", 1,1,1); - ospSetObject(material, "volume", modelStates[0].volumes[0]->handle); - ospCommit(material); - - ospSetMaterial(triangleMesh, material); - ospCommit(triangleMesh); - - // Create an instance of the geometry and add the instance to the main model(s)--this prevents the geometry - // from being rebuilt every time the main model is committed (e.g. when slices / isosurfaces are manipulated) - OSPModel modelInstance = ospNewModel(); - ospAddGeometry(modelInstance, triangleMesh); - ospCommit(modelInstance); - - ospcommon::affine3f xfm = ospcommon::one; - OSPGeometry triangleMeshInstance = ospNewInstance(modelInstance, (osp::affine3f&)xfm); - ospCommit(triangleMeshInstance); - - for(size_t i=0; i instanceModels; - - for (size_t i = 0; i < msgModel->mesh.size(); i++) { - Ref msgMesh = msgModel->mesh[i]; - TriangleMeshSceneParser parser(ospray::cpp::Renderer(), "triangles"); - auto ospMesh = parser.createOSPRayGeometry(msgModel, msgMesh.ptr); - - OSPMaterial mat = ospNewMaterial(renderer, "OBJMaterial"); - ospSet3f(mat,"Kd",.8f,.8f,.8f); - ospCommit(mat); - ospSetMaterial(ospMesh.handle(), mat); - ospCommit(ospMesh.handle()); - - cpp::Model model_i; - model_i.addGeometry(ospMesh); - model_i.commit(); - instanceModels.push_back(model_i.handle()); - } - - for (size_t i = 0; i < msgModel->instance.size(); i++) { - msgModel->instance[i].xfm = msgModel->instance[i].xfm*ospcommon::affine3f::translate(ospcommon::vec3f(16,16,.1)); // hack for landing gear - OSPGeometry inst = - ospNewInstance(instanceModels[msgModel->instance[i].meshID], - reinterpret_cast(msgModel->instance[i].xfm)); - ospCommit(inst); - // sceneModel->addGeometry(inst); - for(size_t i=0; igetViewport()) << std::endl; - - // Get filename if not specified. - if(filename.empty()) - filename = QFileDialog::getSaveFileName(this, tr("Save screenshot"), ".", "PNG files (*.png)").toStdString(); - - if(filename.empty()) - return; - - // Make sure the filename has the proper extension. - if(QString(filename.c_str()).endsWith(".png") != true) - filename += ".png"; - - // Grab the image. - QImage image = osprayWindow->grabFrameBuffer(); - - // Save the screenshot. - bool success = image.save(filename.c_str()); - - std::cout << (success ? "saved screenshot to " : "failed saving screenshot ") << filename << std::endl; -} - -void VolumeViewer::keyPressEvent(QKeyEvent * event) -{ - if (event->key() == Qt::Key_Escape){ - close(); - } - else if (event->key() == Qt::Key_P){ - std::cout << "View parameters (use on command line to reproduce view): " << std::endl - << " " << *(osprayWindow->getViewport()) << std::endl; - } - else if (event->key() == Qt::Key_L){ - std::cout << "Light parameters (use on command line to reproduce view): " << std::endl - << " " << *lightEditor << std::endl; - } -} - -void VolumeViewer::commitVolumes() -{ - for(size_t i=0; ihandle); -} - -void VolumeViewer::render() -{ - if (osprayWindow != NULL) { - osprayWindow->resetAccumulationBuffer(); - osprayWindow->updateGL(); - } -} - -void VolumeViewer::setRenderAnnotationsEnabled(bool value) -{ - if (value) { - if (!annotationRenderer) - annotationRenderer = new OpenGLAnnotationRenderer(this); - - connect(osprayWindow, SIGNAL(renderGLComponents()), annotationRenderer, SLOT(render()), Qt::UniqueConnection); - } - else { - delete annotationRenderer; - annotationRenderer = NULL; - } - - render(); -} - -void VolumeViewer::setSubsamplingInteractionEnabled(bool value) -{ - ospSet1i(renderer, "spp", value ? -1 : 1); - if(rendererInitialized) - ospCommit(renderer); -} - -void VolumeViewer::setGradientShadingEnabled(bool value) -{ - if (gradientShadingEnabled != value) - { - for(size_t i=0; ihandle, "gradientShadingEnabled", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); - gradientShadingEnabled = value; - if (preferencesDialog) - preferencesDialog->setGradientShadingEnabled(value); - } -} - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setPreIntegration(bool value) -{ - if (preIntegration != value) - { - ospSet1i(transferFunction, "preIntegration", value); - ospCommit(transferFunction); - - render(); - preIntegration = value; - if (preferencesDialog) - preferencesDialog->setPreIntegration(value); - } -} - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setSingleShade(bool value) -{ - for(size_t i=0; ihandle, "singleShade", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); -} - -void VolumeViewer::setShadows(bool value) -{ - if (shadows != value) - { - ospSet1i(renderer, "shadowsEnabled", value); - if(rendererInitialized) - ospCommit(renderer); - - render(); - shadows = value; - if (preferencesDialog) - preferencesDialog->setShadows(value); - } -} - -void VolumeViewer::setPlane(bool st) -{ - if (usePlane != st) - { - usePlane = st; - if (planeMesh) - { - for(size_t i=0; isetPlane(st); - } -} - -void VolumeViewer::setAOWeight(double value) -{ - ospSet1f(renderer, "aoWeight", value); - if(rendererInitialized) - ospCommit(renderer); - render(); -} - -void VolumeViewer::setAOSamples(int value) -{ - if (aoSamples != value) - { - ospSet1i(renderer, "aoSamples", value); - if(rendererInitialized) - ospCommit(renderer); - render(); - aoSamples = value; - if (preferencesDialog) - preferencesDialog->setAOSamples(value); - } -} - -void VolumeViewer::setSPP(int value) -{ - if (spp != value) - { - ospSet1i(renderer, "spp", value); - if(rendererInitialized) - ospCommit(renderer); - render(); - spp = value; - if (preferencesDialog) - preferencesDialog->setSPP(value); - } -} - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setAdaptiveScalar(double value) -{ - for(size_t i=0; ihandle, "adaptiveScalar", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - render(); -} - - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setAdaptiveMaxSamplingRate(double value) -{ - if (adaptiveMaxSamplingRate != value) - { - for(size_t i=0; ihandle, "adaptiveMaxSamplingRate", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - render(); - adaptiveMaxSamplingRate = value; - if (preferencesDialog) - preferencesDialog->setAdaptiveMaxSamplingRate(value); - } -} - - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setAdaptiveBacktrack(double value) -{ - for(size_t i=0; ihandle, "adaptiveBacktrack", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); -} - -//! Set gradient shading flag on all volumes. -void VolumeViewer::setAdaptiveSampling(bool value) -{ - if (value != adaptiveSampling) - { - for(size_t i=0; ihandle, "adaptiveSampling", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); - adaptiveSampling = value; - if (preferencesDialog) - preferencesDialog->setAdaptiveSampling(value); - } -} - -void VolumeViewer::setSamplingRate(double value) -{ - if (samplingRate != value) - { - for(size_t i=0; ihandle, "samplingRate", value); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); - samplingRate = value; - if (preferencesDialog) - preferencesDialog->setSamplingRate(value); -} -} - -void VolumeViewer::setVolumeClippingBox(ospcommon::box3f value) -{ - for(size_t i=0; ihandle, "volumeClippingBoxLower", &value.lower.x); - ospSet3fv(modelStates[i].volumes[j]->handle, "volumeClippingBoxUpper", &value.upper.x); - ospCommit(modelStates[i].volumes[j]->handle); - } - - render(); -} - -void VolumeViewer::setSlices(std::vector sliceParameters) -{ - // Provide the slices to OSPRay as the coefficients (a,b,c,d) of the plane equation ax + by + cz + d = 0. - std::vector planes; - - for(size_t i=0; ihandle); - - modelStates[i].slices.clear(); - } - - // Add new slices for each volume of each model. Later we can do this only for the active model on time step change... - for(size_t i=0; i 0) { - for(size_t j=0; jhandle); - ospCommit(slicesGeometry); - - ospAddGeometry(modelStates[i].model, slicesGeometry); - - modelStates[i].slices.push_back(new ModelState::Geometry(slicesGeometry)); - } - } - - ospCommit(modelStates[i].model); - } - - render(); -} - -void VolumeViewer::setIsovalues(std::vector isovalues) -{ - OSPData isovaluesData = ospNewData(isovalues.size(), OSP_FLOAT, &isovalues[0]); - - // Remove existing isosurface geometries from models. - for(size_t i=0; ihandle); - - modelStates[i].isosurfaces.clear(); - } - - // Add new isosurfaces for each volume of each model. Later we can do this only for the active model on time step change... - for(size_t i=0; i 0) { - for(size_t j=0; jhandle); - ospCommit(isosurfacesGeometry); - - ospAddGeometry(modelStates[i].model, isosurfacesGeometry); - - modelStates[i].isosurfaces.push_back(new ModelState::Geometry(isosurfacesGeometry)); - } - } - - ospCommit(modelStates[i].model); - } - - render(); -} - -void VolumeViewer::importObjectsFromFile(const std::string &filename) -{ - if (!ownModelPerObject) - // Create an OSPRay model and its associated model state. - modelStates.push_back(ModelState(ospNewModel())); - - // Load OSPRay objects from a file. - // OSPObject *objects = ObjectFile::importObjects(filename.c_str()); - ospray::importer::Group *imported = ospray::importer::import(filename); - assert(imported); - -#if 1 - PING; - // Iterate over the GEOMETREIS contained in the object list. - PRINT(imported->geometry.size()); - - for (size_t i=0 ; i < imported->geometry.size() ; i++) { - if (ownModelPerObject) - modelStates.push_back(ModelState(ospNewModel())); - - // Commit the geometry. - ospCommit(imported->geometry[i]->handle); - - // Add the loaded geometry to the model. - ospAddGeometry(modelStates.back().model, imported->geometry[i]->handle); - - if (ownModelPerObject) - ospCommit(modelStates.back().model); - } - // Iterate over the objects contained in the object list. - for (size_t i=0 ; i < imported->volume.size() ; i++) { - if (ownModelPerObject) - modelStates.push_back(ModelState(ospNewModel())); - - ospray::importer::Volume *vol = imported->volume[i]; - assert(vol); - // For now we set the same transfer function on all volumes. - ospSetObject(vol->handle, "transferFunction", transferFunction); - ospCommit(vol->handle); - - // Add the loaded volume(s) to the model. - ospAddVolume(modelStates.back().model, vol->handle); - - assert(!vol->bounds.empty()); - // Add to volumes vector for the current model. - modelStates.back().volumes.push_back(new ModelState::Volume(vol->handle, - vol->bounds, - vol->voxelRange - )); - - if (ownModelPerObject) - ospCommit(modelStates.back().model); - } -#else - // Iterate over the objects contained in the object list. - for (size_t i=0 ; objects[i] ; i++) { - if (ownModelPerObject) - modelStates.push_back(ModelState(ospNewModel())); - - OSPDataType type; - ospGetType(objects[i], NULL, &type); - - if (type == OSP_GEOMETRY) { - - // Commit the geometry. - ospCommit(objects[i]); - - // Add the loaded geometry to the model. - ospAddGeometry(modelStates.back().model, (OSPGeometry) objects[i]); - - } else if (type == OSP_VOLUME) { - - // For now we set the same transfer function on all volumes. - ospSetObject(objects[i], "transferFunction", transferFunction); - ospCommit(objects[i]); - - // Add the loaded volume(s) to the model. - ospAddVolume(modelStates.back().model, (OSPVolume) objects[i]); - - // Add to volumes vector for the current model. - modelStates.back().volumes.push_back((OSPVolume) objects[i]); - } - - if (ownModelPerObject) - ospCommit(modelStates.back().model); - } -#endif - - if (!ownModelPerObject) - // Commit the model. - ospCommit(modelStates.back().model); -} - -void VolumeViewer::initObjects(const std::string &renderer_type) -{ - // Create an OSPRay renderer. - renderer = ospNewRenderer(renderer_type.c_str()); - exitOnCondition(renderer == NULL, "could not create OSPRay renderer object"); - - // Set renderer defaults (if not using 'aoX' renderers) - if (renderer_type[0] != 'a' && renderer_type[1] != 'o') - { - ospSet1i(renderer, "aoSamples", 1); - ospSet1i(renderer, "shadowsEnabled", 1); - ospSet1i(renderer, "aoTransparencyEnabled", 1); - } - - // Create OSPRay ambient and directional lights. GUI elements will modify their parameters. - ambientLight = ospNewLight(renderer, "AmbientLight"); - exitOnCondition(ambientLight == NULL, "could not create ambient light"); - ospSet3f(ambientLight, "color", 0.3f, 0.5f, 1.f); - ospCommit(ambientLight); - - directionalLight = ospNewLight(renderer, "DirectionalLight"); - exitOnCondition(directionalLight == NULL, "could not create directional light"); - ospSet3f(directionalLight, "color", 1.f, 0.9f, 0.4f); - ospCommit(directionalLight); - - // Set the light sources on the renderer. - std::vector lights; - lights.push_back(ambientLight); - lights.push_back(directionalLight); - - ospSetData(renderer, "lights", ospNewData(lights.size(), OSP_OBJECT, &lights[0])); - - // Create an OSPRay transfer function. - auto tfFromEnv = getEnvVar("OSPRAY_USE_TF_TYPE"); - - if (tfFromEnv.first) { - transferFunction = ospNewTransferFunction(tfFromEnv.second.c_str()); - } else { - transferFunction = ospNewTransferFunction("piecewise_linear"); - } - exitOnCondition(transferFunction == NULL, "could not create OSPRay transfer function object"); - ospCommit(transferFunction); - - // Load OSPRay objects from files. - for (size_t i=0 ; i < objectFileFilenames.size() ; i++) - importObjectsFromFile(objectFileFilenames[i]); - - - boundingBox = ospcommon::empty; - if (!modelStates.empty()) { - for (size_t i=0; iboundingBox); - } - PRINT(boundingBox); - - //add plane - OSPMaterial planeMaterial = ospNewMaterial(renderer,"default"); - ospSet3f(planeMaterial,"Kd",.5,.5,.5); - ospSet3f(planeMaterial,"Ks",0,0,0); - ospSet1f(planeMaterial,"Ns",0); - ospCommit(planeMaterial); - - osp::vec3f *vertices = new osp::vec3f[4]; - float ps = 100000.f; - float py = boundingBox.upper.y+1.f; -#if 1 - vertices[0] = osp::vec3f{-ps, -ps, py}; - vertices[1] = osp::vec3f{-ps, ps, py}; - vertices[2] = osp::vec3f{ ps, -ps, py}; - vertices[3] = osp::vec3f{ ps, ps, py}; -#else - vertices[0] = osp::vec3f{-ps, py, -ps}; - vertices[1] = osp::vec3f{-ps, py, ps}; - vertices[2] = osp::vec3f{ps, py, -ps}; - vertices[3] = osp::vec3f{ps, py, ps}; -#endif - - planeMesh = ospNewGeometry("triangles"); - OSPData position = ospNewData(4, OSP_FLOAT3, &vertices[0]); - ospCommit(position); - ospSetData(planeMesh, "vertex", position); - - osp::vec3i *triangles = new osp::vec3i[2]; - triangles[0] = osp::vec3i{0,1,2}; - triangles[1] = osp::vec3i{1,2,3}; - - OSPData index = ospNewData(2, OSP_INT3, &triangles[0]); - ospCommit(index); - ospSetData(planeMesh, "index", index); - delete[] triangles; - - ospSetMaterial(planeMesh, planeMaterial); - ospCommit(planeMesh); - setPlane(usePlane); - ospRelease(index); - - osp::vec3f specular = osp::vec3f{0.135f,0.135f,0.135f}; - for(size_t i=0; ihandle, "specular", &specular.x); - ospCommit(modelStates[i].volumes[j]->handle); - } -} - -void VolumeViewer::initUserInterfaceWidgets() -{ - // Create a toolbar at the top of the window. - QToolBar *toolbar = addToolBar("toolbar"); - - // Add preferences widget and callback. - preferencesDialog = new PreferencesDialog(this, boundingBox); - QAction *showPreferencesAction = new QAction("Preferences", this); - connect(showPreferencesAction, SIGNAL(triggered()), preferencesDialog, SLOT(show())); - toolbar->addAction(showPreferencesAction); - preferencesDialog->setSamplingRate(samplingRate); - preferencesDialog->setAdaptiveMaxSamplingRate(adaptiveMaxSamplingRate); - - // Add the "auto rotate" widget and callback. - autoRotateAction = new QAction("Auto rotate", this); - autoRotateAction->setCheckable(true); - connect(autoRotateAction, SIGNAL(toggled(bool)), this, SLOT(autoRotate(bool))); - toolbar->addAction(autoRotateAction); - - // Add the "next timestep" widget and callback. - QAction *nextTimeStepAction = new QAction("Next timestep", this); - connect(nextTimeStepAction, SIGNAL(triggered()), this, SLOT(nextTimeStep())); - toolbar->addAction(nextTimeStepAction); - - // Add the "play timesteps" widget and callback. - QAction *playTimeStepsAction = new QAction("Play timesteps", this); - playTimeStepsAction->setCheckable(true); - connect(playTimeStepsAction, SIGNAL(toggled(bool)), this, SLOT(playTimeSteps(bool))); - toolbar->addAction(playTimeStepsAction); - - // Connect the "play timesteps" timer. - connect(&playTimeStepsTimer, SIGNAL(timeout()), this, SLOT(nextTimeStep())); - - // Add the "add geometry" widget and callback. - QAction *addGeometryAction = new QAction("Add geometry", this); - connect(addGeometryAction, SIGNAL(triggered()), this, SLOT(addGeometry())); - toolbar->addAction(addGeometryAction); - - // Add the "screenshot" widget and callback. - QAction *screenshotAction = new QAction("Screenshot", this); - connect(screenshotAction, SIGNAL(triggered()), this, SLOT(screenshot())); - toolbar->addAction(screenshotAction); - - // Create the transfer function editor dock widget, this widget modifies the transfer function directly. - QDockWidget *transferFunctionEditorDockWidget = new QDockWidget("Transfer Function", this); - transferFunctionEditor = new TransferFunctionEditor(transferFunction); - transferFunctionEditorDockWidget->setWidget(transferFunctionEditor); - connect(transferFunctionEditor, SIGNAL(committed()), this, SLOT(commitVolumes())); - connect(transferFunctionEditor, SIGNAL(committed()), this, SLOT(render())); - addDockWidget(Qt::LeftDockWidgetArea, transferFunctionEditorDockWidget); - - // Set the transfer function editor widget to its minimum allowed height, to leave room for other dock widgets. - transferFunctionEditor->setMaximumHeight(transferFunctionEditor->minimumSize().height()); - - // Create slice editor dock widget. - QDockWidget *sliceEditorDockWidget = new QDockWidget("Slices", this); - sliceEditor = new SliceEditor(boundingBox); - sliceEditorDockWidget->setWidget(sliceEditor); - connect(sliceEditor, SIGNAL(slicesChanged(std::vector)), this, SLOT(setSlices(std::vector))); - addDockWidget(Qt::LeftDockWidgetArea, sliceEditorDockWidget); - - // Create isosurface editor dock widget. - QDockWidget *isosurfaceEditorDockWidget = new QDockWidget("Isosurfaces", this); - isosurfaceEditor = new IsosurfaceEditor(); - isosurfaceEditorDockWidget->setWidget(isosurfaceEditor); - connect(isosurfaceEditor, SIGNAL(isovaluesChanged(std::vector)), this, SLOT(setIsovalues(std::vector))); - addDockWidget(Qt::LeftDockWidgetArea, isosurfaceEditorDockWidget); - - // Create the light editor dock widget, this widget modifies the light directly. - QDockWidget *lightEditorDockWidget = new QDockWidget("Lights", this); - lightEditor = new LightEditor(ambientLight, directionalLight); - lightEditorDockWidget->setWidget(lightEditor); - connect(lightEditor, SIGNAL(lightsChanged()), this, SLOT(render())); - addDockWidget(Qt::LeftDockWidgetArea, lightEditorDockWidget); - - // Create the probe dock widget. - QDockWidget *probeDockWidget = new QDockWidget("Probe", this); - probeWidget = new ProbeWidget(this); - probeDockWidget->setWidget(probeWidget); - addDockWidget(Qt::LeftDockWidgetArea, probeDockWidget); - - // Tabify dock widgets. - tabifyDockWidget(transferFunctionEditorDockWidget, sliceEditorDockWidget); - tabifyDockWidget(transferFunctionEditorDockWidget, isosurfaceEditorDockWidget); - tabifyDockWidget(transferFunctionEditorDockWidget, lightEditorDockWidget); - tabifyDockWidget(transferFunctionEditorDockWidget, probeDockWidget); - - // Tabs on top. - setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); - - // Default to showing transfer function tab widget. - transferFunctionEditorDockWidget->raise(); - - // Add the current OSPRay object file label to the bottom status bar. - statusBar()->addWidget(¤tFilenameInfoLabel); -} diff --git a/apps/volumeViewer/VolumeViewer.h b/apps/volumeViewer/VolumeViewer.h deleted file mode 100644 index 85a78c35a3..0000000000 --- a/apps/volumeViewer/VolumeViewer.h +++ /dev/null @@ -1,276 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "ospcommon/box.h" -#include "QOSPRayWindow.h" -#include "SliceWidget.h" -#include "LightEditor.h" -#include -#include -#include - -class TransferFunctionEditor; -class IsosurfaceEditor; -class ProbeWidget; -class OpenGLAnnotationRenderer; -class PreferencesDialog; - -//! OSPRay model and its volumes / geometries -struct ModelState { - - struct Volume { - Volume(OSPVolume handle, - const ospcommon::box3f &boundingBox, - const ospcommon::vec2f &voxelRange) - : handle(handle), - boundingBox(boundingBox), - voxelRange(voxelRange) - { - assert(!boundingBox.empty()); - } - - OSPVolume handle; - ospcommon::vec2f voxelRange; - ospcommon::box3f boundingBox; - }; - - struct Geometry { - Geometry(OSPGeometry handle=NULL) : handle(handle) {} - OSPGeometry handle; - }; - - ModelState(OSPModel model) : model(model) { } - - OSPModel model; //!< the OSPRay model - - std::vector volumes; //!< OSPRay volumes for the model - std::vector slices; //! OSPRay slice geometries for the model - std::vector isosurfaces; //! OSPRay isosurface geometries for the model -}; - -class VolumeViewer : public QMainWindow { - -Q_OBJECT - -public: - - //! Constructor. - VolumeViewer(const std::vector &objectFileFilenames, - std::string renderer_type, - bool ownModelPerObject, - bool showFrameRate, - bool fullScreen, - std::string writeFramesFilename); - - //! Get the volume bounding box. - ospcommon::box3f getBoundingBox(); - - //! Get the OSPRay output window. - QOSPRayWindow *getWindow(); - - //! Get the transfer function editor. - TransferFunctionEditor *getTransferFunctionEditor(); - - //! Select the model (time step) to be displayed. - void setModel(size_t index); - - //! A string description of this class. - std::string toString() const; - -public slots: - - //! Toggle auto-rotation of the view. - void autoRotate(bool set); - - //! Set auto-rotation rate - void setAutoRotationRate(float rate); - - //! Draw the model associated with the next time step. - void nextTimeStep(); - - //! Toggle animation over the time steps. - void playTimeSteps(bool animate); - - //! Add a slice to the volume from file. - void addSlice(std::string filename); - - //! Add geometry from file. - void addGeometry(std::string filename = std::string()); - - //! Save screenshot. - void screenshot(std::string filename = std::string()); - - //! Quit the volume viewer when pressing escape - virtual void keyPressEvent(QKeyEvent * event); - - //! Re-commit all OSPRay volumes. - void commitVolumes(); - - //! Force the OSPRay window to be redrawn. - void render(); - - //! Enable / disable rendering of annotations. - void setRenderAnnotationsEnabled(bool value); - - //! Set subsampling during interaction mode on renderer. - void setSubsamplingInteractionEnabled(bool value); - - //! Set gradient shading flag on all volumes. - void setGradientShadingEnabled(bool value); - bool getGradientShadingEnabled() { return gradientShadingEnabled; } - - //! Set gradient shading flag on all volumes. - void setPreIntegration(bool value); - bool getPreIntegration() { return preIntegration; } - - //! Set gradient shading flag on all volumes. - void setSingleShade(bool value); - - void setShadows(bool value); - bool getShadows() { return shadows; } - - void setAdaptiveScalar(double value); - - void setAdaptiveMaxSamplingRate(double value); - float getAdaptiveMaxSamplingRate() { return adaptiveMaxSamplingRate; } - - void setAdaptiveBacktrack(double value); - - //! Set gradient shading flag on all volumes. - void setAdaptiveSampling(bool value); - bool getAdaptiveSampling() { return adaptiveSampling; } - - //! Set sampling rate on all volumes. - void setSamplingRate(double value); - bool getSamplingRate() { return samplingRate; } - - //! Set volume clipping box on all volumes. - void setVolumeClippingBox(ospcommon::box3f value); - - //! Set slices on all volumes. - void setSlices(std::vector sliceParameters); - - //! Set isosurfaces on all volumes. - void setIsovalues(std::vector isovalues); - - void setPlane(bool st); - bool getPlane() { return usePlane; } - - void setAOSamples(int value); - int getAOSamples() { return aoSamples; } - - void setSPP(int value); - int getSPP() { return spp; } - - void setAOWeight(double value); - - LightEditor* getLightEditor() { return lightEditor; } - -protected: - - //! OSPRay object file filenames, one for each model / time step. - std::vector objectFileFilenames; - bool ownModelPerObject; // create a separate model for each object (not not only for each file) - - //! OSPRay models and their volumes / geometries. - std::vector modelStates; - - //! Active OSPRay model index (time step). - size_t modelIndex; - - //! Bounding box of the (first) volume. - ospcommon::box3f boundingBox; - - //! OSPRay renderer. - OSPRenderer renderer; - - //! OSPRay renderer initialization state: set to true after renderer is committed. - bool rendererInitialized; - - //! OSPRay transfer function. - OSPTransferFunction transferFunction; - - //! OSPRay ambient light. - OSPLight ambientLight; - - //! OSPRay directional light. - OSPLight directionalLight; - - //! The OSPRay output window. - QOSPRayWindow *osprayWindow; - - //! The OpenGL annotation renderer. - OpenGLAnnotationRenderer *annotationRenderer; - - //! The transfer function editor. - TransferFunctionEditor *transferFunctionEditor; - - //! The slice editor. - SliceEditor *sliceEditor; - - //! The isosurface editor. - IsosurfaceEditor *isosurfaceEditor; - - //! The probe widget. - ProbeWidget *probeWidget; - - //! Auto-rotate button. - QAction *autoRotateAction; - - //! Auto-rotation rate - float autoRotationRate; - - //! Timer for use when stepping through multiple models. - QTimer playTimeStepsTimer; - - //! Label for current OSPRay object file information. - QLabel currentFilenameInfoLabel; - - //! Print an error message. - void emitMessage(const std::string &kind, const std::string &message) const - { std::cerr << " " + toString() + " " + kind + ": " + message + "." << std::endl; } - - //! Error checking. - void exitOnCondition(bool condition, const std::string &message) const - { if (!condition) return; emitMessage("ERROR", message); exit(1); } - - //! Load an OSPRay model from a file. - void importObjectsFromFile(const std::string &filename); - - //! Create and configure the OSPRay state. - void initObjects(const std::string &renderer_type); - - //! Create and configure the user interface widgets and callbacks. - void initUserInterfaceWidgets(); - - int usePlane; - - OSPGeometry planeMesh; - - LightEditor* lightEditor; - float samplingRate; - float adaptiveMaxSamplingRate; - PreferencesDialog* preferencesDialog; - int spp; - int aoSamples; - int shadows; - int preIntegration; - int adaptiveSampling; - int gradientShadingEnabled; - -}; diff --git a/apps/volumeViewer/glUtil/util.cpp b/apps/volumeViewer/glUtil/util.cpp deleted file mode 100644 index b1dbe2d260..0000000000 --- a/apps/volumeViewer/glUtil/util.cpp +++ /dev/null @@ -1,230 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "util.h" -#include "ospcommon/vec.h" - -#ifdef _WIN32 -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif -# include -#endif - -#ifdef __APPLE__ -#include -#else -#include -#endif -#include - -namespace ospray { - typedef ospcommon::vec2i vec2i; - typedef ospcommon::vec3f vec3f; - - namespace opengl { - - OSPTexture2D getOSPDepthTextureFromOpenGLPerspective() - { - // compute fovy, aspect, zNear, zFar from OpenGL projection matrix - GLdouble glProjectionMatrix[16]; - glGetDoublev(GL_PROJECTION_MATRIX, glProjectionMatrix); - - const double m0 = glProjectionMatrix[0]; - const double m5 = glProjectionMatrix[5]; - const double m10 = glProjectionMatrix[10]; - const double m14 = glProjectionMatrix[14]; - const double k = (m10 - 1.0f) / (m10 + 1.0f); - - const double fovy = 2. * atanf(1.0f / m5) * 180./M_PI; - const double aspect = m5 / m0; - const double zNear = (m14 * (1.0f - k)) / (2.0f * k); - const double zFar = k * zNear; - - // get camera direction and up vectors from model view matrix - GLdouble glModelViewMatrix[16]; - glGetDoublev(GL_MODELVIEW_MATRIX, glModelViewMatrix); - - const ospray::vec3f cameraUp( glModelViewMatrix[1], glModelViewMatrix[5], glModelViewMatrix[9]); - const ospray::vec3f cameraDir(-glModelViewMatrix[2], -glModelViewMatrix[6], -glModelViewMatrix[10]); - - // get window dimensions from OpenGL viewport - GLint glViewport[4]; - glGetIntegerv(GL_VIEWPORT, glViewport); - - const size_t width = glViewport[2]; - const size_t height = glViewport[3]; - - // read OpenGL depth buffer - float *glDepthBuffer = new float[width * height]; - glReadPixels(0, 0, width, height, GL_DEPTH_COMPONENT, GL_FLOAT, (GLvoid *)glDepthBuffer); - - // get an OSPRay depth texture from the OpenGL depth buffer - OSPTexture2D depthTexture - = getOSPDepthTextureFromOpenGLPerspective(fovy, aspect, zNear, zFar, - (osp::vec3f&)cameraDir, (osp::vec3f&)cameraUp, - glDepthBuffer, width, height); - - // free allocated depth buffer - delete[] glDepthBuffer; - - // return OSPRay depth texture - return depthTexture; - } - - OSPTexture2D getOSPDepthTextureFromOpenGLPerspective(const double &fovy, - const double &aspect, - const double &zNear, - const double &zFar, - const osp::vec3f &_cameraDir, - const osp::vec3f &_cameraUp, - const float *glDepthBuffer, - const size_t &glDepthBufferWidth, - const size_t &glDepthBufferHeight) - { - ospray::vec3f cameraDir = (ospray::vec3f&)_cameraDir; - ospray::vec3f cameraUp = (ospray::vec3f&)_cameraUp; - // this should later be done in ISPC... - - float *ospDepth = new float[glDepthBufferWidth * glDepthBufferHeight]; - - // transform OpenGL depth to linear depth - for (size_t i=0; i -#include -#include -// qt -#include - -void printUsage(const char *exeName) -{ - std::cerr << "\n USAGE: " << exeName << " [filenames...] [options]\n" - << " \n" - << " Options:\n" - << " --benchmark run benchmark and report overall frame rate\n" - << " saving the final frame to \n" - << " --dt
use ray cast sample step size 'dt'\n" - << " --module load the module 'moduleName'\n" - << " --geometry load geometry from 'filename'\n" - << " --rotate automatically rotate view according to 'rate'\n" - << " --fps,--show-framerate show the frame rate in the window title bar\n" - << " --fullscreen enter fullscreen mode\n" - << " --own-model-per-object create a separate model for each object\n" - << " --slice load volume slice from 'filename'\n" - << " --transferfunction load transfer function from 'filename'\n" - << " --viewsize x force OSPRay view size to 'width'x'height'\n" - << " -vu set viewport up vector to ('x', 'y', 'z')\n" - << " -vp set camera position ('x', 'y', 'z')\n" - << " -vi set look at position ('x', 'y', 'z')\n" - << " -r,--renderer set renderer to use\n" - << " --writeframes emit frames to 'filename_xxxxx.ppm'\n" - << " -h,--help print this message\n" - << " \n"; -} - -int main(int argc, char *argv[]) -{ - // Initialize OSPRay. - ospInit(&argc, (const char **) argv); - auto device = ospGetCurrentDevice(); - ospDeviceSetErrorMsgFunc(device, [](const char *msg) { std::cout << msg; }); - - // Initialize Qt. - QApplication *app = new QApplication(argc, argv); - - // Print the command line usage. - if (argc < 2) { - printUsage(argv[0]); - return 1; - } - - // Default values for the optional command line arguments. - float dt = 0.0f; - std::vector plyFilenames; - float rotationRate = 0.0f; - std::vector sliceFilenames; - std::string transferFunctionFilename; - int benchmarkWarmUpFrames = 0; - int benchmarkFrames = 0; - std::string benchmarkFilename; - int viewSizeWidth = 0; - int viewSizeHeight = 0; - ospcommon::vec3f viewUp(0.f); - ospcommon::vec3f viewAt(0.f), viewFrom(0.f); - bool showFrameRate = false; - bool fullScreen = false; - bool ownModelPerObject = false; - std::string renderer = "scivis";//"dvr"; - std::string writeFramesFilename; - bool usePlane = true; - float ambientLightIntensity = 0.2f; - float directionalLightIntensity = 1.7f; - float directionalLightAzimuth = 80; - float directionalLightElevation = 65; - float samplingRate = .125f; - float maxSamplingRate = 2.f; - int spp = 1; - bool noshadows = false; - int aoSamples = 1; - bool preIntegration = true; - bool gradientShading = true; - bool adaptiveSampling = true; - - std::vector inFileName; - // Parse the optional command line arguments. - for (int i = 1; i < argc; ++i) { - - const std::string arg = argv[i]; - - if (arg == "--dt") { - if (i + 1 >= argc) throw std::runtime_error("missing
argument"); - dt = atof(argv[++i]); - std::cout << "got dt = " << dt << std::endl; - } else if (arg == "--geometry") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - plyFilenames.push_back(std::string(argv[++i])); - std::cout << "got PLY filename = " << plyFilenames.back() << std::endl; - } else if (arg == "--samplingRate") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - samplingRate = atof(argv[++i]); - } else if (arg == "--ao-samples" || arg == "-ao") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - aoSamples = atoi(argv[++i]); - } else if (arg == "--gradientShading" ) { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - gradientShading = atoi(argv[++i]); - } else if (arg == "--adaptiveSampling" ) { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - adaptiveSampling = atoi(argv[++i]); - } else if (arg == "--noshadows" || arg == "-ns") { - noshadows = true; - } else if (arg == "--nopreintegration") { - preIntegration = false; - } else if (arg == "--spp" || arg == "-spp") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - spp = atoi(argv[++i]); - } else if (arg == "--maxSamplingRate") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - maxSamplingRate = atof(argv[++i]); - } else if (arg == "--rotate") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - rotationRate = atof(argv[++i]); - std::cout << "got rotationRate = " << rotationRate << std::endl; - } else if (arg == "--noplane") { - usePlane = false; - } else if (arg == "--slice") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - sliceFilenames.push_back(std::string(argv[++i])); - std::cout << "got slice filename = " << sliceFilenames.back() << std::endl; - } else if (arg == "--show-framerate" || arg == "--fps") { - showFrameRate = true; - std::cout << "set show frame rate" << std::endl; - } else if (arg == "--fullscreen") { - fullScreen = true; - std::cout << "go full screen" << std::endl; - } else if (arg == "--own-model-per-object") { - ownModelPerObject = true; - } else if (arg == "--transferfunction") { - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - transferFunctionFilename = std::string(argv[++i]); - std::cout << "got transferFunctionFilename = " << transferFunctionFilename << std::endl; - } else if (arg == "--benchmark") { - if (i + 3 >= argc) throw std::runtime_error("missing arguments"); - benchmarkWarmUpFrames = atoi(argv[++i]); - benchmarkFrames = atoi(argv[++i]); - benchmarkFilename = argv[++i]; - std::cout << "got benchmarkWarmUpFrames = " - << benchmarkWarmUpFrames << ", benchmarkFrames = " - << benchmarkFrames << ", benchmarkFilename = " << benchmarkFilename << std::endl; - } else if (arg == "-r" || arg == "--renderer") { - renderer = argv[++i]; - } else if (arg == "--viewsize") { - if (i + 1 >= argc) throw std::runtime_error("missing x argument"); - std::string arg2(argv[++i]); - size_t pos = arg2.find("x"); - if (pos != std::string::npos) { - arg2.replace(pos, 1, " "); - std::stringstream ss(arg2); - ss >> viewSizeWidth >> viewSizeHeight; - std::cout << "got viewSizeWidth = " << viewSizeWidth << ", viewSizeHeight = " << viewSizeHeight << std::endl; - } else throw std::runtime_error("improperly formatted x argument"); - } else if (arg == "--ambientLight") { - if (i + 1 >= argc) throw std::runtime_error("missing light argument"); - ambientLightIntensity = atof(argv[++i]); - } else if (arg == "--directionalLight") { - if (i + 3 >= argc) throw std::runtime_error("missing light argument"); - directionalLightIntensity = atof(argv[++i]); - directionalLightAzimuth = atof(argv[++i]); - directionalLightElevation = atof(argv[++i]); - } else if (arg == "-vu") { - - if (i + 3 >= argc) throw std::runtime_error("missing arguments"); - - viewUp.x = atof(argv[++i]); - viewUp.y = atof(argv[++i]); - viewUp.z = atof(argv[++i]); - - std::cout << "got viewup (-vu) = " << viewUp.x << " " << viewUp.y << " " << viewUp.z << std::endl; - - } else if (arg == "-vp") { - - if (i + 3 >= argc) throw std::runtime_error("missing arguments"); - - viewFrom.x = atof(argv[++i]); - viewFrom.y = atof(argv[++i]); - viewFrom.z = atof(argv[++i]); - - std::cout << "got view-from (-vp) = " << viewFrom.x << " " << viewFrom.y << " " << viewFrom.z << std::endl; - - } else if (arg == "-vi") { - - if (i + 3 >= argc) throw std::runtime_error("missing arguments"); - - viewAt.x = atof(argv[++i]); - viewAt.y = atof(argv[++i]); - viewAt.z = atof(argv[++i]); - - std::cout << "got view-at (-vi) = " << viewAt.x << " " << viewAt.y << " " << viewAt.z << std::endl; - - } else if (arg == "--writeframes") { - - if (i + 1 >= argc || argv[i + 1][0] == '-') throw std::runtime_error("the '--writeframes' option requires a filename argument"); - writeFramesFilename = argv[++i]; - std::cout << "got writeFramesFilename = " << writeFramesFilename << std::endl; - - } else if (arg == "--module") { - - if (i + 1 >= argc) throw std::runtime_error("missing argument"); - std::string moduleName = argv[++i]; - std::cout << "loading module '" << moduleName << "'." << std::endl; - int error = ospLoadModule(moduleName.c_str()); - - if(error != 0) { - std::ostringstream ss; - ss << error; - throw std::runtime_error("could not load module " + moduleName + ", error " + ss.str()); - } - } else if (arg == "-h" || arg == "--help") { - printUsage(argv[0]); - return 0; - } else if (arg[0] == '-') { - std::cerr << "Unknown parameter " + arg << std::endl; - printUsage(argv[0]); - throw std::runtime_error("unknown parameter " + arg); - } else { - inFileName.push_back(arg); - } - - } - - // Create the OSPRay state and viewer window. - VolumeViewer *volumeViewer = new VolumeViewer(inFileName, - renderer, - ownModelPerObject, - showFrameRate, - fullScreen, - writeFramesFilename); - - volumeViewer->getLightEditor()->setAmbientLightIntensity(ambientLightIntensity); - volumeViewer->getLightEditor()->setDirectionalLightIntensity(directionalLightIntensity); - volumeViewer->getLightEditor()->setDirectionalLightAzimuth(directionalLightAzimuth); - volumeViewer->getLightEditor()->setDirectionalLightElevation(directionalLightElevation); - - volumeViewer->setSamplingRate(samplingRate); - volumeViewer->setAdaptiveMaxSamplingRate(maxSamplingRate); - volumeViewer->setAdaptiveSampling(adaptiveSampling); - - volumeViewer->setSPP(spp); - volumeViewer->setShadows(!noshadows); - volumeViewer->setAOSamples(aoSamples); - volumeViewer->setPreIntegration(preIntegration); - volumeViewer->setGradientShadingEnabled(gradientShading); - - // Display the first model. - volumeViewer->setModel(0); - - volumeViewer->setPlane(usePlane); - - // Load PLY geometries from file. - for(unsigned int i=0; iaddGeometry(plyFilenames[i]); - - // Set rotation rate to use in animation mode. - if(rotationRate != 0.f) { - volumeViewer->setAutoRotationRate(rotationRate); - volumeViewer->autoRotate(true); - } - - if (dt > 0.0f) - volumeViewer->setSamplingRate(dt); - - // Load slice(s) from file. - for(unsigned int i=0; iaddSlice(sliceFilenames[i]); - - // Load transfer function from file. - if(transferFunctionFilename.empty() != true) - volumeViewer->getTransferFunctionEditor()->load(transferFunctionFilename); - - // Set benchmarking parameters. - volumeViewer->getWindow()->setBenchmarkParameters(benchmarkWarmUpFrames, - benchmarkFrames, benchmarkFilename); - - if (viewAt != viewFrom) { - volumeViewer->getWindow()->getViewport()->at = viewAt; - volumeViewer->getWindow()->getViewport()->from = viewFrom; - } - - // Set the window size if specified. - if (viewSizeWidth != 0 && viewSizeHeight != 0) volumeViewer->getWindow()->setFixedSize(viewSizeWidth, viewSizeHeight); - - - // Set the view up vector if specified. - if(viewUp != ospcommon::vec3f(0.f)) { - volumeViewer->getWindow()->getViewport()->setUp(viewUp); - volumeViewer->getWindow()->resetAccumulationBuffer(); - } - - // Enter the Qt event loop. - app->exec(); - - // Cleanup - delete volumeViewer; - - return 0; -} diff --git a/cmake/configure_embree.cmake b/cmake/configure_embree.cmake index e8e8b1910b..0f39d30da7 100644 --- a/cmake/configure_embree.cmake +++ b/cmake/configure_embree.cmake @@ -24,7 +24,7 @@ IF(NOT DEFINED EMBREE_INCLUDE_DIRS) "We did not find Embree installed on your system. OSPRay requires an" " Embree installation >= v${EMBREE_VERSION_REQUIRED}, please download" " and extract Embree or compile Embree from source, then set the" - " 'embree_DIR' variable to where it is installed.") + " 'embree_DIR' variable to /lib/cmake/embree-.") ENDIF() # Check for required Embree features ####################################### @@ -37,53 +37,61 @@ OSPRAY_CHECK_EMBREE_FEATURE(GEOMETRY_USER "user geometries") OSPRAY_CHECK_EMBREE_FEATURE(RAY_PACKETS "ray packets") OSPRAY_CHECK_EMBREE_FEATURE(BACKFACE_CULLING "backface culling" OFF) -# also remove Embree's TBB libs if OSPRay uses TBB to avoid problems with -# cyclic rpath -IF(NOT DEFINED EMBREE_LIBRARIES OR OSPRAY_TASKING_TBB) - SET(EMBREE_LIBRARIES ${EMBREE_LIBRARY}) -ELSE() - # check if we need to add TBB to EMBREE_LIBRARIES - IF((EMBREE_TASKING_TBB OR (${EMBREE_TASKING_SYSTEM} STREQUAL "TBB")) - AND NOT EMBREE_USE_PACKAGED_TBB) - OSPRAY_WARN_ONCE(EMBREE_FORCE_TBB - "You *MUST* have TBB installed based on the Embree we found!") - FIND_PACKAGE(TBB REQUIRED) - SET(EMBREE_LIBRARIES ${EMBREE_LIBRARIES} ${TBB_LIBRARIES}) - ENDIF() -ENDIF() +# remove Embree's TBB libs (not needed, cyclic rpath) +SET(EMBREE_LIBRARIES ${EMBREE_LIBRARY}) IF (EMBREE_MAX_ISA STREQUAL "NONE") - SET(EMBREE_ISA_SUPPORTS_SSE4 ${EMBREE_ISA_SSE42}) - SET(EMBREE_ISA_SUPPORTS_AVX ${EMBREE_ISA_AVX}) - SET(EMBREE_ISA_SUPPORTS_AVX2 ${EMBREE_ISA_AVX2}) - SET(EMBREE_ISA_SUPPORTS_AVX512 ${EMBREE_ISA_AVX512KNL}) + SET(EMBREE_ISA_SUPPORTS_SSE2 ${EMBREE_ISA_SSE2}) + SET(EMBREE_ISA_SUPPORTS_SSE4 ${EMBREE_ISA_SSE42}) + SET(EMBREE_ISA_SUPPORTS_AVX ${EMBREE_ISA_AVX}) + SET(EMBREE_ISA_SUPPORTS_AVX2 ${EMBREE_ISA_AVX2}) + SET(EMBREE_ISA_SUPPORTS_AVX512KNL ${EMBREE_ISA_AVX512KNL}) + SET(EMBREE_ISA_SUPPORTS_AVX512SKX ${EMBREE_ISA_AVX512SKX}) ELSE() - SET(EMBREE_ISA_SUPPORTS_SSE4 FALSE) - SET(EMBREE_ISA_SUPPORTS_AVX FALSE) - SET(EMBREE_ISA_SUPPORTS_AVX2 FALSE) - SET(EMBREE_ISA_SUPPORTS_AVX512 FALSE) - + SET(EMBREE_ISA_SUPPORTS_SSE2 FALSE) + SET(EMBREE_ISA_SUPPORTS_SSE4 FALSE) + SET(EMBREE_ISA_SUPPORTS_AVX FALSE) + SET(EMBREE_ISA_SUPPORTS_AVX2 FALSE) + SET(EMBREE_ISA_SUPPORTS_AVX512KNL FALSE) + SET(EMBREE_ISA_SUPPORTS_AVX512SKX FALSE) - IF (EMBREE_MAX_ISA MATCHES "SSE4\\.[12]$") + IF (EMBREE_MAX_ISA STREQUAL "SSE2") + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) + ELSEIF (EMBREE_MAX_ISA MATCHES "SSE4\\.[12]$") + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) ELSEIF (EMBREE_MAX_ISA STREQUAL "AVX") + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) SET(EMBREE_ISA_SUPPORTS_AVX TRUE) ELSEIF (EMBREE_MAX_ISA STREQUAL "AVX2") + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) SET(EMBREE_ISA_SUPPORTS_AVX TRUE) SET(EMBREE_ISA_SUPPORTS_AVX2 TRUE) ELSEIF (EMBREE_MAX_ISA STREQUAL "AVX512KNL") - SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) - SET(EMBREE_ISA_SUPPORTS_AVX TRUE) - SET(EMBREE_ISA_SUPPORTS_AVX2 TRUE) - SET(EMBREE_ISA_SUPPORTS_AVX512 TRUE) + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) + SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX2 TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX512KNL TRUE) + ELSEIF (EMBREE_MAX_ISA STREQUAL "AVX512SKX") + SET(EMBREE_ISA_SUPPORTS_SSE2 TRUE) + SET(EMBREE_ISA_SUPPORTS_SSE4 TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX2 TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX512KNL TRUE) + SET(EMBREE_ISA_SUPPORTS_AVX512SKX TRUE) ENDIF() ENDIF() -IF(NOT EMBREE_ISA_SUPPORTS_SSE4) +IF (NOT (EMBREE_ISA_SUPPORTS_SSE4 + OR EMBREE_ISA_SUPPORTS_AVX + OR EMBREE_ISA_SUPPORTS_AVX2 + OR EMBREE_ISA_SUPPORTS_AVX512KNL + OR EMBREE_ISA_SUPPORTS_AVX512SKX)) MESSAGE(FATAL_ERROR - "Your Embree build needs to support at least SSE4.1!") + "Your Embree build needs to support at least one ISA >= SSE4.1!") ENDIF() # Configure OSPRay ISA last after we've detected what we got w/ Embree diff --git a/cmake/ospray_cmake_config.cmake b/cmake/ospray_cmake_config.cmake index b08daf5184..6ef2e40e10 100644 --- a/cmake/ospray_cmake_config.cmake +++ b/cmake/ospray_cmake_config.cmake @@ -37,7 +37,6 @@ SET(ospray_MODULE_FILES ) FOREACH(ospray_MODULE_FILENAME ${ospray_MODULE_FILES}) - LIST(APPEND ospray_MODULE_INSTALL_FILES ${ospray_MODULE_CONFIG_OUTPUT_DIR}/${ospray_MODULE_FILENAME} ) @@ -47,7 +46,6 @@ FOREACH(ospray_MODULE_FILENAME ${ospray_MODULE_FILES}) ${ospray_MODULE_CONFIG_OUTPUT_DIR}/${ospray_MODULE_FILENAME} @ONLY ) - ENDFOREACH(ospray_MODULE_FILENAME ${ospray_MODULE_FILES}) LIST(APPEND ospray_MODULE_INSTALL_FILES diff --git a/cmake/ospray_cmake_config/osprayConfig.cmake.in b/cmake/ospray_cmake_config/osprayConfig.cmake.in index bcb23a736e..383c8a96a6 100644 --- a/cmake/ospray_cmake_config/osprayConfig.cmake.in +++ b/cmake/ospray_cmake_config/osprayConfig.cmake.in @@ -108,10 +108,10 @@ set(OSPRAY_COMPILER_GCC @OSPRAY_COMPILER_GCC@) set(OSPRAY_COMPILER_CLANG @OSPRAY_COMPILER_CLANG@) set(OSPRAY_COMPILER_MSVC @OSPRAY_COMPILER_MSVC@) -set(OSPRAY_USE_EXTERNAL_EMBREE @OSPRAY_USE_EXTERNAL_EMBREE@) set(OSPRAY_TASKING_TBB @OSPRAY_TASKING_TBB@) set(OSPRAY_ISPC_TARGET_LIST @OSPRAY_ISPC_TARGET_LIST@) +set(OSPRAY_MPI_ENABLED @OSPRAY_MODULE_MPI@) ############################################################################### # ospray dependencies @@ -151,16 +151,8 @@ ELSE() SET(LIBRARY_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) ENDIF() -if(OSPRAY_USE_EXTERNAL_EMBREE) - # Find existing Embree on the machine - find_package(embree @EMBREE_VERSION_REQUIRED@ REQUIRED) -else() - if (APPLE) - set(EMBREE_LIBRARY ${LIBRARY_PATH_PREFIX}embree.2${LIBRARY_SUFFIX}) - else() - set(EMBREE_LIBRARY ${LIBRARY_PATH_PREFIX}embree${LIBRARY_SUFFIX}.2) - endif() -endif() +# Find existing Embree on the machine +find_package(embree @EMBREE_VERSION_REQUIRED@ REQUIRED) list(APPEND OSPRAY_INCLUDE_DIRS ${EMBREE_INCLUDE_DIRS}) # Restore state @@ -172,45 +164,25 @@ set(CURRENT_ROOT_INSTALL_DIR ${OSPRAY_CURRENT_ROOT_INSTALL_DIR}) ############################################################################### # Finish ospray -# Import exported ospray targets -# NOTE(jda) - Order matters below: targets which depend on other targets need to -# be after the INCLUDE() of the targets they depend upon. -# NOTE(jda) - Targets have been disabled now that we use Embree's root CMake -# list to configure and build Embree. When Embree exports targets, -# using them here should be re-enabled. -#IF(NOT TARGET ospray AND NOT OSPRAY_BINARY_DIR) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libsysTargets.cmake) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/liblexersTargets.cmake) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libsimdTargets.cmake) -# IF (@OSPRAY_EMBREE_ENABLE_SSE@ OR @OSPRAY_EMBREE_ENABLE_AVX@ OR @OSPRAY_EMBREE_ENABLE_AVX2@) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libospray_embree_sse42Targets.cmake) -# ENDIF() -# IF (@OSPRAY_EMBREE_ENABLE_AVX@ OR @OSPRAY_EMBREE_ENABLE_AVX2@) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libospray_embree_avxTargets.cmake) -# ENDIF() -# IF (@OSPRAY_EMBREE_ENABLE_AVX2@) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libospray_embree_avx2Targets.cmake) -# ENDIF() -# IF (@OSPRAY_EMBREE_ENABLE_AVX512@) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libospray_embree_avx512Targets.cmake) -# ENDIF() -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libospray_embreeTargets.cmake) -# INCLUDE(${CURRENT_CONFIG_INSTALL_DIR}/libosprayTargets.cmake) -#endif(NOT TARGET ospray AND NOT OSPRAY_BINARY_DIR) - # Remove duplicate entries from OSPRAY_INCLUDE_DIRS list(REMOVE_DUPLICATES OSPRAY_INCLUDE_DIRS) # Set expected OSPRAY_LIBRARIES variable for FindPackage(). set(OSPRAY_LIBRARIES ${OSPRAY_DEPENDENCIES} - #ospray# NOTE(jda) - target disabled (see above) - #ospray_embree# NOTE(jda) - target disabled (see above) ${EMBREE_LIBRARY} ${LIBRARY_PATH_PREFIX}ospray${LIBRARY_SUFFIX} ${LIBRARY_PATH_PREFIX}ospray_common${LIBRARY_SUFFIX} ) +if(OSPRAY_MPI_ENABLED) + set(OSPRAY_LIBRARIES + ${OSPRAY_LIBRARIES} + ${LIBRARY_PATH_PREFIX}ospray_mpi_common${LIBRARY_SUFFIX} + ${LIBRARY_PATH_PREFIX}ospray_mpi_maml${LIBRARY_SUFFIX} + ) +endif() + # Reset CMake module path to its state when this script was called. set(CMAKE_MODULE_PATH ${OSPRAY_CALLERS_CMAKE_MODULE_PATH}) diff --git a/cmake/ospray_options.cmake b/cmake/ospray_options.cmake index af8be4f42c..b0f346c832 100644 --- a/cmake/ospray_options.cmake +++ b/cmake/ospray_options.cmake @@ -19,8 +19,8 @@ ############################################################## SET(OSPRAY_VERSION_MAJOR 1) -SET(OSPRAY_VERSION_MINOR 2) -SET(OSPRAY_VERSION_PATCH 1) +SET(OSPRAY_VERSION_MINOR 3) +SET(OSPRAY_VERSION_PATCH 0) SET(OSPRAY_VERSION_GITHASH 0) IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) FIND_PACKAGE(Git) @@ -34,6 +34,12 @@ IF(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) ENDIF() ENDIF() +OPTION(OSPRAY_ENABLE_TESTING OFF) + +IF (OSPRAY_ENABLE_TESTING) + ENABLE_TESTING() +ENDIF() + SET(OSPRAY_VERSION ${OSPRAY_VERSION_MAJOR}.${OSPRAY_VERSION_MINOR}.${OSPRAY_VERSION_PATCH} ) @@ -68,14 +74,12 @@ ELSE()# Release SET(OSPRAY_RELEASE_BUILD ON ) ENDIF() -SET(OSPRAY_BINARY_DIR ${PROJECT_BINARY_DIR}) -SET(LIBRARY_OUTPUT_PATH ${OSPRAY_BINARY_DIR}) -SET(EXECUTABLE_OUTPUT_PATH ${OSPRAY_BINARY_DIR}) +SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +SET(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) +SET(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) -#include bindir - that's where ispc puts generated header files -INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR}) -SET(OSPRAY_BINARY_DIR ${CMAKE_BINARY_DIR}) -SET(OSPRAY_DIR ${PROJECT_SOURCE_DIR}) +#include bindir - that's where OSPConfig.h and ospray/version.h will be put +INCLUDE_DIRECTORIES(${PROJECT_BINARY_DIR}) IF (WIN32) # avoid problematic min/max defines of windows.h diff --git a/cmake/package.cmake b/cmake/package.cmake index 7b2c7fb8d7..11a5283b69 100644 --- a/cmake/package.cmake +++ b/cmake/package.cmake @@ -93,7 +93,7 @@ SET(CPACK_COMPONENT_MPI_DISPLAY_NAME "MPI Module") SET(CPACK_COMPONENT_MPI_DESCRIPTION "OSPRay module for MPI-based distributed rendering.") SET(CPACK_COMPONENT_REDIST_DISPLAY_NAME "Redistributables") -SET(CPACK_COMPONENT_REDIST_DESCRIPTION "Dependencies of OSPRay (such as Embree, TBB, Qt, Glut) that may or may not be already installed on your system.") +SET(CPACK_COMPONENT_REDIST_DESCRIPTION "Dependencies of OSPRay (such as Embree, TBB, glfw) that may or may not be already installed on your system.") # dependencies between components SET(CPACK_COMPONENT_DEVEL_DEPENDS lib) diff --git a/components/configure.cmake b/components/configure.cmake index cc815640cb..1b098c8210 100644 --- a/components/configure.cmake +++ b/components/configure.cmake @@ -30,14 +30,14 @@ INCLUDE_DIRECTORIES(${OSPRAY_COMPONENTS_ROOT}) # the macro any part of ospray can use to request ospray to # include/build a specific component MACRO(OSPRAY_BUILD_COMPONENT comp) + SET(CURRENT_COMPONENT_DIR ${COMPONENTS_DIR}/${comp}) + IF(NOT EXISTS ${CURRENT_COMPONENT_DIR}) + MESSAGE(FATAL_ERROR "Could not find component '${comp}'!") + ENDIF() + IF (";${OSPRAY_LIST_OF_ALREADY_BUILT_COMPONENTS};" MATCHES ";${comp};") # component already built; nothing to do! ELSE() - SET(CURRENT_COMPONENT_DIR ${COMPONENTS_DIR}/${comp}) - IF(NOT EXISTS ${CURRENT_COMPONENT_DIR}) - MESSAGE(FATAL_ERROR "Could not find component '${comp}'!") - ENDIF() - SET(INCLUDED_AS_AN_OSPRAY_COMPONENT ON) SET(OSPRAY_LIST_OF_ALREADY_BUILT_COMPONENTS ${OSPRAY_LIST_OF_ALREADY_BUILT_COMPONENTS} ${comp} @@ -54,9 +54,10 @@ MACRO(OSPRAY_BUILD_COMPONENT comp) SET(OSPRAY_DEFAULT_COMPONENT ${DEFAULT_COMPONENT}) + ENDIF() + IF(EXISTS ${CURRENT_COMPONENT_DIR}/include.cmake) INCLUDE(${CURRENT_COMPONENT_DIR}/include.cmake) ENDIF() - ENDIF() ENDMACRO() diff --git a/components/mpiCommon/BufferedMPIComm.cpp b/components/mpiCommon/BufferedMPIComm.cpp deleted file mode 100644 index 3cce4a498f..0000000000 --- a/components/mpiCommon/BufferedMPIComm.cpp +++ /dev/null @@ -1,126 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "BufferedMPIComm.h" - -namespace ospray { - namespace mpi { - // The max size for an MPI Bcast is actually 2GB, but we cut it at 1.8GB - const size_t BufferedMPIComm::MAX_BCAST = 1800000000LL; - std::shared_ptr BufferedMPIComm::global = nullptr; - std::mutex BufferedMPIComm::globalCommAlloc; - BufferedMPIComm::BufferedMPIComm(size_t bufSize) : sendBuffer(bufSize), recvBuffer(bufSize){} - BufferedMPIComm::~BufferedMPIComm() { - flush(); - } - void BufferedMPIComm::send(const Address& addr, work::Work* work) { - { - std::lock_guard lock(sendMutex); - // The packed buffer format is: - // size_t: size of message - // int: number of work units being sent - // [(size_t, X)...]: message tag headers followed by their data - if (sendBuffer.getIndex() == 0) { - sendSizeIndex = sendBuffer.getIndex(); - sendBuffer << size_t(0) << int(1); - sendWorkIndex = sendBuffer.getIndex(); - } - sendBuffer << work->getTag() << *work; - sendNumMessages++; - } - - // TODO: This is assuming we're always sending to the same place - if (sendAddress.group != NULL && sendAddress != addr) { - throw std::runtime_error("Can't send to different addresses currently!"); - } - // TODO WILL: Instead of making data/setregion commands flushing based on their - // size we should flush here if the sendbuffer has reached a certain size. - sendAddress = addr; - if (sendBuffer.getIndex() >= MAX_BCAST) { - flush(); - } - } - void BufferedMPIComm::recv(const Address& addr, std::vector& work) { - //TODO: assert current mode is syncronized - int recvNumMessages = 0; - size_t bufSize = 0; - if (addr.rank != RECV_ALL) { - std::runtime_error("mpi::recv on individual ranks not yet implemented"); - } - MPI_Bcast(recvBuffer.getData(), 2048, MPI_BYTE, 0, addr.group->comm); - recvBuffer >> bufSize >> recvNumMessages; - - if (bufSize > 2048) { - recvBuffer.reserve(bufSize); - // If we have more than 2KB receive the data in batches as big as we can send with bcast - for (size_t i = 2048; i < bufSize;) { - const int to_recv = std::min(bufSize - i, MAX_BCAST); - MPI_CALL(Bcast(recvBuffer.getPtr(i), to_recv, MPI_BYTE, 0, addr.group->comm)); - i += to_recv; - } - } - recvBuffer.setIndex(sizeof(int) + sizeof(size_t)); - work::decode_buffer(recvBuffer, work, recvNumMessages); - recvBuffer.clear(); - } - void BufferedMPIComm::flush() { - send(sendAddress, sendBuffer); - } - void BufferedMPIComm::barrier(const Group& group) { - flush(); - MPI_Barrier(group.comm); - } - void BufferedMPIComm::send(const Address& addr, work::SerialBuffer& buf) { - if (sendNumMessages == 0) { - return; - } - std::lock_guard lock(sendMutex); - const size_t sz = buf.getIndex(); - // set size in msg header - buf.setIndex(sendSizeIndex); - buf << sz << sendNumMessages; - buf.setIndex(sz); - - // Now send the buffer - if (addr.rank != SEND_ALL) { - std::runtime_error("mpi::send to individuals not yet implemented"); - } - MPI_CALL(Bcast(buf.getData(), 2048, MPI_BYTE, MPI_ROOT, addr.group->comm)); - - // If we have more than 2KB send the data in batches as big as we can send with bcast - for (size_t i = 2048; i < sz;) { - const int to_send = std::min(sz - i, MAX_BCAST); - MPI_CALL(Bcast(buf.getPtr(i), to_send, MPI_BYTE, MPI_ROOT, addr.group->comm)); - i += to_send; - } - buf.clear(); - sendNumMessages = 0; - } - std::shared_ptr BufferedMPIComm::get() { - // Create the global buffered comm if we haven't already or if it's been - // destroyed and we need it again. - if (!global || global.use_count() == 0) { - std::lock_guard lock(globalCommAlloc); - // Did someone else already allocate it by the time we got the mutex? - if (!global || global.use_count() == 0) { - global = std::make_shared(); - } - } - return global; - } - } -} - diff --git a/components/mpiCommon/BufferedMPIComm.h b/components/mpiCommon/BufferedMPIComm.h deleted file mode 100644 index 5710f6bb39..0000000000 --- a/components/mpiCommon/BufferedMPIComm.h +++ /dev/null @@ -1,70 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include -#include "common/OSPCommon.h" -#include "mpiCommon/SerialBuffer.h" - -namespace ospray { - namespace mpi { - // Mangement class for the MPI send/recv buffers. Objects interesting - // in sending/receiving messages through the MPI layer must go through this - // object to easily buffer operations. - class OSPRAY_MPI_INTERFACE BufferedMPIComm { - const static size_t MAX_BCAST; - // TODO: Sending to multiple addresses - work::SerialBuffer sendBuffer; - Address sendAddress; - // TODO WILL: I think this sendsizeindex is redundant, it will always be 0 - size_t sendSizeIndex = 0; - // TODO WILL: Won't the send work index always be 12? - size_t sendWorkIndex = 0; - int sendNumMessages = 0; - work::SerialBuffer recvBuffer; - std::mutex sendMutex; - - // TODO: Do we really want to go through a singleton for this? - // I guess it makes it easiest to provide global batching of all messages. - static std::shared_ptr global; - static std::mutex globalCommAlloc; - - public: - BufferedMPIComm(size_t bufSize = 1024 * 2 * 16); - ~BufferedMPIComm(); - // Send a work unit message to some address. - // TODO: Sending to multiple addresses - void send(const Address& addr, work::Work* work); - // Recieve a work unit message from some address, filling the work - // vector with the received work units. - void recv(const Address& addr, std::vector& work); - // Flush the current send buffer. - void flush(); - // Perform a barrier on the passed MPI Group. - void barrier(const Group& group); - // The management class works through a shared ptr global so anyone - // using it can clone the ptr and keep it alive as long as needed. - static std::shared_ptr get(); - - private: - // Actually send the data in the buffer to the address specified. - void send(const Address& addr, work::SerialBuffer& buf); - }; - } -} - diff --git a/components/mpiCommon/CMakeLists.txt b/components/mpiCommon/CMakeLists.txt index d76b30c05e..7628b12a31 100644 --- a/components/mpiCommon/CMakeLists.txt +++ b/components/mpiCommon/CMakeLists.txt @@ -17,23 +17,25 @@ OSPRAY_CONFIGURE_MPI() OSPRAY_CREATE_LIBRARY(ospray_mpi_common - buffers.h - command.cpp - BufferedMPIComm.cpp MPICommon.cpp - SerialBuffer.cpp - - async/Messaging.cpp - async/SimpleSendRecvMessaging.cpp - async/MultiIsendIrecvMessaging.cpp - async/BatchedIsendIrecvMessaging.cpp - async/CommLayer.cpp + MPIBcastFabric.cpp LINK - ${MPI_LIBRARIES} + ${MPI_C_LIBRARIES} ospray_common + ospray COMPONENT mpi ) + +SET(MPICOMMON_SDK_INSTALL_LOC + ../mpiCommon #NOTE: this is "next" to the SDK/ directory +) + +OSPRAY_INSTALL_SDK_HEADERS( + MPICommon.h + MPIBcastFabric.h + DESTINATION ${MPICOMMON_SDK_INSTALL_LOC} +) diff --git a/components/mpiCommon/MPIBcastFabric.cpp b/components/mpiCommon/MPIBcastFabric.cpp new file mode 100644 index 0000000000..fc52ec2b53 --- /dev/null +++ b/components/mpiCommon/MPIBcastFabric.cpp @@ -0,0 +1,96 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include +#include + +#include "MPIBcastFabric.h" + +namespace mpicommon { + + MPIBcastFabric::MPIBcastFabric(const Group &group, int sendRank, int recvRank) + : group(group), sendRank(sendRank), recvRank(recvRank) + { + if (!group.valid()) + throw std::runtime_error("#osp:mpi: trying to set up a MPI fabric " + "with a invalid MPI communicator"); +#ifndef NDEBUG + int isInter = 0; + MPI_CALL(Comm_test_inter(group.comm, &isInter)); + if (isInter) { + assert(recvRank == 0 && sendRank == MPI_ROOT); + } +#endif + } + + /*! receive some block of data - whatever the sender has sent - + and give us size and pointer to this data */ + size_t MPIBcastFabric::read(void *&mem) + { + uint32_t sz32 = 0; + // Get the size of the bcast being sent to us. Only this part must be non-blocking, + // after getting the size we know everyone will enter the blocking barrier and the + // blocking bcast where the buffer is sent out. + MPI_Request request; + MPI_CALL(Ibcast(&sz32, 1, MPI_INT, recvRank, group.comm, &request)); + + // Now do non-blocking test to see when this bcast is satisfied to avoid + // locking out the send/recv threads + int size_bcast_done = 0; + while (!size_bcast_done) { + MPI_CALL(Test(&request, &size_bcast_done, MPI_STATUS_IGNORE)); + if (!size_bcast_done) { + std::this_thread::sleep_for(std::chrono::nanoseconds(250)); + } + } + + MPI_CALL(Barrier(group.comm)); + // TODO: Maybe at some point we should dump the buffer if it gets really large + buffer.resize(sz32); + MPI_CALL(Bcast(buffer.data(), sz32, MPI_BYTE, recvRank, group.comm)); + mem = buffer.data(); + return sz32; + } + + /*! send exact number of bytes - the fabric can do that through + multiple smaller messages, but all bytes have to be + delivered */ + void MPIBcastFabric::send(void *mem, size_t size) + { + assert(size < (1LL<<30)); + uint32_t sz32 = size; + + // Send the size of the bcast we're sending. Only this part must be non-blocking, + // after getting the size we know everyone will enter the blocking barrier and the + // blocking bcast where the buffer is sent out. + MPI_Request request; + MPI_CALL(Ibcast(&sz32, 1, MPI_INT, sendRank, group.comm, &request)); + + // Now do non-blocking test to see when this bcast is satisfied to avoid + // locking out the send/recv threads + int size_bcast_done = 0; + while (!size_bcast_done) { + MPI_CALL(Test(&request, &size_bcast_done, MPI_STATUS_IGNORE)); + if (!size_bcast_done) { + std::this_thread::sleep_for(std::chrono::nanoseconds(250)); + } + } + + MPI_CALL(Barrier(group.comm)); + MPI_CALL(Bcast(mem, sz32, MPI_BYTE, sendRank, group.comm)); + } + +} // ::mpicommon diff --git a/apps/volumeViewer/IsosurfaceEditor.h b/components/mpiCommon/MPIBcastFabric.h similarity index 55% rename from apps/volumeViewer/IsosurfaceEditor.h rename to components/mpiCommon/MPIBcastFabric.h index 2ad8ab1170..d9dbdcae1b 100644 --- a/apps/volumeViewer/IsosurfaceEditor.h +++ b/components/mpiCommon/MPIBcastFabric.h @@ -16,41 +16,35 @@ #pragma once -#include -#include "ospcommon/vec.h" -#include -#include +#include "ospcommon/networking/DataStreaming.h" +#include "ospcommon/networking/Fabric.h" -class IsovalueWidget; +#include "MPICommon.h" -class IsosurfaceEditor : public QWidget -{ -Q_OBJECT +namespace mpicommon { -public: + /*! a specific fabric based on MPI. Note that in the case of an + * MPIBcastFabric using an intercommunicator the send rank must + * be MPI_ROOT and the recv rank must be 0. + */ + struct OSPRAY_MPI_INTERFACE MPIBcastFabric : public networking::Fabric + { + MPIBcastFabric(const Group &group, int sendRank, int recvRank); - IsosurfaceEditor(); + virtual ~MPIBcastFabric() = default; -signals: + /*! send exact number of bytes - the fabric can do that through + multiple smaller messages, but all bytes have to be + delivered */ + virtual void send(void *mem, size_t size) override; - void isovaluesChanged(std::vector values); + /*! receive some block of data - whatever the sender has sent - + and give us size and pointer to this data */ + virtual size_t read(void *&mem) override; -public slots: + std::vector buffer; + Group group; + int sendRank, recvRank; + }; - void setDataValueRange(ospcommon::vec2f dataValueRange); - - void apply(); - -protected slots: - - void addIsovalue(); - -protected: - - ospcommon::vec2f dataValueRange; - - QVBoxLayout layout; - - std::vector isovalueWidgets; - -}; +} // ::mpicommon diff --git a/components/mpiCommon/MPICommon.cpp b/components/mpiCommon/MPICommon.cpp index d93f6923e0..ed0a89ed29 100644 --- a/components/mpiCommon/MPICommon.cpp +++ b/components/mpiCommon/MPICommon.cpp @@ -15,101 +15,161 @@ // ======================================================================== // #include "MPICommon.h" -#include "async/CommLayer.h" -#include "BufferedMPIComm.h" -namespace ospray { - namespace mpi { +namespace mpicommon { - OSPRAY_MPI_INTERFACE Group world; - OSPRAY_MPI_INTERFACE Group app; - OSPRAY_MPI_INTERFACE Group worker; + /*! global variable that turns on logging of MPI communication + (for debugging) _may_ eventually turn this into a real logLevel, + but for now tihs is cleaner here thatn in the MPI device + */ + OSPRAY_MPI_INTERFACE bool mpiIsThreaded = 0; - /*! constructor. sets the 'comm', 'rank', and 'size' fields */ - Group::Group(MPI_Comm initComm) - { - setTo(initComm); - } + OSPRAY_MPI_INTERFACE Group world; + OSPRAY_MPI_INTERFACE Group app; + OSPRAY_MPI_INTERFACE Group worker; - /*! set to given intercomm, and properly set size, root, etc */ - void Group::setTo(MPI_Comm comm) - { - this->comm = comm; - if (comm == MPI_COMM_NULL) { - rank = size = -1; - } else { - int isInter; - MPI_CALL(Comm_test_inter(comm,&isInter)); - if (isInter) - makeInterComm(comm); - else - makeIntraComm(comm); - } - } + // Group definitions //////////////////////////////////////////////////////// - /*! do an MPI_Comm_dup, and return duplicated communicator */ - Group Group::dup() const - { - MPI_Comm duped; - MPI_CALL(Comm_dup(comm,&duped)); - return Group(duped); - } - - void init(int *ac, const char **av) - { - int initialized = false; - MPI_CALL(Initialized(&initialized)); - - if (!initialized) { - // MPI_Init(ac,(char ***)&av); - int required = MPI_THREAD_MULTIPLE; - int provided = 0; - MPI_CALL(Init_thread(ac,(char ***)&av,required,&provided)); - if (provided != required) { - throw std::runtime_error("MPI implementation does not offer " - "multi-threading capabilities"); - } - } + /*! constructor. sets the 'comm', 'rank', and 'size' fields */ + Group::Group(MPI_Comm initComm) + { + setTo(initComm); + } + + /*! constructor. sets the 'comm', 'rank', and 'size' fields */ + Group::Group(const Group &other) + : containsMe(other.containsMe), comm(other.comm), + rank(other.rank), size(other.size) + { + } + + void Group::makeIntraComm() + { + MPI_CALL(Comm_rank(comm,&rank)); + MPI_CALL(Comm_size(comm,&size)); + containsMe = true; + } + + void Group::makeIntraComm(MPI_Comm comm) + { + this->comm = comm; makeIntraComm(); + } + + void Group::makeInterComm(MPI_Comm comm) + { + this->comm = comm; makeInterComm(); + } + + void Group::makeInterComm() + { + containsMe = false; + rank = MPI_ROOT; + MPI_CALL(Comm_remote_size(comm, &size)); + } + + void Group::barrier() const + { + MPI_CALL(Barrier(comm)); + } + + /*! set to given intercomm, and properly set size, root, etc */ + void Group::setTo(MPI_Comm comm) + { + this->comm = comm; + if (comm == MPI_COMM_NULL) { + rank = size = -1; + } else { + int isInter; + MPI_CALL(Comm_test_inter(comm,&isInter)); + if (isInter) + makeInterComm(comm); else - { - printf("running ospray in pre-initialized mpi mode\n"); - int provided; - MPI_Query_thread(&provided); - int requested = MPI_THREAD_MULTIPLE; - if (provided != requested) - throw std::runtime_error("ospray requires mpi to be initialized with " - "MPI_THREAD_MULTIPLE if initialized before calling ospray"); - } - world.comm = MPI_COMM_WORLD; - MPI_CALL(Comm_rank(MPI_COMM_WORLD,&world.rank)); - MPI_CALL(Comm_size(MPI_COMM_WORLD,&world.size)); - - mpi::async::CommLayer::WORLD = new mpi::async::CommLayer; - mpi::async::Group *worldGroup = - mpi::async::createGroup(MPI_COMM_WORLD, - mpi::async::CommLayer::WORLD, - 290374); - mpi::async::CommLayer::WORLD->group = worldGroup; + makeIntraComm(comm); } + } - void send(const Address& addr, work::Work* work) - { - BufferedMPIComm::get()->send(addr, work); - } + /*! do an MPI_Comm_dup, and return duplicated communicator */ + Group Group::dup() const + { + MPI_Comm duped; + MPI_CALL(Comm_dup(comm,&duped)); + return Group(duped); + } + + // Message definitions ////////////////////////////////////////////////////// + + /*! create a new message with given amount of bytes in storage */ + Message::Message(size_t size) : size(size) + { + data = (ospcommon::byte_t*)malloc(size); + } + + /*! create a new message with given amount of storage, and copy + memory from the given address to it */ + Message::Message(const void *copyMem, size_t size) : Message(size) + { + if (copyMem == nullptr) + OSPRAY_THROW("#mpicommon: cannot create a message from a null pointer!"); + + memcpy(data, copyMem, size); + } - void recv(const Address& addr, std::vector& work) - { - BufferedMPIComm::get()->recv(addr, work); + /*! create a new message (addressed to given comm:rank) with given + amount of storage, and copy memory from the given address to + it */ + Message::Message(MPI_Comm comm, int rank, + const void *copyMem, size_t size) + : Message(copyMem, size) + { + this->comm = comm; + this->rank = rank; + } + + /*! destruct message and free allocated memory */ + Message::~Message() + { + if (data) { + free(data); } + } + + bool Message::isValid() const + { + return comm != MPI_COMM_NULL && rank >= 0; + } + + void init(int *ac, const char **av) + { + int initialized = false; + MPI_CALL(Initialized(&initialized)); - void flush() - { - BufferedMPIComm::get()->flush(); + int provided = 0; + if (!initialized) { + /* MPI not initialized by the app - it's up to us */ + MPI_CALL(Init_thread(ac, (char ***)&av, + MPI_THREAD_MULTIPLE, &provided)); + } else { + /* MPI was already initialized by the app that called us! */ + MPI_Query_thread(&provided); } - void barrier(const Group& group) - { - BufferedMPIComm::get()->barrier(group); + int rank; + MPI_CALL(Comm_rank(MPI_COMM_WORLD,&rank)); + switch(provided) { + case MPI_THREAD_MULTIPLE: + mpiIsThreaded = true; + break; + case MPI_THREAD_SERIALIZED: + mpiIsThreaded = false; + break; + default: + throw std::runtime_error("fatal MPI error: MPI runtime doesn't offer " + "even MPI_THREAD_SERIALIZED ..."); } - } // ::ospray::mpi -} // ::ospray + + world.comm = MPI_COMM_WORLD; + MPI_CALL(Comm_rank(MPI_COMM_WORLD,&world.rank)); + MPI_CALL(Comm_size(MPI_COMM_WORLD,&world.size)); + } + +} // ::mpicommon diff --git a/components/mpiCommon/MPICommon.h b/components/mpiCommon/MPICommon.h index f920a98d5f..a3dd0156b6 100644 --- a/components/mpiCommon/MPICommon.h +++ b/components/mpiCommon/MPICommon.h @@ -19,11 +19,13 @@ // std #include #include + // mpi +#define OMPI_SKIP_MPICXX 1 #include + // ospcommon #include "ospcommon/common.h" -#include "OSPMPIConfig.h" #ifdef _WIN32 # ifdef ospray_mpi_common_EXPORTS @@ -41,126 +43,188 @@ #endif /*! helper macro that checks the return value of all MPI_xxx(...) calls via MPI_CALL(xxx(...)). */ -#define MPI_CALL(a) { int rc = MPI_##a; if (rc != MPI_SUCCESS) throw std::runtime_error("MPI call returned error"); } - -namespace ospray { - namespace mpi { - - inline void checkMpiError(int rc) - { - if (rc != MPI_SUCCESS) - throw std::runtime_error("MPI Error"); - } - - //! abstraction for an MPI group. - /*! it's the responsiblity of the respective mpi setup routines to - fill in the proper values */ - struct Group - { - /*! constructor. sets the 'comm', 'rank', and 'size' fields */ - Group(MPI_Comm initComm=MPI_COMM_NULL); - - // this is the RIGHT naming convention - old code has them all inside out. - void makeIntraComm() - { MPI_Comm_rank(comm,&rank); MPI_Comm_size(comm,&size); containsMe = true; } - void makeIntraComm(MPI_Comm comm) - { this->comm = comm; makeIntraComm(); } - void makeInterComm(MPI_Comm comm) - { this->comm = comm; makeInterComm(); } - void makeInterComm() - { containsMe = false; rank = MPI_ROOT; MPI_Comm_remote_size(comm,&size); } - - /*! set to given intercomm, and properly set size, root, etc */ - void setTo(MPI_Comm comm); - - /*! do an MPI_Comm_dup, and return duplicated communicator */ - Group dup() const; - - /*! perform a MPI_barrier on this communicator */ - void barrier() const { MPI_CALL(Barrier(comm)); } - - /*! whether the current process/thread is a member of this - gorup */ - bool containsMe {false}; - /*! communictor for this group. intercommunicator if i'm a - member of this gorup; else it's an intracommunicator */ - MPI_Comm comm {MPI_COMM_NULL}; - /*! my rank in this group if i'm a member; else set to - MPI_ROOT */ - int rank {-1}; - /*! size of this group if i'm a member, else size of remote - group this intracommunicaotr refers to */ - int size {-1}; - }; - - // //! abstraction for any other peer node that we might want to communicate with - struct Address - { - //! group that this peer is in - Group *group; - //! this peer's rank in this group - int rank; - - Address(Group *group = nullptr, int rank = -1) - : group(group), rank(rank) {} - inline bool isValid() const { return group != nullptr && rank >= 0; } - }; - - inline bool operator==(const Address &a, const Address &b) - { - return a.group == b.group && a.rank == b.rank; - } - - inline bool operator!=(const Address &a, const Address &b) - { - return !(a == b); - } - - //special flags for sending and reciving from all ranks instead of individuals - const int SEND_ALL = -1; - const int RECV_ALL = -1; - - //! MPI_COMM_WORLD - OSPRAY_MPI_INTERFACE extern Group world; - /*! for workers: intracommunicator to app - for app: intercommunicator among app processes - */ - OSPRAY_MPI_INTERFACE extern Group app; - /*!< group of all ospray workers (often the - world root is reserved for either app or - load balancing, and not part of the worker - group */ - OSPRAY_MPI_INTERFACE extern Group worker; - - // Initialize OSPRay's MPI groups - OSPRAY_MPI_INTERFACE void init(int *ac, const char **av); - - namespace work { - struct Work; - } - - // OSPRAY_INTERFACE void send(const Address& address, void* msgPtr, int32 msgSize); - OSPRAY_MPI_INTERFACE void send(const Address& addr, work::Work* work); - //TODO: callback? - OSPRAY_MPI_INTERFACE void recv(const Address& addr, std::vector& work); - // OSPRAY_INTERFACE void send(const Address& addr, ) - OSPRAY_MPI_INTERFACE void flush(); - OSPRAY_MPI_INTERFACE void barrier(const Group& group); - - inline int getWorkerCount() - { - return mpi::worker.size; - } - - inline int getWorkerRank() - { - return mpi::worker.rank; - } - - inline bool isMpiParallel() +#define MPI_CALL(a) { \ + int rc = MPI_##a; \ + if (rc != MPI_SUCCESS) \ + throw std::runtime_error("MPI call returned error"); \ +} + +#define OSPRAY_THROW(a) \ + throw std::runtime_error("in " + std::string(__PRETTY_FUNCTION__) + \ + " : " + std::string(a)) + +// Log level at which extremely verbose MPI logging output will +// be written +#define OSPRAY_MPI_VERBOSE_LEVEL 3 + +#define OSPRAY_WORLD_GROUP_TAG 290374 + +namespace mpicommon { + using namespace ospcommon; + + /*! global variable that turns on logging of MPI communication + (for debugging) _may_ eventually turn this into a real logLevel, + but for now tihs is cleaner here thatn in the MPI device + */ + OSPRAY_MPI_INTERFACE extern bool mpiIsThreaded; + + //! abstraction for an MPI group. + /*! it's the responsiblity of the respective mpi setup routines to + fill in the proper values */ + struct OSPRAY_MPI_INTERFACE Group + { + /*! constructor. sets the 'comm', 'rank', and 'size' fields */ + Group(MPI_Comm initComm = MPI_COMM_NULL); + Group(const Group &other); + + inline bool valid() const { - return getWorkerCount() > 0; + return comm != MPI_COMM_NULL; } - }// ::ospray::mpi -} // ::ospray + // this is the RIGHT naming convention - old code has them all inside out. + void makeIntraComm(); + void makeIntraComm(MPI_Comm); + void makeInterComm(); + void makeInterComm(MPI_Comm); + + /*! set to given intercomm, and properly set size, root, etc */ + void setTo(MPI_Comm comm); + + /*! do an MPI_Comm_dup, and return duplicated communicator */ + Group dup() const; + + /*! perform a MPI_barrier on this communicator */ + void barrier() const; + + /*! whether the current process/thread is a member of this + gorup */ + bool containsMe {false}; + /*! communictor for this group. intercommunicator if i'm a + member of this gorup; else it's an intracommunicator */ + MPI_Comm comm {MPI_COMM_NULL}; + /*! my rank in this group if i'm a member; else set to + MPI_ROOT */ + int rank {-1}; + /*! size of this group if i'm a member, else size of remote + group this intracommunicaotr refers to */ + int size {-1}; + }; + + /*! object that handles a message. a message primarily consists of a + pointer to data; the message itself "owns" this pointer, and + will delete it once the message itself dies. the message itself + is reference counted using the std::shared_ptr functionality. */ + struct OSPRAY_MPI_INTERFACE Message + { + Message() = default; + + /*! create a new message with given amount of bytes in storage */ + Message(size_t size); + + /*! create a new message with given amount of storage, and copy + memory from the given address to it */ + Message(const void *copyMem, size_t size); + + /*! create a new message (addressed to given comm:rank) with given + amount of storage, and copy memory from the given address to + it */ + Message(MPI_Comm comm, int rank, const void *copyMem, size_t size); + + /*! destruct message and free allocated memory */ + virtual ~Message(); + + bool isValid() const; + + /*! @{ sender/receiver of this message */ + MPI_Comm comm {MPI_COMM_NULL}; + int rank {-1}; + int tag { 0}; + /*! @} */ + + /*! @{ actual payload of this message */ + ospcommon::byte_t *data {nullptr}; + size_t size {0}; + /*! @} */ + }; + + /*! a message whose payload is owned by the user, and which we do + NOT delete upon termination */ + struct UserMemMessage : public Message + { + UserMemMessage(void *nonCopyMem, size_t size) + : Message() + { data = (ospcommon::byte_t*)nonCopyMem; this->size = size; } + + /* set data to null to keep the parent from deleting it */ + virtual ~UserMemMessage() + { data = nullptr; } + }; + + + //! MPI_COMM_WORLD + OSPRAY_MPI_INTERFACE extern Group world; + /*! for workers: intracommunicator to app + for app: intercommunicator among app processes + */ + OSPRAY_MPI_INTERFACE extern Group app; + /*!< group of all ospray workers (often the + world root is reserved for either app or + load balancing, and not part of the worker + group */ + OSPRAY_MPI_INTERFACE extern Group worker; + + // Initialize OSPRay's MPI groups + OSPRAY_MPI_INTERFACE void init(int *ac, const char **av); + + inline int globalRank() + { + return world.rank; + } + + inline int numGlobalRanks() + { + return world.size; + } + + inline int numWorkers() + { + return world.size - 1; + } + + inline int workerRank() + { + return worker.rank; + } + + inline bool isMpiParallel() + { + return numWorkers() > 0; + } + + inline int masterRank() + { + return 0; + } + + inline bool IamTheMaster() + { + return world.rank == masterRank(); + } + + inline bool IamAWorker() + { + return world.rank > 0; + } + + inline int workerRankFromGlobalRank(int globalRank) + { + return globalRank - 1; + } + + inline int globalRankFromWorkerRank(int workerRank) + { + return 1 + workerRank; + } + +} // ::mpicommon diff --git a/components/mpiCommon/SerialBuffer.cpp b/components/mpiCommon/SerialBuffer.cpp deleted file mode 100644 index 875dab10e3..0000000000 --- a/components/mpiCommon/SerialBuffer.cpp +++ /dev/null @@ -1,121 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "SerialBuffer.h" - -namespace ospray { - namespace mpi { - namespace work { - - Work::WorkMap Work::WORK_MAP; - - SerialBuffer::SerialBuffer(size_t sz) : buffer(sz, 255), index(0) - { - } - - byte_t* SerialBuffer::getPtr() - { - return &buffer[index]; - } - - void SerialBuffer::write(byte_t* data, size_t size) - { - // TODO: This should not call reserve, it should only grow if we - // actually need more space. Also it should use a different growing - // strategy than just allocating 1k extra than requested - reserve(size); - memcpy(getPtr(), data, size); - index += size; - // bytesAvailable += size; - } - - void SerialBuffer::read(byte_t* data, size_t size) - { - if (buffer.size() - getIndex() < size) { - PRINT(buffer.size()); - PRINT(getIndex()); - throw std::runtime_error("SerialBuffer is out of room for read of" - "size " + std::to_string(size)); - } - memcpy(data, getPtr(), size); - index += size; - // bytesAvailable -= size; - } - - void SerialBuffer::clear() - { - index = 0; - // bytesAvailable = 0; - } - - void SerialBuffer::reserve(size_t size) - { - if ((buffer.size() - getIndex()) < size) - { - /* - std::cout << "warning: reallocating SerialBuffer, had " - << buffer.size() << " bytes but need " << index + size << "\n"; - */ - // allocates over requested so we don't get a lot of small reallocations - // TODO: This should not allocate more than requested - buffer.resize(getIndex() + size + 1024); - // bytesAvailable += size + 1024; - } - } - - void decode_buffer(SerialBuffer &buf, - std::vector &cmds, - const int numMessages) - { - for (size_t i = 0; i < numMessages; ++i) { - size_t type = 0; - buf >> type; - Work::WorkMap::const_iterator fnd = Work::WORK_MAP.find(type); - if (fnd != Work::WORK_MAP.end()) { - Work *w = (*fnd->second)(); - buf >> *w; - cmds.push_back(w); - } else { - throw std::runtime_error("Unknown work message tag: " + - std::to_string(type)); - } - } - } - - void debug_log_messages(SerialBuffer &buf, const int numMessages) - { - size_t startIndex = buf.getIndex(); - for (size_t i = 0; i < numMessages; ++i) { - size_t type = 0; - buf >> type; - std::cout << "DBG Message[" << i << "] = " - << commandToString(CommandTag(type)) << "\n"; - Work::WorkMap::const_iterator fnd = Work::WORK_MAP.find(type); - if (fnd != Work::WORK_MAP.end()) { - Work *w = (*fnd->second)(); - buf >> *w; - delete w; - } else { - throw std::runtime_error("Unknown work message tag: " + - std::to_string(type)); - } - } - buf.setIndex(startIndex); - } - - }// namespace work - }// namespace mpi -}// namespace ospray diff --git a/components/mpiCommon/SerialBuffer.h b/components/mpiCommon/SerialBuffer.h deleted file mode 100644 index 3dc7003a54..0000000000 --- a/components/mpiCommon/SerialBuffer.h +++ /dev/null @@ -1,179 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2016 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include -#include -#include - -#include "mpiCommon/MPICommon.h" -#include "mpiCommon/command.h" - -namespace ospray { - namespace mpi { - namespace work { - - struct OSPRAY_MPI_INTERFACE SerialBuffer - { - std::vector buffer; - size_t index; - // size_t bytesAvailable; - - SerialBuffer(size_t sz = 1024 * 32); - // get data at current index WARNING: if realloced, ptrs invalidated - byte_t* getPtr(); - // get data at index WARNING: if realloced, ptrs invalidated - byte_t* getPtr(size_t ind) { return &buffer.at(ind); } - // return data at index 0 WARNING: if realloced, ptrs invalidated - byte_t* getData() { return buffer.data(); } - void write(byte_t* data, size_t size); - void read(byte_t* data, size_t size); - //reserve size elements from current index - void reserve(size_t size); - void clear(); //resets index to 0, does not actually clear out memory - size_t getIndex() { return index; } - void setIndex(size_t i) { index = i; } - // size_t size() const; - // bool empty() const; - //Carson: empty and size depend a bit on how you are utlizing the - // buffer not sure how the bytesRemaining was meant to be - // used - it's a self allocating buffer - }; - - struct OSPRAY_MPI_INTERFACE Work - { - friend SerialBuffer& operator<<(SerialBuffer& b, const Work &work); - friend SerialBuffer& operator>>(SerialBuffer& b, Work &work); - - virtual void run() {} - // Run the master-side variant of this command in master/worker mode - // The default just does nothing - virtual void runOnMaster() {} - virtual size_t getTag() const = 0; - // Check whether this work unit should flush the command buffer, - // the default returns false - virtual bool flushing() const { return false; } - - // Have the child work unit write its data to the buffer - virtual void serialize(SerialBuffer &b) const = 0; - // Have the child work unit read its data from the buffer - virtual void deserialize(SerialBuffer &b) = 0; - - using WorkMap = std::unordered_map; - // NOTE(jda) - No longer const as this is now populated on mpi module - // load - static WorkMap WORK_MAP; - }; - - // NOTE(jda) - I'd rather use constexpr here to give a name to value in - // the enable_if, but alas that's only supported in VS2015+... - template - using enable_SerialBuffer_operator_t = - typename std::enable_if::value && - !std::is_same::value && - !std::is_same::value, - SerialBuffer&>::type; - - // We can't take types which aren't POD or which are pointers - // because there's no sensible way to serialize/deserialize them. - // Unfortunately while the vecNT types are definitely just trivially - // copy-able data they don't meet the template constraints to be pod or - // trivially_copyable so we can't use that as a filter test - template - enable_SerialBuffer_operator_t - inline operator<<(SerialBuffer &buf, const T &rh) - { - buf.write((byte_t*)&rh, sizeof(T)); - return buf; - } - - template - enable_SerialBuffer_operator_t - inline operator>>(SerialBuffer &buf, T &rh) - { - buf.read((byte_t*)&rh, sizeof(T)); - return buf; - } - - template - typename std::enable_if::value, SerialBuffer&>::type - inline operator<<(SerialBuffer &buf, const std::vector &rh) - { - buf << rh.size(); - buf.write((byte_t*)rh.data(), rh.size() * sizeof(T)); - return buf; - } - - template - typename std::enable_if::value, SerialBuffer&>::type - inline operator>>(SerialBuffer &buf, std::vector &rh) - { - size_t size; - buf >> size; - rh = std::vector(size, T()); - buf.read((byte_t*)rh.data(), rh.size() * sizeof(T)); - return buf; - } - - // Child work classes should can implement these but should probably - // just go through serialize/deserialize so we can generically serialize - // work units into a buffer. - inline SerialBuffer& operator<<(SerialBuffer &b, const Work &work) - { - work.serialize(b); - return b; - } - - inline SerialBuffer& operator>>(SerialBuffer &b, Work &work) - { - work.deserialize(b); - return b; - } - - inline SerialBuffer &operator<<(SerialBuffer &buf, const std::string &rh) - { - buf << rh.size(); - buf.write((byte_t*)rh.c_str(), rh.size()); - return buf; - } - - inline SerialBuffer &operator>>(SerialBuffer &buf, std::string &rh) - { - size_t size; - buf >> size; - rh = std::string(size, ' '); - buf.read((byte_t*)&rh[0], size); - return buf; - } - - // Decode the commands in the buffer, appending them to the cmds vector. - // TODO: Should we be using std::shared_ptr here instead of raw pointers? - OSPRAY_MPI_INTERFACE void decode_buffer(SerialBuffer &buf, - std::vector &cmds, - const int numMessages); - OSPRAY_MPI_INTERFACE void debug_log_messages(SerialBuffer &buf, - const int numMessages); - - template - inline Work* make_work_unit() - { - return new T(); - } - - }// namespace work - }// namespace mpi -}// namespace ospray diff --git a/components/mpiCommon/async/BatchedIsendIrecvMessaging.cpp b/components/mpiCommon/async/BatchedIsendIrecvMessaging.cpp deleted file mode 100644 index 93cb76a6b7..0000000000 --- a/components/mpiCommon/async/BatchedIsendIrecvMessaging.cpp +++ /dev/null @@ -1,238 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include // for Sleep -#else -# include -#endif -#include -#include -#include -#include "BatchedIsendIrecvMessaging.h" - -namespace ospray { - namespace mpi { - namespace async { - - enum { SEND_WINDOW_SIZE = 48 }; - enum { RECV_WINDOW_SIZE = 48 }; - enum { PROC_WINDOW_SIZE = 20 }; - - BatchedIsendIrecvImpl::Group::Group(MPI_Comm comm, - Consumer *consumer, - int32 tag) - : async::Group(comm,consumer,tag), - sendThread(this), - procThread(this), - recvThread(this), - shouldExit(false) - { - sendThread.handle = std::thread([this](){this->sendThread.run();}); - procThread.handle = std::thread([this](){this->procThread.run();}); - recvThread.handle = std::thread([this](){this->recvThread.run();}); - } - - void BatchedIsendIrecvImpl::SendThread::run() - { - Group *g = this->group; - Action *actions[SEND_WINDOW_SIZE]; - MPI_Request request[SEND_WINDOW_SIZE]; - while (1) { - size_t numActions = 0; - while (numActions == 0){ - numActions = g->sendQueue.getSomeFor(actions,SEND_WINDOW_SIZE, - std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } - for (uint32_t i = 0; i < numActions; i++) { - Action *action = actions[i]; - MPI_CALL(Isend(action->data,action->size,MPI_BYTE, - action->addr.rank,g->tag,g->comm,&request[i])); - } - - // TODO: Is it ok to wait even if we're exiting? Maybe we'd just get send - // failed statuses back? - MPI_CALL(Waitall(numActions,request,MPI_STATUSES_IGNORE)); - - for (uint32_t i = 0; i < numActions; i++) { - Action *action = actions[i]; - free(action->data); - delete action; - } - } - } - - void BatchedIsendIrecvImpl::RecvThread::run() - { - // note this thread not only _probes_ for new receives, it - // also immediately starts the receive operation using Irecv() - Group *g = (Group *)this->group; - - MPI_Request request[RECV_WINDOW_SIZE]; - Action *actions[RECV_WINDOW_SIZE]; - MPI_Status status; - int numRequests = 0; -#ifdef _WIN32 - const DWORD sleep_time = 1; // ms --> much longer than 150us -#else - const timespec sleep_time = timespec{0, 150000}; -#endif - - while (1) { - numRequests = 0; - // wait for first message - { - // usleep(280); - int msgAvail = 0; - while (!msgAvail) { - MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); - if (g->shouldExit.load()){ - return; - } - if (msgAvail){ - break; - } -#ifdef _WIN32 - Sleep(sleep_time); -#else - // TODO: Can we do a CMake feature test for this_thread::sleep_for and - // conditionally use nanosleep? - nanosleep(&sleep_time, NULL); -#endif - } - Action *action = new Action; - action->addr = Address(g,status.MPI_SOURCE); - MPI_CALL(Get_count(&status,MPI_BYTE,&action->size)); - - action->data = malloc(action->size); - MPI_CALL(Irecv(action->data,action->size,MPI_BYTE, - status.MPI_SOURCE,status.MPI_TAG, - g->comm,&request[numRequests])); - actions[numRequests++] = action; - } - // ... and add all the other ones that are outstanding, up - // to max window size - while (numRequests < RECV_WINDOW_SIZE) { - int msgAvail; - MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); - if (!msgAvail) - break; - - Action *action = new Action; - action->addr = Address(g,status.MPI_SOURCE); - MPI_CALL(Get_count(&status,MPI_BYTE,&action->size)); - - action->data = malloc(action->size); - MPI_CALL(Irecv(action->data,action->size,MPI_BYTE, - status.MPI_SOURCE,status.MPI_TAG, - g->comm,&request[numRequests])); - actions[numRequests++] = action; - } - - // TODO: Is it ok to wait even if we're exiting? Maybe we'd just get send - // failed statuses back? - // now, have certain number of messages available... - MPI_CALL(Waitall(numRequests,request,MPI_STATUSES_IGNORE)); - - // OK, all actions are valid now - g->recvQueue.putSome(&actions[0],numRequests); - } - } - - void BatchedIsendIrecvImpl::ProcThread::run() - { - Group *g = (Group *)this->group; - while (1) { - Action *actions[PROC_WINDOW_SIZE]; - size_t numActions = 0; - while (numActions == 0){ - numActions = g->recvQueue.getSomeFor(actions,PROC_WINDOW_SIZE, - std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } - for (uint32_t i = 0; i < numActions; i++) { - Action *action = actions[i]; - g->consumer->process(action->addr,action->data,action->size); - delete action; - } - } - } - - void BatchedIsendIrecvImpl::Group::shutdown() - { - std::cout << "#osp:mpi:BatchIsendIrecvMessaging:Group shutting down" - << std::endl; - shouldExit.store(true); - sendThread.handle.join(); - recvThread.handle.join(); - procThread.handle.join(); - } - - void BatchedIsendIrecvImpl::init() - { - mpi::world.barrier(); - printf("#osp:mpi:BatchedIsendIrecvMessaging started up %i/%i\n", - mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - } - - void BatchedIsendIrecvImpl::shutdown() - { - mpi::world.barrier(); - printf("#osp:mpi:BatchedIsendIrecvMessaging shutting down %i/%i\n", - mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - for (uint32_t i = 0; i < myGroups.size(); i++) - myGroups[i]->shutdown(); - - printf("#osp:mpi:BatchedIsendIrecvMessaging finalizing %i/%i\n", - mpi::world.rank,mpi::world.size); - MPI_CALL(Finalize()); - } - - async::Group *BatchedIsendIrecvImpl::createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag) - { - Group *g = new Group(comm,consumer,tag); - myGroups.push_back(g); - return g; - } - - void BatchedIsendIrecvImpl::send(const Address &dest, - void *msgPtr, - int32 msgSize) - { - Action *action = new Action; - action->addr = dest; - action->data = msgPtr; - action->size = msgSize; - - Group *g = (Group *)dest.group; - g->sendQueue.put(action); - } - - } // ::ospray::mpi::async - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/BatchedIsendIrecvMessaging.h b/components/mpiCommon/async/BatchedIsendIrecvMessaging.h deleted file mode 100644 index db2f874804..0000000000 --- a/components/mpiCommon/async/BatchedIsendIrecvMessaging.h +++ /dev/null @@ -1,125 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -// ours -#include "Messaging.h" -// ospcommon -#include "ospcommon/ProducerConsumerQueue.h" -// stl -#include -#include -#include -#include - -namespace ospray { - namespace mpi { - namespace async { - struct BatchedIsendIrecvImpl : public AsyncMessagingImpl - { - struct Group; - struct Message; - - struct ThreadBase - { - std::thread handle; - }; - - /*! message _sender_ thread */ - struct SendThread : public ThreadBase { - SendThread(Group *group) : group(group) { -#ifdef OSPRAY_PIN_ASYNC - embree::setAffinity(58); // 58 -#endif - } - virtual void run(); - - Group *group; - }; - - /*! message _probing_ thread - continuously probes for newly - incoming messages, and puts them into recv queue */ - struct RecvThread : public ThreadBase - { - RecvThread(Group *group) : group(group) - { -#ifdef OSPRAY_PIN_ASYNC - embree::setAffinity(55); // 55 -#endif - } - - virtual void run(); - Group *group; - }; - - /*! message _processing_ thread */ - struct ProcThread : public ThreadBase - { - ProcThread(Group *group) : group(group) - { -#ifdef OSPRAY_PIN_ASYNC - embree::setAffinity(57); // 56 -#endif - } - virtual void run(); - - Group *group; - }; - - /*! an 'action' (either a send or a receive, or a - processmessage) to be performed by a thread; what action - it is depends on the queue it is in */ - struct Action - { - void *data; - int32 size; - Address addr; - MPI_Request request; - }; - - struct Group : public mpi::async::Group - { - Group(MPI_Comm comm, Consumer *consumer, int32 tag = MPI_ANY_TAG); - void shutdown(); - - /*! the queue new send requests are put into; the send - thread pulls from this and sends ad infinitum */ - - ProducerConsumerQueue sendQueue; - /*! the queue that newly received messages are put in; the - reiver thread puts new messages in here, the processing - thread pulls from here and processes */ - ProducerConsumerQueue recvQueue; - - SendThread sendThread; - ProcThread procThread; - RecvThread recvThread; - std::atomic shouldExit; - }; - - virtual void init(); - virtual void shutdown(); - virtual async::Group *createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag = MPI_ANY_TAG); - virtual void send(const Address &dest, void *msgPtr, int32 msgSize); - - std::vector myGroups; - }; - } - } -} diff --git a/components/mpiCommon/async/CommLayer.cpp b/components/mpiCommon/async/CommLayer.cpp deleted file mode 100644 index 1eb0a07df6..0000000000 --- a/components/mpiCommon/async/CommLayer.cpp +++ /dev/null @@ -1,81 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#include "CommLayer.h" - -/*! \file ospray/mpi/async/CommLayer.h Extends the asynchronous - messaging protocal (that can send messages asynchronously from one - rank to another) by a communication layer that allows sending from - one *object* in a rank to another *object* in another rank using a - global registry of distributed objects */ - -namespace ospray { - namespace mpi { - namespace async { - - CommLayer *CommLayer::WORLD = nullptr; - - CommLayer::Object::Object(CommLayer *comm, ObjectID myID) - : myID(myID), comm(comm) - { - master = mpi::async::CommLayer::Address(comm->masterRank(),myID); - worker = new mpi::async::CommLayer::Address[comm->numWorkers()]; - for (int i = 0 ; i < comm->numWorkers(); i++) - worker[i] = mpi::async::CommLayer::Address(comm->workerRank(i),myID); - } - - void CommLayer::process(const mpi::Address &source, - void *message, - int32 size) - { - UNUSED(size); - Message *msg = (Message*)message; - - Object *obj = nullptr; - { - SCOPED_LOCK(mutex); - obj = registry[msg->dest.objectID]; - } - - if (!obj) { - throw std::runtime_error("#osp:mpi:CommLayer: no object with given " - "ID"); - } - - msg->source.rank = source.rank; - obj->incoming(msg); - } - - void CommLayer::sendTo(Address dest, Message *msg, size_t size) - { - msg->source.rank = group->rank; - msg->dest = dest; - msg->size = size; - async::send(mpi::Address(group,dest.rank),msg,size); - } - - void CommLayer::registerObject(Object *object, ObjectID ID) - { - /* WARNING: though we do protect the registry here, the - registry lookup itself is NOT thread-safe right now */ - SCOPED_LOCK(mutex); - assert(registry.find(ID) == registry.end()); - registry[ID] = object; - } - - } // ::ospray::mpi::async - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/CommLayer.h b/components/mpiCommon/async/CommLayer.h deleted file mode 100644 index 7082988354..0000000000 --- a/components/mpiCommon/async/CommLayer.h +++ /dev/null @@ -1,139 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "Messaging.h" -#include -#include - -/*! \file ospray/mpi/async/CommLayer.h Extends the asynchronous - messaging protocal (that can send messages asynchronously from one - rank to another) by a communication layer that allows sending from - one *object* in a rank to another *object* in another rank using a - global registry of distributed objects */ - -namespace ospray { - namespace mpi { - namespace async { - - /*! object that handles the registration of distributed - objects. objects can send messages to other objects (on other - nodes) that are registered on thsi comm layer (to be more - exact: to objects registered at the _remote_ instance of the - commlayer - though a distributed objects would usually - register itself on each rank's instance of the commlayer, in - principle it could be registered only one one (say, the - master), and others can send to that as long as they know the - ID at the rank it is registered at). - - The commlayer object itself is a async::Consumer - it receives - messages from other nodes, and then routes them to the - respective object(s). - */ - struct OSPRAY_MPI_INTERFACE CommLayer : public async::Consumer - { - using ObjectID = int32; - - static CommLayer *WORLD; - - //! the async::consumer virtual callback that a async::message arrived - void process(const mpi::Address &source, - void *message, - int32 size) override; - - struct Address - { - Address(int32 rank = -1, ObjectID objectID = -1) - : rank(rank), objectID(objectID) - {} - - int32 rank; - ObjectID objectID; - }; - - /*! a common header used for all messages sent over the - commlayer, to enable proper routing at the remote node. */ - struct Message - { - /*! @{ internal header - do not use from the - application. Note we have to have this field inside the - messsage, because the messaging protocal doesn't send - headers and messages separately, but requires both to be - a single block of data */ - Address source; /*!< address of sender that sent this - message. commlayer will fill this in, - user doesn't have to carea */ - Address dest; /*!< address of sender that will receive this - message. commlayer will fill this in, - user doesn't have to carea */ - size_t size; /*!< size, in bytes. Commlayer will fill that - in, user doesn't have to care. */ - - int64 command; /*!< optional command field similar to an - MPI tag (allows to send messages similar - to some RPC protocol). may be unused */ - /*! @} */ - }; - - /*! abstraction for an object that can send and receive messages */ - struct OSPRAY_MPI_INTERFACE Object - { - Object(CommLayer *comm, ObjectID myID); - - /*! function that will be called by the commlayer as soon as - a message for this object comes in. this function should - execute as soon as possible - needing a seprate thread - to actually do heavy work if needed - because this - function may actually block the system's receiver thread - until complete */ - virtual void incoming(mpi::async::CommLayer::Message *msg) = 0; - - ObjectID myID; - CommLayer *comm; - - //! address of corresponding instance of 'this' on master - mpi::async::CommLayer::Address master; - //! address of corresponding instance of 'this' on i'th worker - mpi::async::CommLayer::Address *worker; - }; - - void sendTo(Address dest, Message *msg, size_t size); - - void registerObject(Object *object, ObjectID ID); - - //! convenience function that returns our rank in the comm group - inline int32 rank() const { return group->rank; } - - inline int32 numWorkers() const { return group->size - 1; } - inline static int32 masterRank() { return 0; } - inline static int32 workerRank(int clientID) { return 1 + clientID; } - inline bool IamTheMaster() const { return group->rank == masterRank(); } - - //! mutex to protect the registry - std::mutex mutex; - - /*! registry that allows to lookup objects by handle. objects - have to register themselves in order to be reachable */ - std::map registry; - - /*! the mpi::async group we're using to communicate with - remote nodes */ - async::Group *group; - }; - } // ::ospray::mpi::async - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/Messaging.cpp b/components/mpiCommon/async/Messaging.cpp deleted file mode 100644 index 93fc966626..0000000000 --- a/components/mpiCommon/async/Messaging.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -// ours -#include "Messaging.h" -#include "BatchedIsendIrecvMessaging.h" - -namespace ospray { - namespace mpi { - - namespace async { - - AsyncMessagingImpl *AsyncMessagingImpl::global = NULL; - - Group::Group(MPI_Comm comm, Consumer *consumer, int32 tag) - : consumer(consumer), tag(tag) - { - MPI_CALL(Comm_dup(comm,&this->comm)); - MPI_CALL(Comm_rank(comm,&rank)); - MPI_CALL(Comm_size(comm,&size)); - } - - Group *WORLD = NULL; - - // init async layer - void initAsync() - { - if (AsyncMessagingImpl::global == NULL) { -// #if 1 - AsyncMessagingImpl::global = new BatchedIsendIrecvImpl; -// #elif 1 -// AsyncMessagingImpl::global = new MultiIsendIrecvImpl; -// #else -// AsyncMessagingImpl::global = new SimpleSendRecvImpl; -// #endif - AsyncMessagingImpl::global->init(); - } - - // extern Group *WORLD; - } - - Group *createGroup(MPI_Comm comm, Consumer *consumer, int32 tag) - { - initAsync(); - return AsyncMessagingImpl::global->createGroup(comm,consumer,tag); - } - - void shutdown() - { - AsyncMessagingImpl::global->shutdown(); - } - - void send(const Address &dest, void *msgPtr, int32 msgSize) - { - AsyncMessagingImpl::global->send(dest,msgPtr,msgSize); - } - - } // ::ospray::mpi::async - - - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/Messaging.h b/components/mpiCommon/async/Messaging.h deleted file mode 100644 index a5824adf99..0000000000 --- a/components/mpiCommon/async/Messaging.h +++ /dev/null @@ -1,96 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -#include "../MPICommon.h" - -namespace ospray { - namespace mpi { - - typedef int32_t int32; - typedef uint32_t uint32; - typedef int64_t int64; - typedef uint64_t uint64; - - namespace async { - struct Message - { - Address addr; - void *ptr; - int32 size; - MPI_Request request; //! request for MPI_Test - int done; //! done flag for MPI_Test - MPI_Status status; //! status for MPI_Test - }; - - /*! a 'async::Consumer is any class that can handle incoming - messages. how it handles those isn't defined, other than - that the consumer is supposed to 'free' the message after it - is done processing (the comm layer itself will never free - it!), and that it should strive to process this message as - quickly as it possibly can (using a separate worker - thread(s) if need be */ - struct Consumer - { - virtual void process(const Address &source, void *message, int32 size) = 0; - }; - - struct OSPRAY_MPI_INTERFACE Group : public mpi::Group - { - //! message consumer (may be NULL) - - Group(// const std::string &name, - MPI_Comm comm, - Consumer *consumer, int32 tag = MPI_ANY_TAG); - // virtual std::string toString() { return "mpi::async::Group("+name+")"; }; - - //! consumer to handle this group's incoming messags - Consumer *consumer; - //! tag to be used for asynchronous messaging - int32 tag; - }; - - //! abstraction - internally used - implement the messaging MPI - struct OSPRAY_MPI_INTERFACE AsyncMessagingImpl - { - virtual void init() = 0; - virtual void shutdown() = 0; - virtual Group *createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag = MPI_ANY_TAG) = 0; - virtual void send(const Address &dest, void *msgPtr, int32 msgSize) = 0; - - static AsyncMessagingImpl *global; - }; - - /*! @{ The actual asynchronous messaging API */ - OSPRAY_MPI_INTERFACE Group *createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag = MPI_ANY_TAG); - OSPRAY_MPI_INTERFACE void shutdown(); - - /*! send a asynchronous message to the address specified. the - 'group' in said address HAS to be a group created via - 'async::Group' */ - OSPRAY_MPI_INTERFACE void send(const Address &dest, void *msgPtr, int32 msgSize); - /*! @} */ - - } // ::ospray::mpi::async - - - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/MultiIsendIrecvMessaging.cpp b/components/mpiCommon/async/MultiIsendIrecvMessaging.cpp deleted file mode 100644 index f94decfb65..0000000000 --- a/components/mpiCommon/async/MultiIsendIrecvMessaging.cpp +++ /dev/null @@ -1,183 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 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. // -// ======================================================================== // - -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include // for Sleep -#else -# include -#endif -#include -#include -#include -#include "MultiIsendIrecvMessaging.h" - -namespace ospray { - namespace mpi { - namespace async { - - MultiIsendIrecvImpl::Group::Group(MPI_Comm comm, - Consumer *consumer, - int32 tag) - : async::Group(comm,consumer,tag), - sendThread(this), - procThread(this), - recvThread(this) - { - sendThread.start(); - procThread.start(); - recvThread.start(); - } - - void MultiIsendIrecvImpl::SendThread::run() - { - Group *g = this->group; - while (1) { - Action *action = nullptr; - size_t numActions = 0; - while (numActions == 0){ - numActions = g->sendQueue.getSomeFor(&action, - 1, - std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } - // note we're not using any window here; we just pull them - // in order. this is OK because they key is to have mulitple - // sends going in parallel - which we do because each - // (i)send is already started by the time it enters the - // queue - MPI_Wait(&action->request,MPI_STATUS_IGNORE); - free(action->data); - delete action; - } - } - - void MultiIsendIrecvImpl::RecvThread::run() - { - // note this thread not only _probes_ for new receives, it - // also immediately starts the receive operation using Irecv() - Group *g = (Group *)this->group; -#ifdef _WIN32 - const DWORD sleep_time = 1; // ms --> much longer than 150us -#else - const timespec sleep_time = timespec{ 0, 150000 }; -#endif - - while (1) { - MPI_Status status; - int msgAvail = 0; - while (!msgAvail) { - MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); - if (g->shouldExit.load()){ - return; - } - if (msgAvail){ - break; - } -#ifdef _WIN32 - Sleep(sleep_time); -#else - // TODO: Can we do a CMake feature test for this_thread::sleep_for and - // conditionally use nanosleep? - nanosleep(&sleep_time, NULL); -#endif - } - Action *action = new Action; - action->addr = Address(g,status.MPI_SOURCE); - - MPI_CALL(Get_count(&status,MPI_BYTE,&action->size)); - - action->data = malloc(action->size); - MPI_CALL(Irecv(action->data,action->size,MPI_BYTE,status.MPI_SOURCE,status.MPI_TAG, - g->comm,&action->request)); - g->recvQueue.put(action); - } - } - - void MultiIsendIrecvImpl::ProcThread::run() - { - Group *g = (Group *)this->group; - while (1) { - Action *action = nullptr; - size_t numActions = 0; - while (numActions == 0){ - numActions = g->recvQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } - MPI_CALL(Wait(&action->request,MPI_STATUS_IGNORE)); - g->consumer->process(action->addr,action->data,action->size); - delete action; - } - } - - void MultiIsendIrecvImpl::Group::shutdown() - { - std::cout << "#osp:mpi:MultiIsendIrecvImpl:Group shutting down" << std::endl; - shouldExit.store(true); - sendThread.join(); - recvThread.join(); - procThread.join(); - } - - void MultiIsendIrecvImpl::init() - { - mpi::world.barrier(); - printf("#osp:mpi:MultiIsendIrecvMessaging started up %i/%i\n",mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - } - - void MultiIsendIrecvImpl::shutdown() - { - mpi::world.barrier(); - printf("#osp:mpi:MultiIsendIrecvMessaging shutting down %i/%i\n",mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - for (uint32_t i = 0; i < myGroups.size(); i++) - myGroups[i]->shutdown(); - - MPI_CALL(Finalize()); - } - - async::Group *MultiIsendIrecvImpl::createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag) - { - Group *g = new Group(comm,consumer,tag); - myGroups.push_back(g); - return g; - } - - void MultiIsendIrecvImpl::send(const Address &dest, void *msgPtr, int32 msgSize) - { - Action *action = new Action; - action->addr = dest; - action->data = msgPtr; - action->size = msgSize; - - Group *g = (Group *)dest.group; - MPI_CALL(Isend(action->data,action->size,MPI_BYTE, - dest.rank,g->tag,g->comm,&action->request)); - g->sendQueue.put(action); - } - - } // ::ospray::mpi::async - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/MultiIsendIrecvMessaging.h b/components/mpiCommon/async/MultiIsendIrecvMessaging.h deleted file mode 100644 index 8b3107f92d..0000000000 --- a/components/mpiCommon/async/MultiIsendIrecvMessaging.h +++ /dev/null @@ -1,106 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -// ospray -#include "Messaging.h" -#include "ospcommon/thread.h" -#include "ospcommon/ProducerConsumerQueue.h" -// stl -#include -#include -#include - -namespace ospray { - namespace mpi { - namespace async { - struct MultiIsendIrecvImpl : public AsyncMessagingImpl - { - struct Group; - struct Message; - - /*! message _sender_ thread */ - struct SendThread : public ospcommon::Thread - { - SendThread(Group *group) : group(group) {} - virtual void run(); - - Group *group; - }; - - /*! message _probing_ thread - continuously probes for newly - incoming messages, and puts them into recv queue */ - struct RecvThread : public ospcommon::Thread - { - RecvThread(Group *group) : group(group) {} - - virtual void run(); - Group *group; - }; - - /*! message _processing_ thread */ - struct ProcThread : public ospcommon::Thread - { - ProcThread(Group *group) : group(group) {} - virtual void run(); - - Group *group; - }; - - /*! an 'action' (either a send or a receive, or a - processmessage) to be performed by a thread; what action - it is depends on the queue it is in */ - struct Action - { - void *data; - int32 size; - Address addr; - MPI_Request request; - }; - - struct Group : public mpi::async::Group - { - Group(MPI_Comm comm, Consumer *consumer, int32 tag = MPI_ANY_TAG); - void shutdown(); - - /*! the queue new send requests are put into; the send - thread pulls from this and sends ad infinitum */ - - ProducerConsumerQueue sendQueue; - /*! the queue that newly received messages are put in; the - reiver thread puts new messages in here, the processing - thread pulls from here and processes */ - ProducerConsumerQueue recvQueue; - - SendThread sendThread; - ProcThread procThread; - RecvThread recvThread; - std::atomic shouldExit; - }; - - virtual void init(); - virtual void shutdown(); - virtual async::Group *createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag = MPI_ANY_TAG); - virtual void send(const Address &dest, void *msgPtr, int32 msgSize); - - std::vector myGroups; - }; - } - } -} diff --git a/components/mpiCommon/async/SimpleSendRecvMessaging.cpp b/components/mpiCommon/async/SimpleSendRecvMessaging.cpp deleted file mode 100644 index 83ce8cf80e..0000000000 --- a/components/mpiCommon/async/SimpleSendRecvMessaging.cpp +++ /dev/null @@ -1,260 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -//#define PROFILE_MPI 1 - -#ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN -# include // for Sleep -#else -# include -#endif -#include -#include -#include -#include "SimpleSendRecvMessaging.h" - -namespace ospray { - namespace mpi { - namespace async { - -#if PROFILE_MPI - int frameID = 0; - bool logIt = 0; - double T0; - double t_recv, t_send; - size_t b_recv, b_sent; - - struct MsgLog { - int to, size; - double begin, end; - }; - std::vector sendLog, recvLog; - - extern "C" void async_beginFrame() - { - ++ frameID; - if (frameID > 20) logIt = true; - - if (logIt) { - T0 = getSysTime(); - t_send = 0; - t_recv = 0; - b_recv = 0; - b_sent = 0; - sendLog.clear(); - recvLog.clear(); - } - } - - extern "C" void async_endFrame() - { - printf("rank %i t_send %fMb in %fs recv %fMb in %fs\n", - mpi::world.rank, - b_sent*1e-6f,t_send,b_recv*1e-6f,t_recv); - for (int i=0;igroup; - - while (1) { - Action *action = nullptr; - size_t numActions = 0; - while (numActions == 0){ - numActions = g->sendQueue.getSomeFor(&action, - 1, - std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } -#if PROFILE_MPI - double t0 = getSysTime(); -#endif - MPI_CALL(Send(action->data,action->size,MPI_BYTE, - action->addr.rank,g->tag,action->addr.group->comm)); -#if PROFILE_MPI - double t1 = getSysTime(); - if (logIt) { - t_send += (t1-t0); - b_sent += action->size; - - MsgLog log; - log.to = action->addr.rank; - log.size = action->size; - log.begin = t0; - log.end = t1; - sendLog.push_back(log); - } -#endif - free(action->data); - delete action; - } - } - - void SimpleSendRecvImpl::RecvThread::run() - { - Group *g = (Group *)this->group; -#ifdef _WIN32 - const DWORD sleep_time = 1; // ms --> much longer than 150us -#else - const timespec sleep_time = timespec{ 0, 150000 }; -#endif - - while (1) { - MPI_Status status; - int msgAvail = 0; - while (!msgAvail) { - MPI_CALL(Iprobe(MPI_ANY_SOURCE,g->tag,g->comm,&msgAvail,&status)); - if (g->shouldExit.load()){ - return; - } - if (msgAvail){ - break; - } -#ifdef _WIN32 - Sleep(sleep_time); -#else - // TODO: Can we do a CMake feature test for this_thread::sleep_for and - // conditionally use nanosleep? - nanosleep(&sleep_time, NULL); -#endif - } - Action *action = new Action; - action->addr = Address(g,status.MPI_SOURCE); - - MPI_CALL(Get_count(&status,MPI_BYTE,&action->size)); - - action->data = malloc(action->size); -#if PROFILE_MPI - double t0 = getSysTime(); -#endif - MPI_CALL(Recv(action->data,action->size, - MPI_BYTE,status.MPI_SOURCE,status.MPI_TAG, - g->comm,MPI_STATUS_IGNORE)); -#if PROFILE_MPI - double t1 = getSysTime(); - if (logIt) { - t_recv += (t1-t0); - b_recv += action->size; - - MsgLog log; - log.to = action->addr.rank; - log.size = action->size; - log.begin = t0; - log.end = t1; - recvLog.push_back(log); - } -#endif - g->procQueue.put(action); - } - } - - void SimpleSendRecvImpl::ProcThread::run() - { - Group *g = (Group *)this->group; - while (1) { - Action *action = nullptr; - size_t numActions = 0; - while (numActions == 0){ - numActions = g->procQueue.getSomeFor(&action,1,std::chrono::milliseconds(1)); - if (g->shouldExit.load()){ - return; - } - } - g->consumer->process(action->addr,action->data,action->size); - delete action; - } - } - - void SimpleSendRecvImpl::Group::shutdown() - { - std::cout << "#osp:mpi:SimpleSendRecvImpl:Group shutting down" << std::endl; - shouldExit.store(true); - sendThread.join(); - recvThread.join(); - procThread.join(); - } - - void SimpleSendRecvImpl::init() - { - mpi::world.barrier(); - printf("#osp:mpi:SimpleSendRecvMessaging started up %i/%i\n",mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - } - - void SimpleSendRecvImpl::shutdown() - { - mpi::world.barrier(); - printf("#osp:mpi:SimpleSendRecvMessaging shutting down %i/%i\n",mpi::world.rank,mpi::world.size); - fflush(0); - mpi::world.barrier(); - for (uint32_t i = 0; i < myGroups.size(); i++) - myGroups[i]->shutdown(); - - MPI_CALL(Finalize()); - } - - async::Group *SimpleSendRecvImpl::createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag) - { - Group *g = new Group(comm,consumer,tag); - myGroups.push_back(g); - return g; - } - - void SimpleSendRecvImpl::send(const Address &dest, void *msgPtr, int32 msgSize) - { - Action *action = new Action; - action->addr = dest; - action->data = msgPtr; - action->size = msgSize; - - Group *g = (Group *)dest.group; - g->sendQueue.put(action); - } - - } // ::ospray::mpi::async - } // ::ospray::mpi -} // ::ospray diff --git a/components/mpiCommon/async/SimpleSendRecvMessaging.h b/components/mpiCommon/async/SimpleSendRecvMessaging.h deleted file mode 100644 index b77f5fb10c..0000000000 --- a/components/mpiCommon/async/SimpleSendRecvMessaging.h +++ /dev/null @@ -1,100 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -// ospray -#include "Messaging.h" -#include "ospcommon/thread.h" -#include "ospcommon/ProducerConsumerQueue.h" -// stl -#include -#include -#include - -namespace ospray { - namespace mpi { - namespace async { - struct SimpleSendRecvImpl : public AsyncMessagingImpl - { - struct Group; - - /*! message _sender_ thread */ - struct SendThread : public ospcommon::Thread - { - SendThread(Group *group) : group(group) {} - virtual void run(); - Group *group; - }; - - /*! message _receiver_ thread */ - struct RecvThread : public ospcommon::Thread - { - RecvThread(Group *group) : group(group) {} - virtual void run(); - Group *group; - }; - - /*! message _processing_ thread */ - struct ProcThread : public ospcommon::Thread - { - ProcThread(Group *group) : group(group) {} - virtual void run(); - Group *group; - }; - - /*! an 'action' (either a send or a receive, or a - processmessage) to be performed by a thread; what action - it is depends on the queue it is in */ - struct Action - { - void *data; - int32 size; - Address addr; - }; - - struct Group : public mpi::async::Group - { - Group(MPI_Comm comm, Consumer *consumer, int32 tag = MPI_ANY_TAG); - void shutdown(); - - /*! the queue new send requests are put into; the send - thread pulls from this and sends ad infinitum */ - - ProducerConsumerQueue sendQueue; - /*! the queue that newly received messages are put in; the - reiver thread puts new messages in here, the processing - thread pulls from here and processes */ - ProducerConsumerQueue procQueue; - - SendThread sendThread; - RecvThread recvThread; - ProcThread procThread; - std::atomic shouldExit; - }; - - virtual void init(); - virtual void shutdown(); - virtual async::Group *createGroup(MPI_Comm comm, - Consumer *consumer, - int32 tag = MPI_ANY_TAG); - virtual void send(const Address &dest, void *msgPtr, int32 msgSize); - - std::vector myGroups; - }; - } - } -} diff --git a/components/mpiCommon/buffers.h b/components/mpiCommon/buffers.h deleted file mode 100644 index c50b393d8e..0000000000 --- a/components/mpiCommon/buffers.h +++ /dev/null @@ -1,124 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -/*! \file ospray/device/nwlayer.h \brief Implement utility read/write - buffers that can be used to send data across all kinds of - network/communication devices */ - -#include "ospray/common/OSPCommon.h" - -namespace ospray { - namespace nwlayer { - - /*! implements a local data buffer that keeps data sent through a - network device, and that the reader can read typed data - from */ - struct ReadBuffer { - unsigned char *mem; - size_t size; - size_t next; /*!< next posiiton we're reading from */ - bool mine; - inline ReadBuffer() - : mem(NULL), size(0), mine(false) - {} - inline ReadBuffer(size_t sz) - : mem((unsigned char*)malloc(sz)), size(sz), next(0), mine(true) - { assert(mem); } - inline ReadBuffer(unsigned char *ptr, size_t sz) - : mem(ptr), size(sz), next(0), mine(false) - {} - inline ~ReadBuffer() - { if (mine) free(mem); } - inline void read(void *t, size_t sz) - { assert(next+sz <= size); memcpy(t,mem+next,sz); next+=sz; } - - /*! helper function to read typed data from a recv buffer */ - template inline T read() - { T t; this->read(&t,sizeof(t)); return t; } - }; - - /*! implements a local data buffer that one can write data to; - this data then gets queues up in a single memory region, from - where it can be sent in a single network message */ - struct WriteBuffer { - static const int initialReservedSize = 16*1024; - unsigned char *mem; - size_t size; - size_t reserved; - /*! constructor allocate new empty data stream of initial - reserved size */ - inline WriteBuffer() - : mem((unsigned char *)malloc(initialReservedSize)), - size(0), - reserved(initialReservedSize) - { assert(mem); }; - /*! destructor - free alloc'ed memory */ - inline ~WriteBuffer() - { free((char*)mem); } - /*! reserve at least 'delta' bytes at end of stream */ - inline void reserve(size_t delta) { - if (size+delta <= reserved) return; - while (size+delta > reserved) reserved *= 2; - mem = (unsigned char *)realloc(mem,reserved); - } - /*! append new (untyped) block of mem to buffer */ - inline void write(const void *t, size_t t_size) - { reserve(t_size); memcpy(mem+size,t,t_size); size+=t_size; } - - /*! helper function to write typed data into this buffer */ - template inline void write(const T &t) - { this->write(&t,sizeof(t)); } - }; - - /*! explicit instantiation for string types */ - template<> inline const char *ReadBuffer::read() - { - int32 len = read(); - char *s = (char*)malloc(len+1); - read(s,len+1); - return s; - } - /*! explicit instantiation for string types */ - template<> inline char *ReadBuffer::read() - { - return (char*)read(); - } - /*! explicit instantiation for string types */ - template<> inline std::string ReadBuffer::read() - { - const char *s = read(); - std::string ss = s; - free((char*)s); - return s; - } - - - /*! explicit instantiation for string types */ - template<> inline void WriteBuffer::write(const char *const &s) - { - const int32 len = strlen(s); - write(len); - this->write(s,len+1); - } - /*! explicit instantiation for string types */ - template<> inline void WriteBuffer::write(const std::string &s) { - write(s.c_str()); - } - - } // ::ospray::nwlayer -} // ::ospray diff --git a/components/mpiCommon/command.cpp b/components/mpiCommon/command.cpp deleted file mode 100644 index 5948f4503f..0000000000 --- a/components/mpiCommon/command.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "command.h" - -namespace ospray { -const char* commandToString(CommandTag tag) { - switch (tag) { - case CMD_INVALID: return "CMD_INVALID"; - case CMD_NEW_RENDERER: return "CMD_NEW_RENDERER"; - case CMD_FRAMEBUFFER_CREATE: return "CMD_FRAMEBUFFER_CREATE"; - case CMD_RENDER_FRAME: return "CMD_RENDER_FRAME"; - case CMD_FRAMEBUFFER_CLEAR: return "CMD_FRAMEBUFFER_CLEAR"; - case CMD_FRAMEBUFFER_MAP: return "CMD_FRAMEBUFFER_MAP"; - case CMD_FRAMEBUFFER_UNMAP: return "CMD_FRAMEBUFFER_UNMAP"; - case CMD_NEW_DATA: return "CMD_NEW_DATA"; - case CMD_NEW_MODEL: return "CMD_NEW_MODEL"; - case CMD_NEW_GEOMETRY: return "CMD_NEW_GEOMETRY"; - case CMD_NEW_MATERIAL: return "CMD_NEW_MATERIAL"; - case CMD_NEW_LIGHT: return "CMD_NEW_LIGHT"; - case CMD_NEW_CAMERA: return "CMD_NEW_CAMERA"; - case CMD_NEW_VOLUME: return "CMD_NEW_VOLUME"; - case CMD_NEW_TRANSFERFUNCTION: return "CMD_NEW_TRANSFERFUNCTION"; - case CMD_NEW_TEXTURE2D: return "CMD_NEW_TEXTURE2D"; - case CMD_ADD_GEOMETRY: return "CMD_ADD_GEOMETRY"; - case CMD_REMOVE_GEOMETRY: return "CMD_REMOVE_GEOMETRY"; - case CMD_ADD_VOLUME: return "CMD_ADD_VOLUME"; - case CMD_COMMIT: return "CMD_COMMIT"; - case CMD_LOAD_MODULE: return "CMD_LOAD_MODULE"; - case CMD_RELEASE: return "CMD_RELEASE"; - case CMD_REMOVE_VOLUME: return "CMD_REMOVE_VOLUME"; - case CMD_SET_MATERIAL: return "CMD_SET_MATERIAL"; - case CMD_SET_REGION: return "CMD_SET_REGION"; - case CMD_SET_REGION_DATA: return "CMD_SET_REGION_DATA"; - case CMD_SET_OBJECT: return "CMD_SET_OBJECT"; - case CMD_SET_STRING: return "CMD_SET_STRING"; - case CMD_SET_INT: return "CMD_SET_INT"; - case CMD_SET_FLOAT: return "CMD_SET_FLOAT"; - case CMD_SET_VEC2F: return "CMD_SET_VEC2F"; - case CMD_SET_VEC2I: return "CMD_SET_VEC2I"; - case CMD_SET_VEC3F: return "CMD_SET_VEC3F"; - case CMD_SET_VEC3I: return "CMD_SET_VEC3I"; - case CMD_SET_VEC4F: return "CMD_SET_VEC4F"; - case CMD_SET_PIXELOP: return "CMD_SET_PIXELOP"; - case CMD_REMOVE_PARAM: return "CMD_REMOVE_PARAM"; - case CMD_NEW_PIXELOP: return "CMD_NEW_PIXELOP"; - case CMD_API_MODE: return "CMD_API_MODE"; - case CMD_FINALIZE: return "CMD_FINALIZE"; - default: return "Unrecognized CommandTag"; - } -} -} - diff --git a/components/mpiCommon/command.h b/components/mpiCommon/command.h deleted file mode 100644 index 80f7dbeb9d..0000000000 --- a/components/mpiCommon/command.h +++ /dev/null @@ -1,93 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -/*! \file ospray/device/nwlayer.h \brief Defines the basic network layer abstraction */ - -#include "include/ospray/ospray.h" -#include "buffers.h" - -namespace ospray { - // namespace nwlayer { - - /*! the command ID is a numerical value that corresponds to a - given command type, and that lets the receiver figure out what - kind of command it is to execute. CommandIDs are only useful - inside \see Command structures. */ - typedef enum { - CMD_INVALID = -555, - CMD_NEW_RENDERER=0, - CMD_FRAMEBUFFER_CREATE, - CMD_RENDER_FRAME, - CMD_FRAMEBUFFER_CLEAR, - CMD_FRAMEBUFFER_MAP, - CMD_FRAMEBUFFER_UNMAP, - - CMD_NEW_DATA = 100, - CMD_NEW_MODEL, - CMD_NEW_GEOMETRY, - CMD_NEW_MATERIAL, - CMD_NEW_LIGHT, - CMD_NEW_CAMERA, - CMD_NEW_VOLUME, - CMD_NEW_TRANSFERFUNCTION, - CMD_NEW_TEXTURE2D, - - CMD_ADD_GEOMETRY=200, - CMD_REMOVE_GEOMETRY, - CMD_ADD_VOLUME, - CMD_COMMIT, - CMD_LOAD_MODULE, - CMD_RELEASE, - CMD_REMOVE_VOLUME, - - CMD_SET_MATERIAL, - CMD_SET_REGION, - CMD_SET_REGION_DATA, - CMD_SET_OBJECT, - CMD_SET_STRING, - CMD_SET_INT, - CMD_SET_FLOAT, - CMD_SET_VEC2F, - CMD_SET_VEC2I, - CMD_SET_VEC3F, - CMD_SET_VEC3I, - CMD_SET_VEC4F, - - CMD_REMOVE_PARAM, - - CMD_SET_PIXELOP, - CMD_NEW_PIXELOP, - - CMD_API_MODE,//TODO - - CMD_FINALIZE, - } CommandTag; - - const char* commandToString(CommandTag tag); - - namespace nwlayer { - struct InitCmd { - }; - struct NewFrameBufferCmd { - vec2ui size; - OSPFrameBufferFormat externalFormat; - int channelFlags; - }; - - } // ::ospray::nwlayer -} // ::ospray diff --git a/components/mpiMessageLayer/.#Comm.h b/components/mpiMessageLayer/.#Comm.h deleted file mode 120000 index 70b0af566b..0000000000 --- a/components/mpiMessageLayer/.#Comm.h +++ /dev/null @@ -1 +0,0 @@ -wald@luchser.2572:1479741419 \ No newline at end of file diff --git a/components/mpiMessageLayer/CMakeLists.txt b/components/mpiMessageLayer/CMakeLists.txt index a7316c3379..497c4c550d 100644 --- a/components/mpiMessageLayer/CMakeLists.txt +++ b/components/mpiMessageLayer/CMakeLists.txt @@ -14,11 +14,11 @@ ## limitations under the License. ## ## ======================================================================== ## -OSPRAY_BUILD_COMPONENT(mpiCommon) +SET(MAML_LIBRARY ospray_mpi_maml) OSPRAY_CONFIGURE_MPI() -SET(MAML_LIBRARY ospray_mpi_maml) +OSPRAY_BUILD_COMPONENT(mpiCommon) # ------------------------------------------------------------------ # The MAML library itself @@ -31,19 +31,29 @@ LINK COMPONENT mpi ) +SET(MAML_SDK_INSTALL_LOC + ../maml #NOTE: this is "next" to the SDK/ directory +) + +OSPRAY_INSTALL_SDK_HEADERS( + maml/maml.h + DESTINATION ${MAML_SDK_INSTALL_LOC} +) + # ------------------------------------------------------------------ # tests and examples # ------------------------------------------------------------------ -OSPRAY_CREATE_APPLICATION(mamlTest + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_LIST_DIR}) + +OSPRAY_CREATE_TEST(mamlTest apps/mamlTest.cpp LINK ${MAML_LIBRARY} -EXCLUDE_FROM_ALL ) -OSPRAY_CREATE_APPLICATION(mamlTestMessageSwarm +OSPRAY_CREATE_TEST(mamlTestMessageSwarm apps/testMessageSwarm.cpp LINK ${MAML_LIBRARY} -EXCLUDE_FROM_ALL ) diff --git a/components/mpiMessageLayer/apps/mamlTest.cpp b/components/mpiMessageLayer/apps/mamlTest.cpp index 1249df2683..4ddfef17a1 100644 --- a/components/mpiMessageLayer/apps/mamlTest.cpp +++ b/components/mpiMessageLayer/apps/mamlTest.cpp @@ -18,14 +18,11 @@ #include #include -namespace maml { -} // ::maml - struct MyHandler : public maml::MessageHandler { - MyHandler() : numReceived() {}; + MyHandler() = default; - virtual void incoming(const std::shared_ptr &message) + void incoming(const std::shared_ptr &message) override { ++numReceived; } @@ -36,8 +33,7 @@ struct MyHandler : public maml::MessageHandler extern "C" int main(int ac, char **av) { MPI_CALL(Init(&ac, &av)); - maml::init(ac,av); - srand48(times(NULL)); + srand48(times(nullptr)); int numRuns = 1000000; int rank = -1; @@ -75,7 +71,6 @@ extern "C" int main(int ac, char **av) usleep(10000); } - maml::flush(); maml::stop(); double t1 = ospcommon::getSysTime(); double bytes = numRanks * numMessages * payloadSize / (t1-t0); diff --git a/components/mpiMessageLayer/apps/testMessageSwarm.cpp b/components/mpiMessageLayer/apps/testMessageSwarm.cpp index af3e8e13a4..3ea8675617 100644 --- a/components/mpiMessageLayer/apps/testMessageSwarm.cpp +++ b/components/mpiMessageLayer/apps/testMessageSwarm.cpp @@ -28,51 +28,53 @@ std::atomic numReceived; struct BounceHandler : public maml::MessageHandler { - virtual void incoming(const std::shared_ptr &message) + void incoming(const std::shared_ptr &message) override { ++numReceived; - int nextRank = (int)(drand48()*numRanks); - maml::sendTo(MPI_COMM_WORLD,nextRank,message); + int nextRank = (int)(drand48() * numRanks); + maml::sendTo(MPI_COMM_WORLD, nextRank, message); } }; extern "C" int main(int ac, char **av) { MPI_CALL(Init(&ac, &av)); - maml::init(ac,av); - srand48(times(NULL)); + srand48(times(nullptr)); - int numRuns = 1000000; int rank = -1; - MPI_CALL(Comm_size(MPI_COMM_WORLD,&numRanks)); - MPI_CALL(Comm_rank(MPI_COMM_WORLD,&rank)); + MPI_CALL(Comm_size(MPI_COMM_WORLD, &numRanks)); + MPI_CALL(Comm_rank(MPI_COMM_WORLD, &rank)); int numMessages = 100; int payloadSize = 100000; BounceHandler handler; - maml::registerHandlerFor(MPI_COMM_WORLD,&handler); + maml::registerHandlerFor(MPI_COMM_WORLD, &handler); char *payload = (char*)malloc(payloadSize); - for (int i=0;i(payload,payloadSize)); + for (int mID = 0; mID < numMessages; mID++) { + int r = int(drand48() * numRanks); + maml::sendTo(MPI_COMM_WORLD, r, + std::make_shared(payload, payloadSize)); } while (1) { sleep(1); double t1 = ospcommon::getSysTime(); - std::string numBytes = ospcommon::prettyNumber((size_t)numReceived*payloadSize); + std::string numBytes = + ospcommon::prettyNumber((size_t)numReceived*payloadSize); double rate = (size_t)numReceived * payloadSize / (t1-t0); std::string rateString = ospcommon::prettyNumber(rate); + printf("rank %i: received %li messages (%sbytes) in %lf secs; that is %sB/s\n", - rank,(size_t)numReceived,numBytes.c_str(),t1-t0,rateString.c_str()); + rank, (size_t)numReceived, numBytes.c_str(), + t1-t0, rateString.c_str()); } /* this will never terminate ... */ diff --git a/components/mpiMessageLayer/maml/Context.cpp b/components/mpiMessageLayer/maml/Context.cpp index bafcb989d9..e9482e1881 100644 --- a/components/mpiMessageLayer/maml/Context.cpp +++ b/components/mpiMessageLayer/maml/Context.cpp @@ -15,28 +15,40 @@ // ************************************************************************** // #include "Context.h" +#include "ospcommon/malloc.h" #include +using ospcommon::make_unique; + namespace maml { /*! the singleton object that handles all the communication */ - Context *Context::singleton = NULL; + std::unique_ptr Context::singleton = make_unique(); Context::Context() - : canDoMPICalls(false), flushed(true) { - mpiThreadHandle = std::thread([this](){ mpiThread(); }); - inboxThreadHandle = std::thread([this](){ inboxThread(); }); + mpiSendRecvThread = std::thread([this](){ mpiSendAndRecieveThread(); }); + inboxProcThread = std::thread([this](){ processInboxThread(); }); + } + + Context::~Context() + { + threadsRunning = false; + start();// wake up mpiSendRecvThread so it can be shutdown + mpiSendRecvThread.join(); + inboxProcThread.join(); } - + /*! register a new incoing-message handler. if any message comes in on the given communicator we'll call this handler */ void Context::registerHandlerFor(MPI_Comm comm, MessageHandler *handler) { - std::lock_guard lock(handlersMutex); - if (handlers.find(comm) != handlers.end()) - std::cout << __PRETTY_FUNCTION__ - << ": Warning: handler for this MPI_Comm already installed" << std::endl; + if (handlers.find(comm) != handlers.end()) { + std::cerr << CODE_LOCATION + << ": Warning: handler for this MPI_Comm already installed" + << std::endl; + } + handlers[comm] = handler; /*! todo: to avoid race conditions we MAY want to check if there's @@ -49,217 +61,164 @@ namespace maml { stopped */ void Context::send(std::shared_ptr msg) { - assert(msg); - - std::lock_guard lock(outboxMutex); - std::lock_guard flushLock(flushMutex); - outbox.push_back(msg); - flushed = false; } - - void Context::inboxThread() + void Context::processInboxThread() { - while (1) { - std::vector > execList; - - // fetch the list of messages that need to be executed - { - std::unique_lock lock(inboxMutex); - if (inbox.empty()) - inboxCondition.wait(lock, [this]{return !inbox.empty();}); - execList = inbox; - // std::cout << "found " << inbox.size() << " messages in inbox!" << std::endl; - inbox.clear(); - } + while(threadsRunning) { + if (!inbox.empty()) { + auto incomingMessages = inbox.consume(); - for (int i=0;i lock(handlersMutex); - handler = handlers[execList[i]->comm]; - assert(handler); + for (auto &message : incomingMessages) { + auto *handler = handlers[message->comm]; + handler->incoming(message); } - - handler->incoming(execList[i]); - execList[i] = NULL; } } } - - void Context::mpiThread() + + void Context::mpiSendAndRecieveThread() { - /*! list of messages we are currently in the process of receiving - from the MPI layer - ie, we know those messages exist, but - they have NOT yet been fully received */ - std::vector > currentlyReceiving; - - /*! list of messages we are currently in the process of sending - via the MPI layer - ie, we have already triggered an MPI_Isend, - but have not verified that the message has been received yet */ - std::vector > currentlySending; - - while (1) { - bool anyDoneSend = false; - bool anyDoneRecv = false; - /*! the messages we have finihsed receiving this round */ - std::vector > inbox; - - { /* do all MPI commands under the mpi lock */ - std::unique_lock lock(canDoMPIMutex); - canDoMPICondition.wait(lock, [this]{return this->canDoMPICalls;}); - - /* check if there's any outgoing messages ... and trigger - their sending */ - { std::lock_guard lock(outboxMutex); - if (!outbox.empty()) { - for (int i=0;i msg = outbox[i]; - assert(msg); - assert(msg->data); - MPI_CALL(Isend(msg->data,msg->size,MPI_BYTE,msg->rank,msg->tag,msg->comm,&msg->request)); - currentlySending.push_back(msg); - // std::cout << "got new out message - starting to send and pushing to send queue ..." << std::endl; - } - outbox.clear(); - } - } - - /* check if there's any new incoming message(s) ... and trigger receiving for any new ones */ - { - std::lock_guard lock(handlersMutex); - /* check each message handler (we're not receiveing anything - unless we have a handler */ - for (auto it=handlers.begin(); it != handlers.end(); it++) { - MessageHandler *handler = it->second; - MPI_Comm comm = it->first; - - /* probe if there's something incoming on this handler's comm */ - int hasIncoming = 0; - MPI_Status status; - MPI_CALL(Iprobe(MPI_ANY_SOURCE,MPI_ANY_TAG,comm,&hasIncoming,&status)); - if (hasIncoming) { - - int size; - MPI_CALL(Get_count(&status,MPI_BYTE,&size)); - - std::shared_ptr msg = std::shared_ptr(new Message(size)); - msg->rank = status.MPI_SOURCE; - msg->tag = status.MPI_TAG; - msg->comm = comm; - MPI_CALL(Irecv(msg->data,size,MPI_BYTE,msg->rank,msg->tag,msg->comm,&msg->request)); - currentlyReceiving.push_back(msg); - } - } - } - - /* wait for ANY of the currently incoming our outgoing - messages to be done sending/receiving */ - if (!(currentlySending.empty() && currentlyReceiving.empty())) { - // create list of all currently active requests: - size_t numSendRequests = currentlySending.size(); - size_t numRecvRequests = currentlyReceiving.size(); - size_t numRequests = numSendRequests+numRecvRequests; - MPI_Request activeRequest[numRequests]; - // - add all ongoing send requests - for (int i=0;irequest; - // - add all ongoing recv requests - for (int i=0;irequest; - - // wait for at least one of those to complete - int done[numRequests]; - int numDone = 0; - // printf("waitsome(%i) send=%i recv=%i %i\n",rank,numSendRequests,numRecvRequests,numRequests); - if (numRecvRequests == 0) { - MPI_CALL(Testsome(numRequests,activeRequest,&numDone,done,MPI_STATUSES_IGNORE)); - } else { - MPI_CALL(Waitsome(numRequests,activeRequest,&numDone,done,MPI_STATUSES_IGNORE)); - } - for (int i=0;i= numSendRequests) { - // this was a recv request - std::shared_ptr msg = currentlyReceiving[requestID-numSendRequests]; - currentlyReceiving[requestID-numSendRequests] = NULL; - inbox.push_back(msg); - anyDoneRecv = true; - } else { - // this was a send request that just finished - currentlySending[requestID] = NULL; - anyDoneSend = true; - } - } - } - } /* end of scope ofr MPI lock - we can now operate on the - messages we've received (and update out local state), but - from here on out we're no longer do any MPI calls (until - the next round) !!!! */ - - /* purge the send queue if any sends completed */ - if (anyDoneSend) { - std::vector > stillNotDone; - for (auto it=currentlySending.begin();it!=currentlySending.end();it++) - if (*it) stillNotDone.push_back(*it); - currentlySending = stillNotDone; - } - static long lastSendSize = -1; - if (lastSendSize != currentlySending.size()) { - lastSendSize = currentlySending.size(); + while(true) { + std::unique_lock lock(canDoMPIMutex); + canDoMPICondition.wait(lock, [&]{ return canDoMPICalls; }); + + if (!threadsRunning) + return; + + sendAndRecieveThreadActive = true; + + sendMessagesFromOutbox(); + pollForAndRecieveMessages(); + + waitOnSomeSendRequests(); + waitOnSomeRecvRequests(); + + sendAndRecieveThreadActive = false; + } + } + + void Context::sendMessagesFromOutbox() + { + if (!outbox.empty()) { + auto outgoingMessages = outbox.consume(); + + for (auto &msg : outgoingMessages) { + MPI_Request request; + MPI_CALL(Isend(msg->data, msg->size, MPI_BYTE, msg->rank, + msg->tag, msg->comm, &request)); + pendingSends.push_back(request); + sendCache.push_back(std::move(msg)); } - - /* purge the recv queue if any sends completed */ - if (anyDoneRecv) { - std::vector > stillNotDone; - for (auto it=currentlyReceiving.begin();it!=currentlyReceiving.end();it++) - if (*it) stillNotDone.push_back(*it); - currentlyReceiving = stillNotDone; + } + } + + void Context::pollForAndRecieveMessages() + { + for (auto &it : handlers) { + MPI_Comm comm = it.first; + + /* probe if there's something incoming on this handler's comm */ + int hasIncoming = 0; + MPI_Status status; + MPI_CALL(Iprobe(MPI_ANY_SOURCE, MPI_ANY_TAG, + comm, &hasIncoming, &status)); + + if (hasIncoming) { + int size; + MPI_CALL(Get_count(&status, MPI_BYTE, &size)); + + auto msg = std::make_shared(size); + msg->rank = status.MPI_SOURCE; + msg->tag = status.MPI_TAG; + msg->comm = comm; + + MPI_Request request; + MPI_CALL(Irecv(msg->data, size, MPI_BYTE, msg->rank, + msg->tag, msg->comm, &request)); + pendingRecvs.push_back(request); + recvCache.push_back(std::move(msg)); } + } + } + + void Context::waitOnSomeSendRequests() + { + if (!pendingSends.empty()) { + int numDone = 0; + int *done = STACK_BUFFER(int, pendingSends.size()); + + MPI_CALL(Testsome(pendingSends.size(), pendingSends.data(), &numDone, + done, MPI_STATUSES_IGNORE)); - if (anyDoneRecv) { - std::lock_guard lock(inboxMutex); - for (int i=0;iinbox.push_back(inbox[i]); - inboxCondition.notify_one(); + for (int i = 0; i < numDone; ++i) { + int pendingSendCompletedIndex = done[i]; + pendingSends[pendingSendCompletedIndex] = MPI_REQUEST_NULL; + sendCache[pendingSendCompletedIndex].reset(); } - if (currentlyReceiving.empty() && currentlySending.empty()) { - std::lock_guard outboxLock(outboxMutex); - if (outbox.empty()) { - std::lock_guard flushLock(flushMutex); - flushed = true; - flushCondition.notify_one(); - } + sendCache.erase(std::remove(sendCache.begin(), + sendCache.end(), + nullptr), sendCache.end()); + + pendingSends.erase(std::remove(pendingSends.begin(), + pendingSends.end(), + MPI_REQUEST_NULL), pendingSends.end()); + } + } + + void Context::waitOnSomeRecvRequests() + { + if (!pendingRecvs.empty()) { + int numDone = 0; + int *done = STACK_BUFFER(int, pendingRecvs.size()); + + MPI_CALL(Waitsome(pendingRecvs.size(), pendingRecvs.data(), &numDone, + done, MPI_STATUSES_IGNORE)); + + for (int i = 0; i < numDone; ++i) { + int pendingRecvCompletedIndex = done[i]; + pendingRecvs[pendingRecvCompletedIndex] = MPI_REQUEST_NULL; + inbox.push_back(std::move(recvCache[pendingRecvCompletedIndex])); } - + + recvCache.erase(std::remove(recvCache.begin(), + recvCache.end(), + nullptr), recvCache.end()); + + pendingRecvs.erase(std::remove(pendingRecvs.begin(), + pendingRecvs.end(), + MPI_REQUEST_NULL), pendingRecvs.end()); } } - /*! make sure all outgoing messages get sent... */ - void Context::flush() + void Context::flushRemainingMessages() { - std::unique_lock lock(flushMutex); - - // if (!flushed) - // { std::lock_guard outboxLock(outboxMutex); - // std::cout << "#maml: flushing (" << outbox.size() << " messages in outbox)!" << std::endl; - // } - flushCondition.wait(lock, [this]{return flushed;}); - // std::cout << "#maml: flushed..." << std::endl; + sendMessagesFromOutbox(); + + while (!pendingRecvs.empty()) + waitOnSomeRecvRequests(); + + while (!pendingSends.empty()) + waitOnSomeSendRequests(); } - + /*! start the service; from this point on maml is free to use MPI calls to send/receive messages; if your MPI library is not thread safe the app should _not_ do any MPI calls until 'stop()' has been called */ void Context::start() { - std::lock_guard m1(canDoMPIMutex); canDoMPICalls = true; canDoMPICondition.notify_one(); } - + + bool Context::isRunning() const + { + return canDoMPICalls || sendAndRecieveThreadActive; + } + /*! stops the maml layer; maml will no longer perform any MPI calls; if the mpi layer is not thread safe the app is then free to use MPI calls of its own, but it should not expect that this node @@ -267,8 +226,11 @@ namespace maml { if they are already in flight */ void Context::stop() { - std::lock_guard m1(canDoMPIMutex); canDoMPICalls = false; + std::unique_lock lock(canDoMPIMutex); + canDoMPICondition.wait(lock, [&]{ return !sendAndRecieveThreadActive; }); + + flushRemainingMessages(); } } // ::maml diff --git a/components/mpiMessageLayer/maml/Context.h b/components/mpiMessageLayer/maml/Context.h index 1a6c9b84d7..e5a936e116 100644 --- a/components/mpiMessageLayer/maml/Context.h +++ b/components/mpiMessageLayer/maml/Context.h @@ -17,6 +17,9 @@ #pragma once #include "maml.h" +//ospcommon +#include "ospcommon/containers/TransactionalBuffer.h" +//stl #include #include #include @@ -26,10 +29,12 @@ namespace maml { /*! the singleton object that handles all the communication */ - struct Context { + struct OSPRAY_MAML_INTERFACE Context + { Context(); - - static Context *singleton; + ~Context(); + + static std::unique_ptr singleton; /*! register a new incoing-message handler. if any message comes in on the given communicator we'll call this handler */ @@ -40,7 +45,9 @@ namespace maml { thread safe the app should _not_ do any MPI calls until 'stop()' has been called */ void start(); - + + bool isRunning() const; + /*! stops the maml layer; maml will no longer perform any MPI calls; if the mpi layer is not thread safe the app is then free to use MPI calls of its own, but it should not expect that this node @@ -53,10 +60,14 @@ namespace maml { stopped */ void send(std::shared_ptr msg); + private: + + // Helper functions // + /*! the thread (function) that executes all MPI commands to - send/receive messages via MPI. + send/receive messages via MPI. - Some notes: + Some notes: - this thread does MPI calls (only!) between calls of start() and end(). unless you cal start(), nothing will ever get sent @@ -72,39 +83,46 @@ namespace maml { triggered. it's another thread's job to execute those messages */ - void mpiThread(); + void mpiSendAndRecieveThread(); /*! the thread that executes messages that the receiveer thread put into the inbox */ - void inboxThread(); + void processInboxThread(); + + void sendMessagesFromOutbox(); + void pollForAndRecieveMessages(); + + void waitOnSomeSendRequests(); + void waitOnSomeRecvRequests(); + + void flushRemainingMessages(); + + // Data members // + + bool threadsRunning {true}; + + ospcommon::TransactionalBuffer> inbox; + ospcommon::TransactionalBuffer> outbox; + + // NOTE(jda) - sendCache/pendingSends MUST correspond with each other by + // their index in their respective vectors... + std::vector> sendCache; + std::vector pendingSends; + + // NOTE(jda) - recvCache/pendingRecvs MUST correspond with each other by + // their index in their respective vectors... + std::vector> recvCache; + std::vector pendingRecvs; + + std::map handlers; - /*! make sure all outgoing messages get sent... */ - void flush(); + std::thread mpiSendRecvThread; + std::thread inboxProcThread; - bool canDoMPICalls; + bool canDoMPICalls {false}; + bool sendAndRecieveThreadActive {false}; std::mutex canDoMPIMutex; std::condition_variable canDoMPICondition; - - std::thread mpiThreadHandle; - std::thread inboxThreadHandle; - - std::mutex handlersMutex; - std::map handlers; - - /*! new messsages that still need to get sent */ - std::mutex outboxMutex; - std::vector > outbox; - - /*! new messsages that have been received but not yet executed */ - std::mutex inboxMutex; - std::condition_variable inboxCondition; - std::vector > inbox; - - /*! used to execute a flush: this condition gets triggered when - all messages in the outbox have been (fully) sent */ - bool flushed; - std::mutex flushMutex; - std::condition_variable flushCondition; }; } // ::maml diff --git a/components/mpiMessageLayer/maml/maml.cpp b/components/mpiMessageLayer/maml/maml.cpp index 610a6c3190..4d12855d19 100644 --- a/components/mpiMessageLayer/maml/maml.cpp +++ b/components/mpiMessageLayer/maml/maml.cpp @@ -19,94 +19,12 @@ namespace maml { - std::mutex messageMutex; + // maml API definitions ///////////////////////////////////////////////////// - /*! create a new message with given amount of bytes in storage */ - Message::Message(size_t size) - : data(NULL), size(size), - comm(MPI_COMM_NULL), rank(-1), tag(0) - { - //new unsigned char [size] - assert(size > 0); - - // { std::lock_guard lock(messageMutex); - // static size_t sumAlloc = 0; - // sumAlloc += size; - // PRINT(size); - // PRINT(sumAlloc); - data = (unsigned char *)malloc(size); - // PRINT((int*)data); - // } - - assert(data); - } - - /*! create a new message with given amount of storage, and copy - memory from the given address to it */ - Message::Message(const void *copyMem, size_t size) - : data(NULL), size(size), - comm(MPI_COMM_NULL), rank(-1), tag(0) - { - assert(copyMem); - - assert(size > 0); - data = (unsigned char *) malloc(size); //new unsigned char [size] - assert(data); - - memcpy(data,copyMem,size); - } - - /*! create a new message (addressed to given comm:rank) with given - amount of storage, and copy memory from the given address to - it */ - Message::Message(MPI_Comm comm, int rank, - const void *copyMem, size_t size) - : data(NULL), size(size), - comm(comm), rank(rank), tag(0) - { - assert(copyMem); - assert( size > 0); - - // data = new unsigned char [size]; - data = (unsigned char *)malloc(size); - assert(data); - memcpy(data,copyMem,size); - } - - /*! destruct message and free allocated memory */ - Message::~Message() { - if (data) { - // delete[] data; - free(data); //printf("currently NOT deleting the messages!\n"); - } - } - - - /*! initialize the maml layer. must be called before doing any call - below, but should only be called once. note this assuems that - MPI is already initialized; it will use the existing MPI - layer */ - void init(int &ac, char **&av) - { - int initialized; - MPI_CALL(Initialized(&initialized)); - if (!initialized) - MAML_THROW("MPI not initialized"); - - if (Context::singleton) - MAML_THROW("MAML layer _already_ initialized"); - - Context::singleton = new Context; - assert(Context::singleton); - } - /*! register a new incoing-message handler. if any message comes in on the given communicator we'll call this handler */ void registerHandlerFor(MPI_Comm comm, MessageHandler *handler) { - if (!Context::singleton) - MAML_THROW("MAML layer not initialized?!"); - Context::singleton->registerHandlerFor(comm,handler); } @@ -116,12 +34,14 @@ namespace maml { has been called */ void start() { - if (!Context::singleton) - MAML_THROW("MAML layer not initialized?!"); - Context::singleton->start(); } + bool isRunning() + { + return Context::singleton->isRunning(); + } + /*! stops the maml layer; maml will no longer perform any MPI calls; if the mpi layer is not thread safe the app is then free to use MPI calls of its own, but it should not expect that this node @@ -129,71 +49,20 @@ namespace maml { if they are already in flight */ void stop() { - if (!Context::singleton) - MAML_THROW("MAML layer not initialized?!"); - Context::singleton->stop(); } - /*! send given messsage to given comm:rank. Once this function has - called maml has full ownership of this message, and the user may - no longer access it (becuase maml may delete it at any time) */ - void send(std::shared_ptr msg) - { - assert(msg->comm != MPI_COMM_NULL); - assert(msg->rank >= 0); - if (!Context::singleton) - MAML_THROW("MAML layer not initialized?!"); - Context::singleton->send(std::shared_ptr(msg)); - } - - // void send(Message *msg) - // { - // assert(msg); - // send(std::shared_ptr(msg)); - // } - - - /*! send given messsage to given comm:rank. Once this function has - called maml has full ownership of this message, and the user may - no longer access it (becuase maml may delete it at any time) */ - // void sendTo(MPI_Comm comm, int rank, Message *msg) - // { - // assert(msg); - // msg->rank = rank; - // msg->comm = comm; - // msg->tag = 0; - // send(msg); - // } - /*! send given messsage to given comm:rank. Once this function has called maml has full ownership of this message, and the user may no longer access it (becuase maml may delete it at any time) */ void sendTo(MPI_Comm comm, int rank, std::shared_ptr msg) { - assert(msg); - assert(rank >= 0); + if (!(rank >= 0 && msg.get())) + OSPRAY_THROW("Incorrect argument values given to maml::sendTo(...)"); + msg->rank = rank; msg->comm = comm; - msg->tag = 0; - send(msg); - } - - /*! make sure all outgoing messages get sent... */ - void flush() - { - if (!Context::singleton) - MAML_THROW("MAML layer not initialized?!"); - Context::singleton->flush(); - } - - /*! close down and clean exit. NOT YET IMPLEMENTED. */ - void finalize() - { - std::cout << "#maml: warning: maml::finalize() not yet implemented" << std::endl; + Context::singleton->send(msg); } - - } // ::maml - diff --git a/components/mpiMessageLayer/maml/maml.h b/components/mpiMessageLayer/maml/maml.h index bc5c2e1a9b..f165efb0d1 100644 --- a/components/mpiMessageLayer/maml/maml.h +++ b/components/mpiMessageLayer/maml/maml.h @@ -16,126 +16,51 @@ #pragma once -#include -#include -#include - -#include "ospcommon/common.h" - - // IMPI on Windows defines MPI_CALL already, erroneously -#ifdef MPI_CALL -# undef MPI_CALL +#include "mpiCommon/MPICommon.h" + +#ifdef _WIN32 +# ifdef ospray_mpi_maml_EXPORTS +# define OSPRAY_MAML_INTERFACE __declspec(dllexport) +# else +# define OSPRAY_MAML_INTERFACE __declspec(dllimport) +# endif +#else +# define OSPRAY_MAML_INTERFACE #endif - /*! helper macro that checks the return value of all MPI_xxx(...) - calls via MPI_CALL(xxx(...)). */ -#define MPI_CALL(a) { int rc = MPI_##a; if (rc != MPI_SUCCESS) throw std::runtime_error("MPI call returned error"); } - - -#define MAML_THROW(a) throw std::runtime_error("in "+std::string(__PRETTY_FUNCTION__)+" : "+std::string(a)) -#define MAML_NOT_IMPLEMENTED HOTT_THROW("method not implemented") - namespace maml { - struct Context; - - /*! object that handles a message. a message primarily consists of a - pointer to data; the message itself "owns" this pointer, and - will delete it once the message itself dies. the message itself - is reference counted using the std::shared_ptr functionality. */ - struct Message { - - /*! create a new message with given amount of bytes in storage */ - Message(size_t size); - - /*! create a new message with given amount of storage, and copy - memory from the given address to it */ - Message(const void *copyMem, size_t size); - - /*! create a new message (addressed to given comm:rank) with given - amount of storage, and copy memory from the given address to - it */ - Message(MPI_Comm comm, int rank, - const void *copyMem, size_t size); - - /*! destruct message and free allocated memory */ - virtual ~Message(); - - /*! @{ sender/receiver of this message */ - MPI_Comm comm; - int rank; - int tag; - /*! @} */ - /*! @{ actual payload of this message */ - unsigned char *data; - size_t size; - /*! @} */ - - private: - /*! the request for sending/receiving this message */ - MPI_Request request; - - friend class Context; - }; - - /*! a message whose payload is owned by the user, and which we do - NOT delete upon termination */ - struct UserMemMessage : public Message { - UserMemMessage(const void *nonCopyMem, size_t size) - : Message(nonCopyMem,size) - {}; - virtual ~UserMemMessage() - { /* set data to NULL, that keeps the parent from deleting it */ data = NULL; } - }; - - /*! initialize the maml layer. must be called before doing any call - below, but should only be called once. note this assuems that - MPI is already initialized; it will use the existing MPI - layer */ - void init(int &ac, char **&av); + using Message = mpicommon::Message; /*! abstraction for an object that can receive messages. handlers get associated with MPI_Comm's, and get called automatically every time such a message comes in. maml receives the message, then passed it to the handler, from which point it is owned by - and the respsonsibility of - the handler */ - struct MessageHandler { + struct MessageHandler + { virtual void incoming(const std::shared_ptr &message) = 0; }; /*! register a new incoing-message handler. if any message comes in on the given communicator we'll call this handler */ - void registerHandlerFor(MPI_Comm comm, MessageHandler *handler); + OSPRAY_MAML_INTERFACE void registerHandlerFor(MPI_Comm comm, + MessageHandler *handler); /*! start the service; from this point on maml is free to use MPI calls to send/receive messages; if your MPI library is not thread safe the app should _not_ do any MPI calls until 'stop()' has been called */ - void start(); + OSPRAY_MAML_INTERFACE void start(); + + OSPRAY_MAML_INTERFACE bool isRunning(); /*! stops the maml layer; maml will no longer perform any MPI calls; if the mpi layer is not thread safe the app is then free to use MPI calls of its own, but it should not expect that this node receives any more messages (until the next 'start()' call) even if they are already in flight */ - void stop(); - - /*! close down and clean exit. NOT YET IMPLEMENTED. */ - void finalize(); - - /*! schedule the given message to be send to the comm:rank indicated - in this message. comm and rank have to be a valid address. Once - this function has called maml has full ownership of this - message, and the user may no longer access it (becuase maml may - delete it at any time). note this message will not be sent - immediately if the mpi sending is stopped; it will, however, be - placed in the outbox to be sent at the next possible - opportunity. */ - // void send(Message *msg); - void send(std::shared_ptr msg); - - /*! make sure all outgoing messages get sent... */ - void flush(); + OSPRAY_MAML_INTERFACE void stop(); /*! schedule the given message to be send to the given comm:rank. comm and rank have to be a valid address. Once this @@ -152,8 +77,8 @@ namespace maml { sending out some new message(s) that simply haven't even STARTED arriving on this node, yet!!! */ - // void sendTo(MPI_Comm comm, int rank, Message *msg); - - void sendTo(MPI_Comm comm, int rank, std::shared_ptr msg); + OSPRAY_MAML_INTERFACE void sendTo(MPI_Comm comm, + int rank, + std::shared_ptr msg); } // ::maml diff --git a/components/ospcommon/CMakeLists.txt b/components/ospcommon/CMakeLists.txt index 5c95f0bb18..99d9bc8366 100644 --- a/components/ospcommon/CMakeLists.txt +++ b/components/ospcommon/CMakeLists.txt @@ -17,7 +17,6 @@ SET(CMAKE_THREAD_PREFER_PTHREAD TRUE) SET(THREADS_PREFER_PTHREAD_FLAG TRUE) - IF (TARGET ospray_common) # this target is already built! ELSE() @@ -28,6 +27,8 @@ ELSE() LIST(APPEND LINK_LIBS ws2_32) ENDIF() + ## Create ospcommon library ## + OSPRAY_CREATE_LIBRARY(ospray_common common.cpp FileName.cpp @@ -38,15 +39,27 @@ ELSE() vec.cpp array3D/Array3D.cpp + containers/TransactionalBuffer.h + + networking/BufferedDataStreaming.cpp + networking/DataStreaming.h + networking/Fabric.h + networking/Socket.cpp + tasking/parallel_for.inl tasking/parallel_for.h tasking/async.h tasking/schedule.inl tasking/schedule.h tasking/tasking_system_handle.cpp - tasking/TaskingTypeTraits.h tasking/TaskSys.cpp - + + utility/Any.h + utility/CodeTimer.h + utility/OnScopeExit.h + utility/PseudoURL.cpp + utility/TransactionalValue.h + AffineSpace.h box.h constants.h @@ -56,12 +69,19 @@ ELSE() platform.h Quaternion.h RefCount.h + TypeTraits.h vec.h LINK ${LINK_LIBS} COMPONENT lib ) + ## Install headers ## + + SET(OSPCOMMON_SDK_INSTALL_LOC + ../ospcommon #NOTE: this is "next" to the SDK/ directory + ) + OSPRAY_INSTALL_SDK_HEADERS( AffineSpace.h box.h @@ -78,10 +98,26 @@ ELSE() RefCount.h sysinfo.h thread.h + TypeTraits.h vec.h - DESTINATION ../ospcommon #NOTE: this is "next" to the SDK/ directory - ) + DESTINATION ${OSPCOMMON_SDK_INSTALL_LOC} + ) + + OSPRAY_INSTALL_SDK_HEADERS( + containers/TransactionalBuffer.h + + DESTINATION ${OSPCOMMON_SDK_INSTALL_LOC}/containers + ) + + OSPRAY_INSTALL_SDK_HEADERS( + networking/BufferedDataStreaming.h + networking/DataStreaming.h + networking/Fabric.h + networking/Socket.h + + DESTINATION ${OSPCOMMON_SDK_INSTALL_LOC}/networking + ) OSPRAY_INSTALL_SDK_HEADERS( tasking/parallel_for.h @@ -90,6 +126,45 @@ ELSE() tasking/TaskSys.h tasking/TaskingTypeTraits.h - DESTINATION ../ospcommon/tasking #NOTE: this is "next" to the SDK/ directory - ) + DESTINATION ${OSPCOMMON_SDK_INSTALL_LOC}/tasking + ) + + OSPRAY_INSTALL_SDK_HEADERS( + utility/Any.h + utility/CodeTimer.h + utility/OnScopeExit.h + utility/PseudoURL.h + utility/TransactionalValue.h + + DESTINATION ${OSPCOMMON_SDK_INSTALL_LOC}/utility + ) + + ## Test apps ## + + # Any + + OSPRAY_CREATE_TEST(test_Any + utility/Any.h + utility/test_Any.cpp + ) + + ADD_TEST(NAME Any COMMAND test_Any) + + # OnScopeExit + + OSPRAY_CREATE_TEST(test_OnScopeExit + utility/OnScopeExit.h + utility/test_OnScopeExit.cpp + ) + + ADD_TEST(NAME OnScopeExit COMMAND test_OnScopeExit) + + # TransactionalBuffer + + OSPRAY_CREATE_TEST(test_TransactionalBuffer + containers/TransactionalBuffer.h + containers/test_TransactionalBuffer.cpp + ) + + ADD_TEST(NAME TransactionalBuffer COMMAND test_TransactionalBuffer) ENDIF() diff --git a/components/ospcommon/ProducerConsumerQueue.h b/components/ospcommon/ProducerConsumerQueue.h deleted file mode 100644 index 753b5b9ef6..0000000000 --- a/components/ospcommon/ProducerConsumerQueue.h +++ /dev/null @@ -1,172 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#pragma once - -/*! \file ospray/common/ProducerConsumerQueue.h \brief Abstraction for - a mutex-protected producer-consumer queue that different threads - can write into / pull from */ - -#include "ospcommon/common.h" -// stl -#include -#include -#include - -namespace ospray { - - /*! \brief A (thread-safe) producer-consumer queue. - - \detailed The producer-consumer queue is intended to facilite a - usage model where some set of "producer" threads "put()"s items - into the queue, that a other set of thread(s) (i.e., the - "consudmer" thread(s)) can then "get()" items from. All accesses - are automatically thread-safe; a 'get()' on an empty queue will - automatically put the consumer that attempted that get to sleep on - a thread condition. */ - template - struct ProducerConsumerQueue { - /*! put a new element into this queue */ - void put(T t); - - /*! put multiple new elements into this queue */ - void putSome(T *t, size_t numTs); - - /*! get element that got written into the queue */ - T get(); - - /*! get elements in the queue into the given vector, and clear the queue. - if the queue is currently empty, wait until at least one element is there */ - void getAll(std::vector &all); - - /*! get elements in the queue into the given vector, and clear the queue. - if the queue is currently empty, waiting until at least one element is there - */ - size_t getSome(T *some, size_t maxSize); - /*! get elements in the queue into the given vector, and clear the queue. - if the queue is currently empty, wait until at least one element is there - or until the timeOut has elapsed, in which case the function may - return 0. */ - size_t getSomeFor(T *some, size_t maxSize, std::chrono::milliseconds timeOut); - - private: - /*! the actual queue that holds the data */ - std::deque content; - /*! mutex to allow thread-safe access */ - std::mutex mutex; - /*! condition that is triggered when queue is not empty. 'get' - requests can wait on this */ - std::condition_variable notEmptyCond; - }; - - - // ======================================================= - // IMPLEMENTATION SECTION - // ======================================================= - - - /*! get element that got written into the queue */ - template - T ProducerConsumerQueue::get() - { - std::unique_lock lock(mutex, std::defer_lock); - notEmptyCond.wait(lock, [&]{return !content.empty();}); - - T t = content.front(); - content.pop_front(); - return t; - } - - /*! put a new element into this queue */ - template - void ProducerConsumerQueue::put(T t) - { - bool wasEmpty = false; - { - std::lock_guard lock(mutex); - wasEmpty = content.empty(); - content.push_back(t); - } - if (wasEmpty) - notEmptyCond.notify_all(); - } - - /*! put multiple new elements into this queue */ - template - void ProducerConsumerQueue::putSome(T *t, size_t numTs) - { - bool wasEmpty = false; - { - std::lock_guard lock(mutex); - wasEmpty = content.empty(); - for (size_t i = 0; i < numTs; i++) - content.push_back(t[i]); - } - if (wasEmpty) - notEmptyCond.notify_all(); - } - - - /*! get element that got written into the queue */ - template - void ProducerConsumerQueue::getAll(std::vector &all) - { - std::unique_lock lock(mutex); - notEmptyCond.wait(lock, [&]{return !content.empty();}); - - size_t size = content.size(); - all.resize(size); - int i = 0; - for (auto it=content.begin(); it != content.end(); it++) - all[i++] = *it; - content.clear(); - } - - /*! get element that got written into the queue */ - template - size_t ProducerConsumerQueue::getSome(T *some, size_t maxSize) - { - std::unique_lock lock(mutex); - notEmptyCond.wait(lock, [&]{return !content.empty();}); - - size_t num = 0; - while (num < maxSize && !content.empty()) { - some[num++] = content.front(); - content.pop_front(); - } - return num; - } - - template - size_t ProducerConsumerQueue::getSomeFor(T *some, size_t maxSize, std::chrono::milliseconds timeOut) - { - using namespace std::chrono; - std::unique_lock lock(mutex); - if (!notEmptyCond.wait_for(lock, timeOut, [&]{return !content.empty();})) { - return 0; - } - - size_t num = 0; - while (num < maxSize && !content.empty()) { - some[num++] = content.front(); - content.pop_front(); - } - return num; - } - -} // ::ospray - - diff --git a/components/ospcommon/TypeTraits.h b/components/ospcommon/TypeTraits.h new file mode 100644 index 0000000000..9ed0d9c125 --- /dev/null +++ b/components/ospcommon/TypeTraits.h @@ -0,0 +1,107 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +namespace ospcommon { + namespace traits { + + template + std::true_type operator==(const T&, const Arg&); + + // type 'T' having '==' operator ////////////////////////////////////////// + + template + struct HasOperatorEqualsT + { + enum + { + value = !std::is_same::value + }; + }; + + template + using HasOperatorEquals = + typename std::enable_if::value, TYPE>::type; + + template + using NoOperatorEquals = + typename std::enable_if::value, TYPE>::type; + + // type 'T' implementing T::operator() //////////////////////////////////// + + //NOTE(jda) - This checks at compile time if T implements the method + // 'void T::operator()'. + template + struct has_operator_method + { + using TASK_T = typename std::decay::type; + + template class checker; + + template + static std::true_type test(checker *); + + template + static std::false_type test(...); + + using type = decltype(test(nullptr)); + static const bool value = std::is_same::value; + }; + + // type 'T' implementing T::operator(P) with P being integral ///////////// + +#ifdef _WIN32 + template + using has_operator_method_with_integral_param = has_operator_method; +#else + //NOTE(jda) - This checks at compile time if T implements the method + // 'void T::operator(P taskIndex)', where P is an integral type + // (must be short, int, uint, or size_t) at compile-time. To be + // used inside a static_assert(). + template + struct has_operator_method_with_integral_param + { + using TASK_T = typename std::decay::type; + + template + using t_param = void(TASK_T::*)(P) const; + using byte_t = unsigned char; + using operator_t = decltype(&TASK_T::operator()); + + using param_is_byte = std::is_same , operator_t>; + using param_is_short = std::is_same , operator_t>; + using param_is_int = std::is_same , operator_t>; + using param_is_unsigned = std::is_same, operator_t>; + using param_is_long = std::is_same , operator_t>; + using param_is_size_t = std::is_same , operator_t>; + + static const bool value = has_operator_method::value && + (param_is_byte::value || + param_is_short::value || + param_is_int::value || + param_is_unsigned::value || + param_is_long::value || + param_is_size_t::value); + }; +#endif + + } // ::ospray::sg::traits +} // ::ospray diff --git a/components/ospcommon/array3D/Array3D.cpp b/components/ospcommon/array3D/Array3D.cpp index d647a8785c..b593696ef8 100644 --- a/components/ospcommon/array3D/Array3D.cpp +++ b/components/ospcommon/array3D/Array3D.cpp @@ -54,7 +54,8 @@ namespace ospcommon { } template - std::shared_ptr> loadRAW(const std::string &fileName, const vec3i &dims) + std::shared_ptr> loadRAW(const std::string &fileName, + const vec3i &dims) { std::shared_ptr> volume = std::make_shared>(dims); FILE *file = fopen(fileName.c_str(),"rb"); @@ -70,7 +71,8 @@ namespace ospcommon { } template - std::shared_ptr> mmapRAW(const std::string &fileName, const vec3i &dims) + std::shared_ptr> mmapRAW(const std::string &fileName, + const vec3i &dims) { #ifdef _WIN32 throw std::runtime_error("mmap not supported under windows"); @@ -83,7 +85,8 @@ namespace ospcommon { size_t fileSize = size_t(dims.x)*size_t(dims.y)*size_t(dims.z)*sizeof(T); std::cout << "mapping file " << fileName - << " exptd size " << prettyNumber(fileSize) << " actual size " << prettyNumber(actualFileSize) << std::endl; + << " exptd size " << prettyNumber(fileSize) << " actual size " + << prettyNumber(actualFileSize) << std::endl; if (actualFileSize < fileSize) throw std::runtime_error("incomplete file!"); if (actualFileSize > fileSize) @@ -139,7 +142,7 @@ namespace ospcommon { template Array3DRepeater::Array3DRepeater(const std::shared_ptr> &actual, const vec3i &repeatedSize) : - actual(actual), repeatedSize(repeatedSize) + repeatedSize(repeatedSize), actual(actual) {} template diff --git a/components/ospcommon/array3D/Array3D.h b/components/ospcommon/array3D/Array3D.h index f56a5610b2..815c78058b 100644 --- a/components/ospcommon/array3D/Array3D.h +++ b/components/ospcommon/array3D/Array3D.h @@ -29,8 +29,8 @@ namespace ospcommon { /*! ABSTRACTION for a 3D array of data */ template - struct Array3D { - + struct Array3D + { /*! return size (ie, "dimensions") of volume */ virtual vec3i size() const = 0; @@ -50,13 +50,13 @@ namespace ospcommon { /*! returns number of elements (as 64-bit int) across all dimensions */ virtual size_t numElements() const = 0; }; - /*! implementation for an actual array3d that stores a 3D array of values */ template - struct ActualArray3D : public Array3D { + struct ActualArray3D : public Array3D + { - ActualArray3D(const vec3i &dims, void *externalMem=nullptr); + ActualArray3D(const vec3i &dims, void *externalMem = nullptr); virtual ~ActualArray3D() { if (valuesAreMine) delete[] value; } /*! return size (ie, "dimensions") of volume */ @@ -87,48 +87,17 @@ namespace ospcommon { bool valuesAreMine; }; - - // /*! implementation for an actual array3d that stores a 3D array of values */ - // template - // struct DummyArray3D : public Array3D { - - // DummyArray3D(const vec3i &dims) : dims(dims) {}; - - // /*! return size (ie, "dimensions") of volume */ - // virtual vec3i size() const override { return dims; }; - - // /*! get cell value at location - - // \warning 'where' MUST be a valid cell location */ - // virtual value_t get(const vec3i &where) const override { - // return reduce_mul(where) / ((float)reduce_mul(dims-vec3i(1))); - // } - - // /*! set cell value at location to given value - - // \warning 'where' MUST be a valid cell location */ - // virtual void set(const vec3i &where, const value_t &t) { throw std::runtime_error("cannot 'set' in a dummyarray3d"); }; - - // void clear(const value_t &t) {}; - - // /*! returns number of elements (as 64-bit int) across all dimensions */ - // virtual size_t numElements() const override { - // return size_t(dims.x)*size_t(dims.y)*size_t(dims.z); - // }; - - // const vec3i dims; - // }; - /*! shifts another array3d by a given amount */ template - struct IndexShiftedArray3D : public Array3D { - - IndexShiftedArray3D(std::shared_ptr> actual, const vec3i &shift) + struct IndexShiftedArray3D : public Array3D + { + IndexShiftedArray3D(std::shared_ptr> actual, + const vec3i &shift) : actual(actual), shift(shift) - {}; + {} /*! return size (ie, "dimensions") of volume */ - virtual vec3i size() const override { return actual->size(); }; + virtual vec3i size() const override { return actual->size(); } /*! get cell value at location @@ -139,7 +108,8 @@ namespace ospcommon { /*! set cell value at location to given value \warning 'where' MUST be a valid cell location */ - virtual void set(const vec3i &where, const value_t &t) { throw std::runtime_error("cannot 'set' in a IndexShiftArray3D"); }; + virtual void set(const vec3i &where, const value_t &t) + { throw std::runtime_error("cannot 'set' in a IndexShiftArray3D"); } /*! returns number of elements (as 64-bit int) across all dimensions */ virtual size_t numElements() const override @@ -152,8 +122,8 @@ namespace ospcommon { /*! implemnetaiton of a wrapper class that makes an actual array3d of one type look like that of another type */ template - struct Array3DAccessor : public Array3D { - + struct Array3DAccessor : public Array3D + { Array3DAccessor(std::shared_ptr> actual); /*! return size (ie, "dimensions") of volume */ @@ -175,8 +145,8 @@ namespace ospcommon { /*! wrapper class that generates an artifically larger data set by simply repeating the given input */ template - struct Array3DRepeater : public Array3D { - + struct Array3DRepeater : public Array3D + { Array3DRepeater(const std::shared_ptr> &actual, const vec3i &repeatedSize); @@ -195,22 +165,23 @@ namespace ospcommon { const std::shared_ptr> actual; }; - - /*! implements a sub-set of another array3d */ + /*! implements a sub-set of another array3d */ template - struct SubBoxArray3D : public Array3D { + struct SubBoxArray3D : public Array3D + { - SubBoxArray3D(const std::shared_ptr> &actual, const box3i &clipBox) + SubBoxArray3D(const std::shared_ptr> &actual, + const box3i &clipBox) : actual(actual), clipBox(clipBox) { assert(actual); assert(clipBox.upper.x <= actual->size().x); assert(clipBox.upper.y <= actual->size().y); assert(clipBox.upper.z <= actual->size().z); - }; + } /*! return size (ie, "dimensions") of volume */ - virtual vec3i size() const override { return clipBox.size(); }; + virtual vec3i size() const override { return clipBox.size(); } /*! get cell value at location @@ -221,7 +192,8 @@ namespace ospcommon { /*! set cell value at location to given value \warning 'where' MUST be a valid cell location */ - virtual void set(const vec3i &where, const value_t &t) { throw std::runtime_error("cannot 'set' in a SubBoxArray3D"); }; + virtual void set(const vec3i &where, const value_t &t) + { throw std::runtime_error("cannot 'set' in a SubBoxArray3D"); } /*! returns number of elements (as 64-bit int) across all dimensions */ virtual size_t numElements() const override @@ -238,16 +210,16 @@ namespace ospcommon { /*! implements a array3d that's composed of multiple individual slices */ template - struct MultiSliceArray3D : public Array3D { - - MultiSliceArray3D(const std::vector> > &slice) + struct MultiSliceArray3D : public Array3D + { + MultiSliceArray3D(const std::vector>> &slice) : slice(slice) { - }; + } /*! return size (ie, "dimensions") of volume */ virtual vec3i size() const override - { return vec3i(slice[0]->size().x,slice[0]->size().y,slice.size()); }; + { return vec3i(slice[0]->size().x,slice[0]->size().y,slice.size()); } /*! get cell value at location @@ -260,7 +232,8 @@ namespace ospcommon { /*! set cell value at location to given value \warning 'where' MUST be a valid cell location */ - virtual void set(const vec3i &where, const value_t &t) { throw std::runtime_error("cannot 'set' in a MultiSliceArray3D"); }; + virtual void set(const vec3i &where, const value_t &t) + { throw std::runtime_error("cannot 'set' in a MultiSliceArray3D"); } /*! returns number of elements (as 64-bit int) across all dimensions */ virtual size_t numElements() const override @@ -275,20 +248,18 @@ namespace ospcommon { file (uint8,float,...) is given through the function's template parameter */ template - std::shared_ptr> loadRAW(const std::string &fileName, const vec3i &dims); + std::shared_ptr> loadRAW(const std::string &fileName, + const vec3i &dims); /*! load raw file with given dimensions. the 'type' of the raw file (uint8,float,...) is given through the function's template parameter */ template - std::shared_ptr> mmapRAW(const std::string &fileName, const vec3i &dims); - - - - + std::shared_ptr> mmapRAW(const std::string &fileName, + const vec3i &dims); // ------------------------------------------------------- - // iplementation section + // implementation section // ------------------------------------------------------- template diff --git a/components/ospcommon/box.h b/components/ospcommon/box.h index d5f294e079..9de0035e21 100644 --- a/components/ospcommon/box.h +++ b/components/ospcommon/box.h @@ -26,9 +26,11 @@ namespace ospcommon { typedef T scalar_t; typedef typename ospcommon::vec_t vec_t; - inline box_t() {} - inline box_t( EmptyTy ) : lower(pos_inf), upper(neg_inf) {} - inline box_t(const box_t &o) : lower(o.lower), upper(o.upper) {} + inline box_t() = default; + inline box_t(const box_t &o) = default; + inline box_t &operator=(const box_t &o) = default; + + inline box_t(EmptyTy) : lower(pos_inf), upper(neg_inf) {} inline box_t(const vec_t &lower, const vec_t &upper) : lower(lower), upper(upper) {} inline vec_t size() const { return upper - lower; } @@ -42,10 +44,7 @@ namespace ospcommon { inline bool contains(const vec_t &vec) const { return !anyLessThan(vec,lower) && !anyLessThan(upper,vec); } - inline box_t &operator=(const box_t &o) - { lower = o.lower; upper = o.upper; return *this; } - - vec_t lower, upper; + vec_t lower {vec_t()}, upper {vec_t()}; }; template @@ -118,6 +117,7 @@ namespace ospcommon { typedef box_t box2i; typedef box_t box3i; typedef box_t box3f; + typedef box_t box4f; typedef box_t box3fa; // typedef box_t box2i; diff --git a/components/ospcommon/cmake/FindGLFW.cmake b/components/ospcommon/cmake/FindGLFW.cmake new file mode 100644 index 0000000000..d77234606d --- /dev/null +++ b/components/ospcommon/cmake/FindGLFW.cmake @@ -0,0 +1,68 @@ +## ======================================================================== ## +## Copyright 2009-2017 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +IF (NOT GLFW_ROOT) + SET(GLFW_ROOT $ENV{GLFW_ROOT}) +ENDIF() +IF (NOT GLFW_ROOT) + SET(GLFW_ROOT $ENV{GLFWROOT}) +ENDIF() + +# detect changed GLFW_ROOT +IF (NOT GLFW_ROOT STREQUAL GLFW_ROOT_LAST) + UNSET(GLFW_INCLUDE_DIR CACHE) + UNSET(GLFW_LIBRARY CACHE) +ENDIF() + +IF (WIN32) + MESSAGE(WARNING "FindGLFW TODO on Windows!") +ELSE () + + FIND_PATH(GLFW_ROOT include/GLFW/glfw3.h + DOC "Root of GLFW installation" + HINTS ${GLFW_ROOT} + PATHS + ${PROJECT_SOURCE_DIR}/glfw + /usr/local + /usr + / + ) + + FIND_PATH(GLFW_INCLUDE_DIR GLFW/glfw3.h PATHS ${GLFW_ROOT}/include NO_DEFAULT_PATH) + SET(GLFW_HINTS HINTS ${GLFW_ROOT}/lib ${GLFW_ROOT}/lib64) + SET(GLFW_PATHS PATHS /usr/lib /usr/lib64 /lib /lib64) + FIND_LIBRARY(GLFW_LIBRARY libglfw.so ${GLFW_HINTS} ${GLFW_PATHS}) +ENDIF() + +SET(GLFW_ROOT_LAST ${GLFW_ROOT} CACHE INTERNAL "Last value of GLFW_ROOT to detect changes") + +SET(GLFW_ERROR_MESSAGE "GLFW not found in your environment. You can 1) install + via your OS package manager, or 2) install it + somewhere on your machine and point GLFW_ROOT to it.") + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GLFW + ${GLFW_ERROR_MESSAGE} + GLFW_INCLUDE_DIR GLFW_LIBRARY +) + +IF (GLFW_FOUND) + SET(GLFW_INCLUDE_DIRS ${GLFW_INCLUDE_DIR}) + SET(GLFW_LIBRARIES ${GLFW_LIBRARY}) +ENDIF() + +MARK_AS_ADVANCED(GLFW_INCLUDE_DIR) +MARK_AS_ADVANCED(GLFW_LIBRARY) diff --git a/components/ospcommon/cmake/FindTBB.cmake b/components/ospcommon/cmake/FindTBB.cmake index aa6579d588..fe67ce70c8 100644 --- a/components/ospcommon/cmake/FindTBB.cmake +++ b/components/ospcommon/cmake/FindTBB.cmake @@ -97,10 +97,11 @@ ELSE () FIND_LIBRARY(TBB_LIBRARY_MALLOC_DEBUG tbbmalloc_debug PATHS ${TBB_ROOT}/lib NO_DEFAULT_PATH) ELSE() FIND_PATH(TBB_INCLUDE_DIR tbb/task_scheduler_init.h PATHS ${TBB_ROOT}/include NO_DEFAULT_PATH) - FIND_LIBRARY(TBB_LIBRARY libtbb.so.2 HINTS ${TBB_ROOT}/lib/intel64/gcc4.4) - FIND_LIBRARY(TBB_LIBRARY_DEBUG libtbb_debug.so.2 HINTS ${TBB_ROOT}/lib/intel64/gcc4.4) - FIND_LIBRARY(TBB_LIBRARY_MALLOC libtbbmalloc.so.2 HINTS ${TBB_ROOT}/lib/intel64/gcc4.4) - FIND_LIBRARY(TBB_LIBRARY_MALLOC_DEBUG libtbbmalloc_debug.so.2 HINTS ${TBB_ROOT}/lib/intel64/gcc4.4) + SET(TBB_HINTS HINTS ${TBB_ROOT}/lib/intel64/gcc4.4 ${TBB_ROOT}/lib ${TBB_ROOT}/lib64 PATHS /usr/libx86_64-linux-gnu/) + FIND_LIBRARY(TBB_LIBRARY libtbb.so.2 ${TBB_HINTS}) + FIND_LIBRARY(TBB_LIBRARY_DEBUG libtbb_debug.so.2 ${TBB_HINTS}) + FIND_LIBRARY(TBB_LIBRARY_MALLOC libtbbmalloc.so.2 ${TBB_HINTS}) + FIND_LIBRARY(TBB_LIBRARY_MALLOC_DEBUG libtbbmalloc_debug.so.2 ${TBB_HINTS}) ENDIF() ENDIF() diff --git a/components/ospcommon/cmake/Findlibdispatch.cmake b/components/ospcommon/cmake/Findlibdispatch.cmake new file mode 100644 index 0000000000..242940a8ca --- /dev/null +++ b/components/ospcommon/cmake/Findlibdispatch.cmake @@ -0,0 +1,61 @@ +## ======================================================================== ## +## Copyright 2009-2017 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +IF (NOT LIBDISPATCH_ROOT) + SET(LIBDISPATCH_ROOT $ENV{LIBDISPATCH_ROOT}) +ENDIF() +IF (NOT LIBDISPATCH_ROOT) + SET(LIBDISPATCH_ROOT $ENV{LIBDISPATCHROOT}) +ENDIF() + +# detect changed LIBDISPATCH_ROOT +IF (NOT LIBDISPATCH_ROOT STREQUAL LIBDISPATCH_ROOT_LAST) + UNSET(LIBDISPATCH_INCLUDE_DIR CACHE) + UNSET(LIBDISPATCH_LIBRARY CACHE) +ENDIF() + +FIND_PATH(LIBDISPATCH_ROOT include/dispatch/dispatch.h + DOC "Root of 'libdispatch' installation" + HINTS ${LIBDISPATCH_ROOT} + PATHS + /usr + /usr/local + /opt + /opt/libdispatch +) + +FIND_PATH(LIBDISPATCH_INCLUDE_DIR dispatch/dispatch.h PATHS ${LIBDISPATCH_ROOT}/include NO_DEFAULT_PATH) +FIND_LIBRARY(LIBDISPATCH_LIBRARY dispatch PATHS ${LIBDISPATCH_ROOT}/lib NO_DEFAULT_PATH) + +SET(LIBDISPATCH_ROOT_LAST ${LIBDISPATCH_ROOT} CACHE INTERNAL "Last value of LIBDISPATCH_ROOT to detect changes") + +SET(LIBDISPATCH_ERROR_MESSAGE + "Apple's 'libdispatch' not found. Please set LIBDISPATCH_ROOT to the installation location of libdispatch or please choose a different tasking system backend (OSPRAY_TASKING_SYSTEM).") + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBDISPATCH + ${LIBDISPATCH_ERROR_MESSAGE} + LIBDISPATCH_INCLUDE_DIR LIBDISPATCH_LIBRARY +) + +IF (LIBDISPATCH_FOUND) + SET(LIBDISPATCH_INCLUDE_DIRS ${LIBDISPATCH_INCLUDE_DIR}) + SET(LIBDISPATCH_LIBRARIES ${LIBDISPATCH_LIBRARY}) +ENDIF() + +MARK_AS_ADVANCED(LIBDISPATCH_INCLUDE_DIR) +MARK_AS_ADVANCED(LIBDISPATCH_LIBRARY) + diff --git a/components/ospcommon/cmake/clang.cmake b/components/ospcommon/cmake/clang.cmake index 0656767518..52ddb7141e 100644 --- a/components/ospcommon/cmake/clang.cmake +++ b/components/ospcommon/cmake/clang.cmake @@ -14,13 +14,19 @@ ## limitations under the License. ## ## ======================================================================== ## -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fno-strict-aliasing -Wno-narrowing -std=c++11") +SET(OSPRAY_CXX_FLAGS "-std=c++11 -fPIC -fno-strict-aliasing -Wno-narrowing") + +IF(OSPRAY_STRICT_BUILD) + SET(OSPRAY_CXX_FLAGS "-Wall ${OSPRAY_CXX_FLAGS}") +ENDIF() + +SET(CMAKE_CXX_FLAGS "${OSPRAY_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG -g -O0 -Wstrict-aliasing=1") SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -Wstrict-aliasing=1") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -g -O3 -Wstrict-aliasing=1") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}") IF (APPLE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") # we only use MacOSX 10.7 features - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") # link against C++11 stdlib + SET(CMAKE_CXX_FLAGS "-mmacosx-version-min=10.7 ${CMAKE_CXX_FLAGS}") # we only use MacOSX 10.7 features + SET(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") # link against C++11 stdlib ENDIF() diff --git a/components/ospcommon/cmake/gcc.cmake b/components/ospcommon/cmake/gcc.cmake index 2d1614c7b1..738d10e231 100644 --- a/components/ospcommon/cmake/gcc.cmake +++ b/components/ospcommon/cmake/gcc.cmake @@ -14,15 +14,21 @@ ## limitations under the License. ## ## ======================================================================== ## -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -fno-strict-aliasing -std=c++11 -Wno-narrowing") +SET(OSPRAY_CXX_FLAGS "-std=c++11 -fPIC -fno-strict-aliasing -Wno-narrowing") + +IF(OSPRAY_STRICT_BUILD) + SET(OSPRAY_CXX_FLAGS "-Wall ${OSPRAY_CXX_FLAGS}") +ENDIF() + +SET(CMAKE_CXX_FLAGS "${OSPRAY_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG -g -Wstrict-aliasing=1") SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -Wstrict-aliasing=1 -ffast-math ") SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-DNDEBUG -g -O3 -Wstrict-aliasing=1 -ffast-math ") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}") IF (APPLE) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") # we only use MacOSX 10.7 features - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") # link against C++11 stdlib + SET(CMAKE_CXX_FLAGS "-mmacosx-version-min=10.7 ${CMAKE_CXX_FLAGS}") # we only use MacOSX 10.7 features + SET(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") # link against C++11 stdlib ENDIF() # check whether GCC version is new enough for C++11 diff --git a/components/ospcommon/cmake/glut.cmake b/components/ospcommon/cmake/glut.cmake deleted file mode 100644 index 5477e447a2..0000000000 --- a/components/ospcommon/cmake/glut.cmake +++ /dev/null @@ -1,95 +0,0 @@ -## ======================================================================== ## -## Copyright 2009-2017 Intel Corporation ## -## ## -## Licensed under the Apache License, Version 2.0 (the "License"); ## -## you may not use this file except in compliance with the License. ## -## You may obtain a copy of the License at ## -## ## -## http://www.apache.org/licenses/LICENSE-2.0 ## -## ## -## Unless required by applicable law or agreed to in writing, software ## -## distributed under the License is distributed on an "AS IS" BASIS, ## -## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## -## See the License for the specific language governing permissions and ## -## limitations under the License. ## -## ======================================================================== ## - -FIND_PACKAGE(OpenGL REQUIRED) - -IF (APPLE) - FIND_PACKAGE(GLUT REQUIRED) -ELSEIF (WIN32) -# FindGLUT.cmake is broken for Windows in CMake until 3.0: does not support PATH - IF (GLUT_ROOT_PATH) - MESSAGE(WARNING "Warning: GLUT_ROOT_PATH is deprecated.") - SET(DEPRECATED_WIN32_INCLUDE ${GLUT_ROOT_PATH}/include) - SET(DEPRECATED_WIN32_RELEASE ${GLUT_ROOT_PATH}/Release) - ENDIF() - FIND_PATH(GLUT_INCLUDE_DIR - NAMES GL/glut.h - PATHS ${FREEGLUT_ROOT_PATH}/include - ${PROJECT_SOURCE_DIR}/../freeglut/include - ${PROJECT_SOURCE_DIR}/../freeglut-MSVC-3.0.0-2.mp/include ${PROJECT_SOURCE_DIR}/../freeglut-MSVC-3.0.0-2.mp/freeglut/include - ${PROJECT_SOURCE_DIR}/../freeglut-MSVC-2.8.1-1.mp/include ${PROJECT_SOURCE_DIR}/../freeglut-MSVC-2.8.1-1.mp/freeglut/include - ${DEPRECATED_WIN32_INCLUDE} - ) - # detect and select x64 - IF (CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(ARCH x64) - ENDIF() - FIND_LIBRARY(GLUT_glut_LIBRARY - NAMES freeglut glut glut32 - HINTS ${GLUT_INCLUDE_DIR}/../lib/${ARCH} ${FREEGLUT_ROOT_PATH}/lib/${ARCH} ${DEPRECATED_WIN32_RELEASE} - ) - SET(GLUT_LIBRARIES ${GLUT_glut_LIBRARY}) - MARK_AS_ADVANCED( - GLUT_INCLUDE_DIR - GLUT_glut_LIBRARY - ) - IF (NOT GLUT_INCLUDE_DIR OR NOT GLUT_glut_LIBRARY) - MESSAGE(FATAL_ERROR "Could not find GLUT library. You could fetch freeglut from http://www.transmissionzero.co.uk/software/freeglut-devel/ and set the FREEGLUT_ROOT_PATH variable in cmake.") - ENDIF() -ELSE() - FIND_PACKAGE(GLUT) - IF (GLUT_FOUND) - SET(GLUT_LIBRARIES glut GLU m) - ELSE() - FIND_PATH(GLUT_INCLUDE_DIR GL/glut.h - $ENV{TACC_FREEGLUT_INC} - ) - FIND_PATH(GLUT_LINK_DIR libglut.so - $ENV{TACC_FREEGLUT_LIB} - ) - FIND_LIBRARY(GLUT_LIBRARIES NAMES libglut.so PATHS $ENV{TACC_FREEGLUT_LIB}) - IF (NOT GLUT_LIBRARIES) - MESSAGE(FATAL_ERROR "Could not find GLUT library, even after trying" - " additional search dirs." - " Please disable the following to build without GLUT:" - "\n" - " OSPRAY_APPS_MODELVIEWER," - " OSPRAY_APPS_PARTICLEVIEWER," - " OSPRAY_APPS_QTVIEWER," - " OSPRAY_APPS_STREAMLINEVIEWER," - " OSPRAY_MODULE_TACHYON" - "\n") - ELSE() - SET(GLUT_FOUND ON) - ENDIF() - ENDIF() -ENDIF() - -INCLUDE_DIRECTORIES(${OPENGL_INCLUDE_DIR} ${GLUT_INCLUDE_DIR}) - - -############################################################## -# redistribute freeglut on Windows -############################################################## - -IF (WIN32 AND OSPRAY_INSTALL_DEPENDENCIES) - FIND_FILE(GLUT_DLL - NAMES freeglut.dll - HINTS ${GLUT_INCLUDE_DIR}/../bin/${ARCH} ${FREEGLUT_ROOT_PATH}/bin/${ARCH} - ) - MARK_AS_ADVANCED(GLUT_DLL) - INSTALL(PROGRAMS ${GLUT_DLL} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT redist) -ENDIF() diff --git a/components/ospcommon/cmake/icc.cmake b/components/ospcommon/cmake/icc.cmake index a769cf0366..a495b96401 100644 --- a/components/ospcommon/cmake/icc.cmake +++ b/components/ospcommon/cmake/icc.cmake @@ -14,20 +14,26 @@ ## limitations under the License. ## ## ======================================================================== ## -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fPIC -no-ansi-alias -std=c++11 -DNOMINMAX") +SET(OSPRAY_CXX_FLAGS "-std=c++11 -fPIC -fno-strict-aliasing -no-ansi-alias -DNOMINMAX") + +IF(OSPRAY_STRICT_BUILD) + SET(OSPRAY_CXX_FLAGS "-Wall ${OSPRAY_CXX_FLAGS}") +ENDIF() + +SET(CMAKE_CXX_FLAGS "${OSPRAY_CXX_FLAGS} ${CMAKE_CXX_FLAGS}") SET(CMAKE_CXX_FLAGS_DEBUG "-DDEBUG -g") SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3") # on Windows use "-fp:fast" instead of "-fp-model fast" #SET(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -no-ansi-alias -restrict -fp-model fast -fimf-precision=low -no-prec-div -no-prec-sqrt -fma -no-inline-max-total-size -inline-factor=200 ") -SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELEASE} -g") -SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99") +SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-g ${CMAKE_CXX_FLAGS_RELEASE}") +SET(CMAKE_C_FLAGS "-std=c99 ${CMAKE_C_FLAGS}") IF (APPLE) - SET (CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS_INIT} -dynamiclib) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=10.7") # we only use MacOSX 10.7 features - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") # link against C++11 stdlib + SET (CMAKE_SHARED_LINKER_FLAGS "-dynamiclib ${CMAKE_SHARED_LINKER_FLAGS_INIT}") + SET(CMAKE_CXX_FLAGS "-mmacosx-version-min=10.7 ${CMAKE_CXX_FLAGS}") # we only use MacOSX 10.7 features + SET(CMAKE_CXX_FLAGS "-stdlib=libc++ ${CMAKE_CXX_FLAGS}") # link against C++11 stdlib ENDIF() # enable -static-intel and avoid to export ICC specific symbols from OSPRay -SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-intel") -SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--exclude-libs=ALL") +SET(CMAKE_CXX_FLAGS "-static-intel ${CMAKE_CXX_FLAGS}") +SET(CMAKE_SHARED_LINKER_FLAGS "-Wl,--exclude-libs=ALL ${CMAKE_SHARED_LINKER_FLAGS}") diff --git a/components/ospcommon/cmake/ispc.cmake b/components/ospcommon/cmake/ispc.cmake index 0dbfdb9a08..057a6ddd79 100644 --- a/components/ospcommon/cmake/ispc.cmake +++ b/components/ospcommon/cmake/ispc.cmake @@ -15,7 +15,7 @@ ## ======================================================================== ## # ISPC versions to look for, in decending order (newest first) -SET(ISPC_VERSION_WORKING "1.9.1" "1.9.0") +SET(ISPC_VERSION_WORKING "1.9.1") LIST(GET ISPC_VERSION_WORKING -1 ISPC_VERSION_REQUIRED) IF (NOT ISPC_EXECUTABLE) @@ -24,10 +24,10 @@ IF (NOT ISPC_EXECUTABLE) SET(ISPC_DIR_SUFFIX "osx") ELSEIF(WIN32) SET(ISPC_DIR_SUFFIX "windows") - IF (MSVC14) - LIST(APPEND ISPC_DIR_SUFFIX "windows-vs2015") - ELSE() + IF (MSVC_VERSION LESS 1900) LIST(APPEND ISPC_DIR_SUFFIX "windows-vs2013") + ELSE() + LIST(APPEND ISPC_DIR_SUFFIX "windows-vs2015") ENDIF() ELSE() SET(ISPC_DIR_SUFFIX "linux") @@ -38,7 +38,7 @@ IF (NOT ISPC_EXECUTABLE) ENDFOREACH() ENDFOREACH() - FIND_PROGRAM(ISPC_EXECUTABLE ispc PATHS ${ISPC_DIR_HINT} DOC "Path to the ISPC executable.") + FIND_PROGRAM(ISPC_EXECUTABLE ispc HINTS ${ISPC_DIR_HINT} DOC "Path to the ISPC executable.") IF (NOT ISPC_EXECUTABLE) MESSAGE("********************************************") MESSAGE("Could not find ISPC (looked in PATH and ${ISPC_DIR_HINT})") @@ -91,7 +91,7 @@ MACRO (OSPRAY_ISPC_COMPILE) ELSE() SET(ISPC_ARCHITECTURE "x86") ENDIF() - + SET(ISPC_TARGET_DIR ${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES(${ISPC_TARGET_DIR}) @@ -147,26 +147,18 @@ MACRO (OSPRAY_ISPC_COMPILE) # if we have multiple targets add additional object files LIST(LENGTH ISPC_TARGETS NUM_TARGETS) - IF (NUM_TARGETS EQUAL 1) - # workaround link issues to Embree ISPC exports: - # we add a 2nd target to force ISPC to add the ISA suffix during name - # mangling - SET(ISPC_TARGET_ARGS "${ISPC_TARGETS},sse2") - LIST(APPEND ISPC_TARGETS sse2) + IF (NUM_TARGETS GREATER 1) + FOREACH(target ${ISPC_TARGETS}) + STRING(REPLACE "-i32x16" "" target ${target}) # strip avx512(knl|skx)-i32x16 + SET(results ${results} "${outdir}/${fname}.dev_${target}${ISPC_TARGET_EXT}") + ENDFOREACH() ENDIF() - FOREACH(target ${ISPC_TARGETS}) - # in v1.9.0 ISPC changed the ISA suffix of avx512knl-i32x16 to just 'avx512knl' - IF (${target} STREQUAL "avx512knl-i32x16" AND NOT ISPC_VERSION VERSION_LESS "1.9.0") - SET(target "avx512knl") - ENDIF() - SET(results ${results} "${outdir}/${fname}.dev_${target}${ISPC_TARGET_EXT}") - ENDFOREACH() ADD_CUSTOM_COMMAND( OUTPUT ${results} ${ISPC_TARGET_DIR}/${fname}_ispc.h COMMAND ${CMAKE_COMMAND} -E make_directory ${outdir} COMMAND ${ISPC_EXECUTABLE} - -I ${CMAKE_CURRENT_SOURCE_DIR} + -I ${CMAKE_CURRENT_SOURCE_DIR} ${ISPC_INCLUDE_DIR_PARMS} --arch=${ISPC_ARCHITECTURE} --addressing=32 @@ -176,7 +168,7 @@ MACRO (OSPRAY_ISPC_COMPILE) --opt=fast-math ${ISPC_ADDITIONAL_ARGS} -h ${ISPC_TARGET_DIR}/${fname}_ispc.h - -MMM ${outdir}/${fname}.dev.idep + -MMM ${outdir}/${fname}.dev.idep -o ${outdir}/${fname}.dev${ISPC_TARGET_EXT} ${input} DEPENDS ${input} ${deps} diff --git a/components/ospcommon/cmake/macros.cmake b/components/ospcommon/cmake/macros.cmake index a51de89ec5..23acd8732a 100644 --- a/components/ospcommon/cmake/macros.cmake +++ b/components/ospcommon/cmake/macros.cmake @@ -48,18 +48,23 @@ ENDFUNCTION() MACRO(OSPRAY_CONFIGURE_ISPC_ISA) SET(OSPRAY_BUILD_ISA "ALL" CACHE STRING - "Target ISA (SSE4, AVX, AVX2, AVX512, or ALL)") + "Target ISA (SSE4, AVX, AVX2, AVX512KNL, AVX512SKX, or ALL)") STRING(TOUPPER ${OSPRAY_BUILD_ISA} OSPRAY_BUILD_ISA) - SET(OSPRAY_SUPPORTED_ISAS SSE4) + IF(EMBREE_ISA_SUPPORTS_SSE4) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} SSE4) + ENDIF() IF(EMBREE_ISA_SUPPORTS_AVX) SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX) ENDIF() IF(EMBREE_ISA_SUPPORTS_AVX2) SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX2) ENDIF() - IF(EMBREE_ISA_SUPPORTS_AVX512) - SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX512) + IF(EMBREE_ISA_SUPPORTS_AVX512KNL) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX512KNL) + ENDIF() + IF(EMBREE_ISA_SUPPORTS_AVX512SKX) + SET(OSPRAY_SUPPORTED_ISAS ${OSPRAY_SUPPORTED_ISAS} AVX512SKX) ENDIF() SET_PROPERTY(CACHE OSPRAY_BUILD_ISA PROPERTY STRINGS @@ -69,8 +74,10 @@ MACRO(OSPRAY_CONFIGURE_ISPC_ISA) IF (OSPRAY_BUILD_ISA STREQUAL "ALL") - SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} sse4) - MESSAGE(STATUS "OSPRay SSE4 ISA target enabled.") + IF(EMBREE_ISA_SUPPORTS_SSE4) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} sse4) + MESSAGE(STATUS "OSPRay SSE4 ISA target enabled.") + ENDIF() IF(EMBREE_ISA_SUPPORTS_AVX) SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx) MESSAGE(STATUS "OSPRay AVX ISA target enabled.") @@ -79,14 +86,25 @@ MACRO(OSPRAY_CONFIGURE_ISPC_ISA) SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx2) MESSAGE(STATUS "OSPRay AVX2 ISA target enabled.") ENDIF() - IF(EMBREE_ISA_SUPPORTS_AVX512) + IF(EMBREE_ISA_SUPPORTS_AVX512KNL) SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx512knl-i32x16) - MESSAGE(STATUS "OSPRay AVX512 ISA target enabled.") + MESSAGE(STATUS "OSPRay AVX512KNL ISA target enabled.") + ENDIF() + IF(EMBREE_ISA_SUPPORTS_AVX512SKX) + SET(OSPRAY_ISPC_TARGET_LIST ${OSPRAY_ISPC_TARGET_LIST} avx512skx-i32x16) + MESSAGE(STATUS "OSPRay AVX512SKX ISA target enabled.") + ENDIF() + + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX512SKX") + + IF(NOT EMBREE_ISA_SUPPORTS_AVX512SKX) + MESSAGE(FATAL_ERROR "Your Embree build does not support AVX512SKX!") ENDIF() + SET(OSPRAY_ISPC_TARGET_LIST avx512skx-i32x16) - ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX512") + ELSEIF (OSPRAY_BUILD_ISA STREQUAL "AVX512KNL") - IF(NOT EMBREE_ISA_SUPPORTS_AVX512) + IF(NOT EMBREE_ISA_SUPPORTS_AVX512KNL) MESSAGE(FATAL_ERROR "Your Embree build does not support AVX512KNL!") ENDIF() SET(OSPRAY_ISPC_TARGET_LIST avx512knl-i32x16) @@ -106,10 +124,42 @@ MACRO(OSPRAY_CONFIGURE_ISPC_ISA) SET(OSPRAY_ISPC_TARGET_LIST avx) ELSEIF (OSPRAY_BUILD_ISA STREQUAL "SSE4") + + IF(NOT EMBREE_ISA_SUPPORTS_SSE4) + MESSAGE(FATAL_ERROR "Your Embree build does not support SSE4!") + ENDIF() SET(OSPRAY_ISPC_TARGET_LIST sse4) + ELSE () MESSAGE(ERROR "Invalid OSPRAY_BUILD_ISA value. " - "Please select one of SSE4, AVX, AVX2, AVX512, or ALL.") + "Please select one of ${OSPRAY_SUPPORTED_ISAS}, or ALL.") + ENDIF() + + # workaround link issues to Embree ISPC exports + # ISPC only adds the ISA suffix during name mangling (and dynamic dispatch + # code) when compiling for multiple targets. Thus, when only one OSPRay ISA is + # selected, but Embree was compiled for multiple ISAs, we need to add a + # second, different, supported dummy target. + LIST(LENGTH OSPRAY_ISPC_TARGET_LIST NUM_TARGETS) + IF (NUM_TARGETS EQUAL 1) + IF (EMBREE_ISA_SUPPORTS_SSE2) + LIST(APPEND OSPRAY_ISPC_TARGET_LIST sse2) + ELSEIF (EMBREE_ISA_SUPPORTS_SSE4 AND + NOT OSPRAY_ISPC_TARGET_LIST STREQUAL "sse4") + LIST(APPEND OSPRAY_ISPC_TARGET_LIST sse4) + ELSEIF (EMBREE_ISA_SUPPORTS_AVX AND + NOT OSPRAY_ISPC_TARGET_LIST STREQUAL "avx") + LIST(APPEND OSPRAY_ISPC_TARGET_LIST avx) + ELSEIF (EMBREE_ISA_SUPPORTS_AVX2 AND + NOT OSPRAY_ISPC_TARGET_LIST STREQUAL "avx2") + LIST(APPEND OSPRAY_ISPC_TARGET_LIST avx2) + ELSEIF (EMBREE_ISA_SUPPORTS_AVX512KNL AND + NOT OSPRAY_ISPC_TARGET_LIST STREQUAL "avx512knl-i32x16") + LIST(APPEND OSPRAY_ISPC_TARGET_LIST avx512knl-i32x16) + ELSEIF (EMBREE_ISA_SUPPORTS_AVX512SKX AND + NOT OSPRAY_ISPC_TARGET_LIST STREQUAL "avx512skx-i32x16") + LIST(APPEND OSPRAY_ISPC_TARGET_LIST avx512skx-i32x16) + ENDIF() ENDIF() ENDMACRO() @@ -131,6 +181,7 @@ MACRO(OSPRAY_ADD_LIBRARY name type) ENDMACRO() ## Target install macros for OSPRay libraries ## +INCLUDE(GNUInstallDirs) MACRO(OSPRAY_INSTALL_LIBRARY name component) INSTALL(TARGETS ${name} @@ -156,39 +207,39 @@ ENDMACRO() # Helper function to return arguments of OSPRAY_CREATE_ in separate # variables, prefixed with PREFIX FUNCTION(OSPRAY_SPLIT_CREATE_ARGS PREFIX) - SET(SOURCES "") + SET(MY_SOURCES "") SET(LIBS "") - SET(EXCLUDE_FROM_ALL FALSE) - SET(COMPONENT ${OSPRAY_DEFAULT_COMPONENT}) - SET(CURRENT_LIST SOURCES) + SET(MY_EXCLUDE_FROM_ALL FALSE) + SET(MY_COMPONENT ${OSPRAY_DEFAULT_COMPONENT}) + SET(CURRENT_LIST MY_SOURCES) FOREACH(arg ${ARGN}) - IF (${arg} STREQUAL "LINK") + IF ("${arg}" STREQUAL "LINK") SET(CURRENT_LIST LIBS) - ELSEIF (${arg} STREQUAL "EXCLUDE_FROM_ALL") - SET(EXCLUDE_FROM_ALL TRUE) - ELSEIF (${arg} STREQUAL "COMPONENT") - SET(CURRENT_LIST COMPONENT) + ELSEIF ("${arg}" STREQUAL "EXCLUDE_FROM_ALL") + SET(MY_EXCLUDE_FROM_ALL TRUE) + ELSEIF ("${arg}" STREQUAL "COMPONENT") + SET(CURRENT_LIST MY_COMPONENT) ELSE() LIST(APPEND ${CURRENT_LIST} ${arg}) ENDIF () ENDFOREACH() # COMPONENT only required when installed - IF (NOT ${EXCLUDE_FROM_ALL}) - LIST(LENGTH COMPONENT COMPONENTS) + IF (NOT ${MY_EXCLUDE_FROM_ALL}) + LIST(LENGTH MY_COMPONENT COMPONENTS) IF (COMPONENTS EQUAL 0) MESSAGE(WARNING "No COMPONENT for installation specified!") - SET(COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) + SET(MY_COMPONENT ${CMAKE_INSTALL_DEFAULT_COMPONENT_NAME}) ELSE() - LIST(GET COMPONENT -1 COMPONENT) + LIST(GET MY_COMPONENT -1 MY_COMPONENT) ENDIF() ENDIF() - SET(${PREFIX}_SOURCES ${SOURCES} PARENT_SCOPE) + SET(${PREFIX}_SOURCES ${MY_SOURCES} PARENT_SCOPE) SET(${PREFIX}_LIBS ${LIBS} PARENT_SCOPE) - SET(${PREFIX}_EXCLUDE_FROM_ALL ${EXCLUDE_FROM_ALL} PARENT_SCOPE) - SET(${PREFIX}_COMPONENT ${COMPONENT} PARENT_SCOPE) + SET(${PREFIX}_EXCLUDE_FROM_ALL ${MY_EXCLUDE_FROM_ALL} PARENT_SCOPE) + SET(${PREFIX}_COMPONENT ${MY_COMPONENT} PARENT_SCOPE) ENDFUNCTION() ## Conveniance macro for creating OSPRay libraries ## @@ -254,6 +305,20 @@ MACRO(OSPRAY_CREATE_APPLICATION APP_NAME) ENDIF() ENDMACRO() +## Conveniance macro for creating OSPRay test applications ## +# Usage - same as OSPRAY_CREATE_APPLICATION +# +# Will build the app if 'OSPRAY_ENABLE_TESTING=ON' in CMake + +MACRO(OSPRAY_CREATE_TEST) + IF (OSPRAY_ENABLE_TESTING) + OSPRAY_CREATE_APPLICATION( + ${ARGN} + COMPONENT tests + ) + ENDIF() +ENDMACRO() + ## Conveniance macro for installing OSPRay headers ## # Usage # @@ -264,19 +329,19 @@ ENDMACRO() MACRO(OSPRAY_INSTALL_SDK_HEADERS) SET(HEADERS "") - SET(DESTINATION "") + SET(MY_DESTINATION "") SET(CURRENT_LIST HEADERS) FOREACH(arg ${ARGN}) - IF (${arg} STREQUAL "DESTINATION") - SET(CURRENT_LIST DESTINATION) + IF ("${arg}" STREQUAL "DESTINATION") + SET(CURRENT_LIST MY_DESTINATION) ELSE() LIST(APPEND ${CURRENT_LIST} ${arg}) ENDIF () ENDFOREACH() INSTALL(FILES ${HEADERS} - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray/SDK/${DESTINATION} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray/SDK/${MY_DESTINATION} COMPONENT devel ) ENDMACRO() @@ -287,6 +352,9 @@ MACRO(OSPRAY_CONFIGURE_COMPILER) # unhide compiler to make it easier for users to see what they are using MARK_AS_ADVANCED(CLEAR CMAKE_CXX_COMPILER) + OPTION(OSPRAY_STRICT_BUILD "Build with additional warning flags" OFF) + MARK_AS_ADVANCED(OSPRAY_STRICT_BUILD) + SET(OSPRAY_COMPILER_ICC OFF) SET(OSPRAY_COMPILER_GCC OFF) SET(OSPRAY_COMPILER_CLANG OFF) @@ -294,7 +362,11 @@ MACRO(OSPRAY_CONFIGURE_COMPILER) IF (${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel") SET(OSPRAY_COMPILER_ICC ON) - INCLUDE(icc) + IF(WIN32) # icc on Windows behaves like msvc + INCLUDE(msvc) + ELSE() + INCLUDE(icc) + ENDIF() ELSEIF (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") SET(OSPRAY_COMPILER_GCC ON) INCLUDE(gcc) @@ -308,6 +380,12 @@ MACRO(OSPRAY_CONFIGURE_COMPILER) MESSAGE(FATAL_ERROR "Unsupported compiler specified: '${CMAKE_CXX_COMPILER_ID}'") ENDIF() + + IF (WIN32) + # increase stack to 8MB (the default size of 1MB is too small for our apps) + # note: linker options are independent of compiler (icc or MSVC) + SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /STACK:8388608") + ENDIF() ENDMACRO() ## Tasking system configuration macro ## @@ -323,29 +401,22 @@ MACRO(OSPRAY_CONFIGURE_TASKING_SYSTEM) SET(CILK_STRING "Cilk") ENDIF() - # NOTE(jda) - Always default to TBB, at least until Cilk is *exactly* the same - # as TBB... - #IF(${CMAKE_CXX_COMPILER_ID} STREQUAL "Intel") - # SET(TASKING_DEFAULT ${CILK_STRING}) - #ELSE() - SET(TASKING_DEFAULT TBB) - #ENDIF() - - SET(OSPRAY_TASKING_SYSTEM ${TASKING_DEFAULT} CACHE STRING - "Per-node thread tasking system [TBB,OpenMP,Cilk,Internal,Debug]") + SET(OSPRAY_TASKING_SYSTEM TBB CACHE STRING + "Per-node thread tasking system [TBB,OpenMP,Cilk,LibDispatch,Internal,Debug]") SET_PROPERTY(CACHE OSPRAY_TASKING_SYSTEM PROPERTY - STRINGS TBB ${CILK_STRING} OpenMP Internal Debug) + STRINGS TBB ${CILK_STRING} OpenMP Internal LibDispatch Debug) MARK_AS_ADVANCED(OSPRAY_TASKING_SYSTEM) # NOTE(jda) - Make the OSPRAY_TASKING_SYSTEM build option case-insensitive STRING(TOUPPER ${OSPRAY_TASKING_SYSTEM} OSPRAY_TASKING_SYSTEM_ID) - SET(OSPRAY_TASKING_TBB FALSE) - SET(OSPRAY_TASKING_CILK FALSE) - SET(OSPRAY_TASKING_OPENMP FALSE) - SET(OSPRAY_TASKING_INTERNAL FALSE) - SET(OSPRAY_TASKING_DEBUG FALSE) + SET(OSPRAY_TASKING_TBB FALSE) + SET(OSPRAY_TASKING_CILK FALSE) + SET(OSPRAY_TASKING_OPENMP FALSE) + SET(OSPRAY_TASKING_INTERNAL FALSE) + SET(OSPRAY_TASKING_LIBDISPATCH FALSE) + SET(OSPRAY_TASKING_DEBUG FALSE) IF(${OSPRAY_TASKING_SYSTEM_ID} STREQUAL "TBB") SET(OSPRAY_TASKING_TBB TRUE) @@ -355,6 +426,8 @@ MACRO(OSPRAY_CONFIGURE_TASKING_SYSTEM) SET(OSPRAY_TASKING_OPENMP TRUE) ELSEIF(${OSPRAY_TASKING_SYSTEM_ID} STREQUAL "INTERNAL") SET(OSPRAY_TASKING_INTERNAL TRUE) + ELSEIF(${OSPRAY_TASKING_SYSTEM_ID} STREQUAL "LIBDISPATCH") + SET(OSPRAY_TASKING_LIBDISPATCH TRUE) ELSE() SET(OSPRAY_TASKING_DEBUG TRUE) ENDIF() @@ -377,8 +450,9 @@ MACRO(OSPRAY_CONFIGURE_TASKING_SYSTEM) IF (OPENMP_FOUND) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - SET(CMAKE_EXE_LINKER_FLAGS - "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + IF(OSPRAY_COMPILER_ICC) # workaround linker issue #115 + SET(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -liomp5") + ENDIF() ADD_DEFINITIONS(-DOSPRAY_TASKING_OMP) ENDIF() ELSEIF(OSPRAY_TASKING_CILK) @@ -391,6 +465,11 @@ MACRO(OSPRAY_CONFIGURE_TASKING_SYSTEM) ENDIF() ELSEIF(OSPRAY_TASKING_INTERNAL) ADD_DEFINITIONS(-DOSPRAY_TASKING_INTERNAL) + ELSEIF(OSPRAY_TASKING_LIBDISPATCH) + FIND_PACKAGE(libdispatch REQUIRED) + INCLUDE_DIRECTORIES(${LIBDISPATCH_INCLUDE_DIRS}) + SET(TASKING_SYSTEM_LIBS ${LIBDISPATCH_LIBRARIES}) + ADD_DEFINITIONS(-DOSPRAY_TASKING_LIBDISPATCH) ELSE()#Debug # Do nothing, will fall back to scalar code (useful for debugging) ENDIF() diff --git a/components/ospcommon/common.h b/components/ospcommon/common.h index 4aaaa5dea3..e22e942009 100644 --- a/components/ospcommon/common.h +++ b/components/ospcommon/common.h @@ -20,6 +20,7 @@ // std #include #include +#include // std::min etc on windows #ifdef _WIN32 // ----------- windows only ----------- @@ -68,7 +69,7 @@ typedef int ssize_t; #ifdef _WIN32 #define __PRETTY_FUNCTION__ __FUNCSIG__ #endif -#define NOTIMPLEMENTED throw std::runtime_error(std::string(__PRETTY_FUNCTION__)+": not implemented..."); +#define NOTIMPLEMENTED throw std::runtime_error(std::string(__PRETTY_FUNCTION__)+": not implemented..."); #define SCOPED_LOCK(x) \ std::lock_guard _lock(x); \ @@ -76,7 +77,7 @@ typedef int ssize_t; namespace ospcommon { - using byte_t = unsigned char; + typedef unsigned char byte_t; /*! return system time in seconds */ OSPCOMMON_INTERFACE double getSysTime(); diff --git a/components/ospcommon/containers/TransactionalBuffer.h b/components/ospcommon/containers/TransactionalBuffer.h new file mode 100644 index 0000000000..c3dbbba2a5 --- /dev/null +++ b/components/ospcommon/containers/TransactionalBuffer.h @@ -0,0 +1,88 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +namespace ospcommon { + + template + struct TransactionalBuffer + { + TransactionalBuffer() = default; + + // Insert into the buffer (producer) + void push_back(const T &); + void push_back(T &&); + + // Take all contents of the buffer (consumer) + std::vector consume(); + + size_t size() const; + + bool empty() const; + + private: + + // Data members // + + std::vector buffer; + + mutable std::mutex bufferMutex;//NOTE(jda) - Marked mutable so 'const' + // methods can take the lock... + }; + + // Inlined members ////////////////////////////////////////////////////////// + + template + inline void TransactionalBuffer::push_back(const T &v) + { + std::lock_guard lock(bufferMutex); + buffer.push_back(v); + } + + template + inline void TransactionalBuffer::push_back(T &&v) + { + std::lock_guard lock(bufferMutex); + buffer.push_back(std::forward(v)); + } + + template + inline std::vector TransactionalBuffer::consume() + { + std::lock_guard lock(bufferMutex); + return std::move(buffer); + } + + template + inline size_t TransactionalBuffer::size() const + { + std::lock_guard lock(bufferMutex); + return buffer.size(); + } + + template + inline bool TransactionalBuffer::empty() const + { + std::lock_guard lock(bufferMutex); + return buffer.empty(); + } + +} // ::ospcommon diff --git a/components/ospcommon/containers/test_TransactionalBuffer.cpp b/components/ospcommon/containers/test_TransactionalBuffer.cpp new file mode 100644 index 0000000000..ea661ae8f9 --- /dev/null +++ b/components/ospcommon/containers/test_TransactionalBuffer.cpp @@ -0,0 +1,32 @@ +#define CATCH_CONFIG_MAIN +#include "../testing/catch.hpp" + +#define private public +#include "TransactionalBuffer.h" +#undef private + +using ospcommon::TransactionalBuffer; + +// Tests ////////////////////////////////////////////////////////////////////// + +TEST_CASE("Interface Tests", "[all]") +{ + TransactionalBuffer tb; + + REQUIRE(tb.size() == 0); + REQUIRE(tb.empty()); + + tb.push_back(2); + + REQUIRE(tb.buffer.size() == 1); + REQUIRE(tb.size() == 1); + REQUIRE(!tb.empty()); + REQUIRE(tb.buffer[0] == 2); + + auto v = tb.consume(); + + REQUIRE(v.size() == 1); + REQUIRE(v[0] == 2); + + REQUIRE(tb.buffer.empty()); +} diff --git a/components/ospcommon/intrinsics.h b/components/ospcommon/intrinsics.h index 04491565c0..a6500f333f 100644 --- a/components/ospcommon/intrinsics.h +++ b/components/ospcommon/intrinsics.h @@ -39,7 +39,7 @@ namespace ospcommon { LARGE_INTEGER li; QueryPerformanceCounter(&li); - return li.QuadPart; + return (size_t)li.QuadPart; } //////////////////////////////////////////////////////////////////////////////// @@ -48,6 +48,8 @@ namespace ospcommon #else +#if defined(__i386__) && defined(__PIC__) + __forceinline void __cpuid(int out[4], int op) { asm volatile ("xchg{l}\t{%%}ebx, %1\n\t" @@ -66,6 +68,18 @@ namespace ospcommon : "0" (op1), "2" (op2)); } +#else + + __forceinline void __cpuid(int out[4], int op) { + asm volatile ("cpuid" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "a"(op)); + } + + __forceinline void __cpuid_count(int out[4], int op1, int op2) { + asm volatile ("cpuid" : "=a"(out[0]), "=b"(out[1]), "=c"(out[2]), "=d"(out[3]) : "a"(op1), "c"(op2)); + } + +#endif + __forceinline uint64_t read_tsc() { uint32_t high,low; asm volatile ("rdtsc" : "=d"(high), "=a"(low)); diff --git a/components/ospcommon/library.cpp b/components/ospcommon/library.cpp index 815091f015..6cacf6e76a 100644 --- a/components/ospcommon/library.cpp +++ b/components/ospcommon/library.cpp @@ -39,7 +39,7 @@ namespace ospcommon { std::string fullName = file+".dll"; lib = LoadLibrary(fullName.c_str()); #else -#if defined(__MACOSX__) +#if defined(__MACOSX__) || defined(__APPLE__) std::string fullName = "lib"+file+".dylib"; #else std::string fullName = "lib"+file+".so"; diff --git a/components/ospcommon/networking/BufferedDataStreaming.cpp b/components/ospcommon/networking/BufferedDataStreaming.cpp new file mode 100644 index 0000000000..672a389d0c --- /dev/null +++ b/components/ospcommon/networking/BufferedDataStreaming.cpp @@ -0,0 +1,94 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "BufferedDataStreaming.h" + +namespace ospcommon { + namespace networking { + + BufferedReadStream::BufferedReadStream(Fabric &fabric) + : fabric(fabric), buffer(nullptr), numAvailable(0) + { + } + + void BufferedReadStream::read(void *mem, size_t size) + { + uint8_t *writePtr = (uint8_t*)mem; + size_t numStillMissing = size; + + /*! note this code does not - ever - free the 'buffer' because + that buffer is internal to the fabric. it's the fabric that + returns this buffer, and that may or may not free it upon the + next 'read()' */ + while (numStillMissing > 0) { + // fill in what we already have + size_t numWeCanDeliver = std::min(numAvailable,numStillMissing); + if (numWeCanDeliver == 0) { + // read some more ... we HAVE to fulfill this 'read()' + // request, so have to read here + numAvailable = fabric.get().read((void*&)buffer); + continue; + } + + memcpy(writePtr,buffer,numWeCanDeliver); + numStillMissing -= numWeCanDeliver; + numAvailable -= numWeCanDeliver; + writePtr += numWeCanDeliver; + buffer += numWeCanDeliver; + } + } + + BufferedWriteStream::BufferedWriteStream(Fabric &fabric, + size_t maxBufferSize) + : fabric(fabric), + buffer(new byte_t[maxBufferSize]), + maxBufferSize(maxBufferSize), + numInBuffer(0) + { + } + + BufferedWriteStream::~BufferedWriteStream() + { + delete [] buffer; + } + + void BufferedWriteStream::write(void *mem, size_t size) + { + size_t stillToWrite = size; + uint8_t *readPtr = (uint8_t*)mem; + while (stillToWrite) { + size_t numWeCanWrite = std::min(stillToWrite, + size_t(maxBufferSize-numInBuffer)); + memcpy(buffer+numInBuffer,readPtr,numWeCanWrite); + + readPtr += numWeCanWrite; + numInBuffer += numWeCanWrite; + stillToWrite -= numWeCanWrite; + + if (numInBuffer == maxBufferSize) + flush(); + } + } + + void BufferedWriteStream::flush() + { + if (numInBuffer > 0) + fabric.get().send(buffer,numInBuffer); + numInBuffer = 0; + } + + } // ::ospcommon::networking +} // ::ospcommon diff --git a/components/ospcommon/networking/BufferedDataStreaming.h b/components/ospcommon/networking/BufferedDataStreaming.h new file mode 100644 index 0000000000..cb99fa4e04 --- /dev/null +++ b/components/ospcommon/networking/BufferedDataStreaming.h @@ -0,0 +1,65 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "DataStreaming.h" +#include "Fabric.h" + +namespace ospcommon { + namespace networking { + + /* read stream that serves smaller read requests (of a given + siez) from a block of data that it queries from a fabric. if + the internal buffer isn't big enough to fulfill the request, + te next block will automatically get read from the fabric */ + struct OSPCOMMON_INTERFACE BufferedReadStream : public ReadStream + { + BufferedReadStream(Fabric &fabric); + ~BufferedReadStream() = default; + + void read(void *mem, size_t size) override; + + private: + + std::reference_wrapper fabric; + ospcommon::byte_t *buffer; + size_t numAvailable; + }; + + /*! maintains an internal buffer of a given size, and buffers + asll write ops preferably into this buffer; this internal + buffer gets flushed either when the user explcitly calls + flush(), or when the maximum size of the buffer gets + reached */ + struct OSPCOMMON_INTERFACE BufferedWriteStream : public WriteStream + { + BufferedWriteStream(Fabric &fabric, size_t maxBufferSize = 1LL*1024*1024); + ~BufferedWriteStream(); + + void write(void *mem, size_t size) override; + void flush() override; + + private: + + std::reference_wrapper fabric; + ospcommon::byte_t *buffer; + size_t maxBufferSize; + size_t numInBuffer; + }; + + } // ::ospcommon::networking +} // ::ospcommon diff --git a/components/ospcommon/networking/DataStreaming.h b/components/ospcommon/networking/DataStreaming.h new file mode 100644 index 0000000000..f33f33fec9 --- /dev/null +++ b/components/ospcommon/networking/DataStreaming.h @@ -0,0 +1,108 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "../common.h" + +#include + +namespace ospcommon { + namespace networking { + + /*! abstraction of an object that we can serailize/write (raw) data into */ + struct WriteStream + { + virtual void write(void *mem, size_t size) = 0; + virtual void flush() {} + }; + + /*! abstraction of an object that we can read (raw) data from to + then de-serialize into work objects */ + struct ReadStream + { + virtual void read(void *mem, size_t size) = 0; + }; + + /*! generic stream operators into/out of streams, for raw data blocks */ + template + inline WriteStream &operator<<(WriteStream &buf, const T &rh) + { + buf.write((byte_t*)&rh, sizeof(T)); + return buf; + } + + template + inline ReadStream &operator>>(ReadStream &buf, T &rh) + { + buf.read((byte_t*)&rh, sizeof(T)); + return buf; + } + + /*! @{ stream operators into/out of read/write streams, for std::vectors + + \warning This code is often _expensive_ - if this, is for + example, a std::vector, then we'll do a buf.write for each + and every one of these vector entries. for cases like the, what + the caller _should_ do is to manually write first the size, and + then _all_ items in a single block. Still, it is good to have + this in here for correctness, so I'll leave it in here - the + alternative would be to explciitly 'throw' an execptoin in there + to prevent the user from using this slow-path in the first place. + Finally - last comment on this - we might actually want to do add + some templates + */ + template + inline WriteStream &operator<<(WriteStream &buf, const std::vector &rh) + { + buf << rh.size(); + for (const T &v : rh) + buf << v; + return buf; + } + + template + inline ReadStream &operator>>(ReadStream &buf, std::vector &rh) + { + size_t sz; + buf >> sz; + rh.resize(sz); + for (T &v : rh) + buf >> v; + return buf; + } + /*! @} */ + + /*! @{ serialize operations for strings */ + inline WriteStream &operator<<(WriteStream &buf, const std::string &rh) + { + buf << rh.size(); + buf.write((byte_t*)rh.c_str(), rh.size()); + return buf; + } + + inline ReadStream &operator>>(ReadStream &buf, std::string &rh) + { + size_t size; + buf >> size; + rh = std::string(size, ' '); + buf.read((byte_t*)rh.data(), size); + return buf; + } + /*! @} */ + + } // ::ospcommon::networking +} // ::ospcommon diff --git a/apps/glutViewer/GlutViewerScriptHandler.h b/components/ospcommon/networking/Fabric.h similarity index 66% rename from apps/glutViewer/GlutViewerScriptHandler.h rename to components/ospcommon/networking/Fabric.h index 0f43922b50..359c964d55 100644 --- a/apps/glutViewer/GlutViewerScriptHandler.h +++ b/components/ospcommon/networking/Fabric.h @@ -16,28 +16,24 @@ #pragma once -#include "common/script/OSPRayScriptHandler.h" - -namespace ospray { - - class ScriptedOSPGlutViewer; - - class GlutViewerScriptHandler : public OSPRayScriptHandler - { - public: - - GlutViewerScriptHandler(OSPModel model, - OSPRenderer renderer, - OSPCamera camera, - ScriptedOSPGlutViewer *viewer); - - private: - - void registerScriptFunctions(); - - // Data // - - ScriptedOSPGlutViewer *viewer; - }; - -}// namespace ospray +#include + +namespace ospcommon { + namespace networking { + + /*! abstraction for a physical fabric that can transmit data - + sockets, mpi, etc */ + struct Fabric + { + /*! send exact number of bytes - the fabric can do that through + multiple smaller messages, but all bytes have to be + delivered */ + virtual void send(void *mem, size_t s) = 0; + + /*! receive some block of data - whatever the sender has sent - + and give us size and pointer to this data */ + virtual size_t read(void *&mem) = 0; + }; + + } // ::ospcommon::networking +} // ::ospcommon diff --git a/components/ospcommon/networking/Socket.cpp b/components/ospcommon/networking/Socket.cpp new file mode 100644 index 0000000000..dd8a3d9711 --- /dev/null +++ b/components/ospcommon/networking/Socket.cpp @@ -0,0 +1,337 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "Socket.h" +#include + +//////////////////////////////////////////////////////////////////////////////// +/// Platforms supporting Socket interface +//////////////////////////////////////////////////////////////////////////////// + +#ifdef _WIN32 +#define _WINSOCK_DEPRECATED_NO_WARNINGS +//#include +//#include +typedef int socklen_t; +#define SHUT_RDWR 0x2 +#else +#include +#include +#include +#include +#include +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#define closesocket ::close +#endif + +/*! ignore if not supported */ +#ifndef MSG_NOSIGNAL +#define MSG_NOSIGNAL 0 +#endif + +#define BUFFERING 1 + +namespace ospcommon +{ + __forceinline void initialize() { +#ifdef _WIN32 + static bool initialized = false; + static std::mutex initMutex; + std::lock_guard lock(initMutex); + WSADATA wsaData; + short version = MAKEWORD(1,1); + if (WSAStartup(version,&wsaData) != 0) + THROW_RUNTIME_ERROR("Winsock initialization failed"); + initialized = true; +#endif + } + + struct buffered_socket_t + { + buffered_socket_t (SOCKET fd, size_t isize = 64*1024, size_t osize = 64*1024) + : fd(fd), + ibuf(new char[isize]), isize(isize), istart(0), iend(0), + obuf(new char[osize]), osize(osize), oend(0) { + } + + ~buffered_socket_t () { + delete[] ibuf; ibuf = nullptr; + delete[] obuf; obuf = nullptr; + } + + SOCKET fd; //!< file descriptor of the socket + char* ibuf; + size_t isize; + size_t istart,iend; + char* obuf; + size_t osize; + size_t oend; + }; + + struct AutoCloseSocket + { + SOCKET sock; + AutoCloseSocket (SOCKET sock) : sock(sock) {} + ~AutoCloseSocket () { + if (sock != INVALID_SOCKET) { + closesocket(sock); + } + } + }; + + socket_t connect(const char* host, unsigned short port) + { + initialize(); + + /*! create a new socket */ + SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket"); + AutoCloseSocket auto_close(sockfd); + + /*! perform DNS lookup */ + struct hostent* server = ::gethostbyname(host); + if (server == nullptr) THROW_RUNTIME_ERROR("server "+std::string(host)+" not found"); + + /*! perform connection */ + struct sockaddr_in serv_addr; + memset((char*)&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = (unsigned short) htons(port); + memcpy((char*)&serv_addr.sin_addr.s_addr, (char*)server->h_addr, server->h_length); + + if (::connect(sockfd,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) < 0) + THROW_RUNTIME_ERROR("connection to "+std::string(host)+":"+std::to_string((long long)port)+" failed"); + + /*! enable TCP_NODELAY */ +#ifdef TCP_NODELAY + { int flag = 1; ::setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (const char*)&flag, sizeof(int)); } +#endif + + /*! we do not want SIGPIPE to be thrown */ +#ifdef SO_NOSIGPIPE + { int flag = 1; setsockopt(sockfd, SOL_SOCKET, SO_NOSIGPIPE, (const char*) &flag, sizeof(int)); } +#endif + + auto_close.sock = INVALID_SOCKET; + return (socket_t) new buffered_socket_t(sockfd); + } + + socket_t bind(unsigned short port) + { + initialize(); + + /*! create a new socket */ + SOCKET sockfd = ::socket(AF_INET, SOCK_STREAM, 0); + if (sockfd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot create socket"); + AutoCloseSocket auto_close(sockfd); + + /* When the server completes, the server socket enters a time-wait state during which the local + address and port used by the socket are believed to be in use by the OS. The wait state may + last several minutes. This socket option allows bind() to reuse the port immediately. */ +#ifdef SO_REUSEADDR + { int flag = true; ::setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*)&flag, sizeof(int)); } +#endif + + /*! bind socket to port */ + struct sockaddr_in serv_addr; + memset((char *) &serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_port = (unsigned short) htons(port); + serv_addr.sin_addr.s_addr = INADDR_ANY; + + if (::bind(sockfd, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) < 0) + THROW_RUNTIME_ERROR("binding to port "+std::to_string((long long)port)+" failed"); + + /*! listen to port, up to 5 pending connections */ + if (::listen(sockfd,5) < 0) + THROW_RUNTIME_ERROR("listening on socket failed"); + + auto_close.sock = INVALID_SOCKET; + return (socket_t) new buffered_socket_t(sockfd); + } + + socket_t listen(socket_t hsock) + { + SOCKET sockfd = ((buffered_socket_t*) hsock)->fd; + + /*! accept incoming connection */ + struct sockaddr_in addr; + socklen_t len = sizeof(addr); + SOCKET fd = ::accept(sockfd, (struct sockaddr *) &addr, &len); + if (fd == INVALID_SOCKET) THROW_RUNTIME_ERROR("cannot accept connection"); + + /*! enable TCP_NODELAY */ +#ifdef TCP_NODELAY + { int flag = 1; ::setsockopt(fd,IPPROTO_TCP,TCP_NODELAY,(char*)&flag,sizeof(int)); } +#endif + + /*! we do not want SIGPIPE to be thrown */ +#ifdef SO_NOSIGPIPE + { int flag = 1; setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, (void*)&flag, sizeof(int)); } +#endif + + return (socket_t) new buffered_socket_t(fd); + } + + void read(socket_t hsock_i, void* data_i, size_t bytes) + { +#if BUFFERING + char* data = (char*)data_i; + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + while (bytes) { + if (hsock->istart == hsock->iend) { + ssize_t n = ::recv(hsock->fd,hsock->ibuf,hsock->isize,MSG_NOSIGNAL); + if (n == 0) throw Disconnect(); + else if (n < 0) THROW_RUNTIME_ERROR("error reading from socket"); + hsock->istart = 0; + hsock->iend = n; + } + size_t bsize = hsock->iend-hsock->istart; + if (bytes < bsize) bsize = bytes; + memcpy(data,hsock->ibuf+hsock->istart,bsize); + data += bsize; + hsock->istart += bsize; + bytes -= bsize; + } +#else + char* data = (char*) data_i; + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + while (bytes) { + ssize_t n = ::read(hsock->fd,data,bytes); + if (n == 0) throw Disconnect(); + else if (n < 0) THROW_RUNTIME_ERROR("error reading from socket"); + data+=n; + bytes-=n; + } +#endif + } + + void write(socket_t hsock_i, const void* data_i, size_t bytes) + { +#if BUFFERING + const char* data = (const char*) data_i; + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + while (bytes) { + if (hsock->oend == hsock->osize) flush(hsock_i); + size_t bsize = hsock->osize-hsock->oend; + if (bytes < bsize) bsize = bytes; + memcpy(hsock->obuf+hsock->oend,data,bsize); + data += bsize; + hsock->oend += bsize; + bytes -= bsize; + } +#else + const char* data = (const char*) data_i; + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + while (bytes) { + ssize_t n = ::write(hsock->fd,data,bytes); + if (n < 0) THROW_RUNTIME_ERROR("error writing to socket"); + data+=n; + bytes-=n; + } +#endif + } + + void flush(socket_t hsock_i) + { +#if BUFFERING + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + char* data = hsock->obuf; + size_t bytes = hsock->oend; + while (bytes > 0) { + ssize_t n = ::send(hsock->fd,data,(int)bytes,MSG_NOSIGNAL); + if (n < 0) THROW_RUNTIME_ERROR("error writing to socket"); + bytes -= n; + data += n; + } + hsock->oend = 0; +#endif + } + + void close(socket_t hsock_i) { + buffered_socket_t* hsock = (buffered_socket_t*) hsock_i; + ::shutdown(hsock->fd,SHUT_RDWR); + closesocket(hsock->fd); + delete hsock; + } + +//////////////////////////////////////////////////////////////////////////////// +/// All Platforms +//////////////////////////////////////////////////////////////////////////////// + + bool read_bool(socket_t socket) + { + bool value = 0; + read(socket,&value,sizeof(bool)); + return value; + } + + char read_char(socket_t socket) + { + char value = 0; + read(socket,&value,sizeof(char)); + return value; + } + + int read_int(socket_t socket) + { + int value = 0; + read(socket,&value,sizeof(int)); + return value; + } + + float read_float(socket_t socket) + { + float value = 0.0f; + read(socket,&value,sizeof(float)); + return value; + } + + std::string read_string(socket_t socket) + { + int bytes = read_int(socket); + char* str = new char[bytes+1]; + read(socket,str,bytes); + str[bytes] = 0x00; + std::string s(str); + delete[] str; + return s; + } + + void write(socket_t socket, bool value) { + write(socket,&value,sizeof(bool)); + } + + void write(socket_t socket, char value) { + write(socket,&value,sizeof(char)); + } + + void write(socket_t socket, int value) { + write(socket,&value,sizeof(int)); + } + + void write(socket_t socket, float value) { + write(socket,&value,sizeof(float)); + } + + void write(socket_t socket, const std::string& str) { + write(socket,(int)str.size()); + write(socket,str.c_str(),str.size()); + } +} diff --git a/components/ospcommon/networking/Socket.h b/components/ospcommon/networking/Socket.h new file mode 100644 index 0000000000..5386d1c7bc --- /dev/null +++ b/components/ospcommon/networking/Socket.h @@ -0,0 +1,87 @@ +// ======================================================================== // +// Copyright 2009-2017 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. // +// ======================================================================== // + +/*! iw: note this code is literally copied (and then adapted) from + embree's original common/sys/network.h */ + +#pragma once + +#include "../common.h" + +namespace ospcommon +{ + /*! type for a socket */ + using socket_t = struct opaque_socket_t*; + + /*! exception thrown when other side disconnects */ + struct Disconnect : public std::exception + { + virtual const char* what() const throw() override + { return "network disconnect"; } + }; + + /*! creates a socket bound to a port */ + OSPCOMMON_INTERFACE socket_t bind(unsigned short port); + + /*! listens for an incoming connection and accepts that connection */ + OSPCOMMON_INTERFACE socket_t listen(socket_t sockfd); + + /*! initiates a connection */ + OSPCOMMON_INTERFACE socket_t connect(const char* host, unsigned short port); + + /*! read data from the socket */ + OSPCOMMON_INTERFACE void read(socket_t socket, void* data, size_t bytes); + + /*! write data to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, const void* data, size_t bytes); + + /*! flushes the write buffer */ + OSPCOMMON_INTERFACE void flush(socket_t socket); + + /*! close a socket */ + OSPCOMMON_INTERFACE void close(socket_t socket); + + /*! reads a bool from the socket */ + OSPCOMMON_INTERFACE bool read_bool(socket_t socket); + + /*! reads a character from the socket */ + OSPCOMMON_INTERFACE char read_char(socket_t socket); + + /*! reads an integer from the socket */ + OSPCOMMON_INTERFACE int read_int(socket_t socket); + + /*! reads a float from the socket */ + OSPCOMMON_INTERFACE float read_float(socket_t socket); + + /*! reads a string from the socket */ + OSPCOMMON_INTERFACE std::string read_string(socket_t socket); + + /*! writes a bool to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, bool value); + + /*! writes a character to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, char value); + + /*! writes an integer to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, int value); + + /*! writes a float to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, float value); + + /*! writes a string to the socket */ + OSPCOMMON_INTERFACE void write(socket_t socket, const std::string& str); + +}// ::ospcommon diff --git a/components/ospcommon/platform.h b/components/ospcommon/platform.h index 7bdda5b729..ba626e3082 100644 --- a/components/ospcommon/platform.h +++ b/components/ospcommon/platform.h @@ -88,7 +88,8 @@ /* debug printing macros */ #define STRING(x) #x #define TOSTRING(x) STRING(x) -#define PING std::cout << __FILE__ << " (" << __LINE__ << "): " << __FUNCTION__ << std::endl +#define CODE_LOCATION __FILE__ " (" TOSTRING(__LINE__) ")" +#define PING std::cout << CODE_LOCATION << ": " << __FUNCTION__ << std::endl #define PRINT(x) std::cout << STRING(x) << " = " << (x) << std::endl #define PRINT2(x,y) std::cout << STRING(x) << " = " << (x) << ", " << STRING(y) << " = " << (y) << std::endl #define PRINT3(x,y,z) std::cout << STRING(x) << " = " << (x) << ", " << STRING(y) << " = " << (y) << ", " << STRING(z) << " = " << (z) << std::endl diff --git a/components/ospcommon/sysinfo.cpp b/components/ospcommon/sysinfo.cpp index a06b0be023..c99b9b6409 100644 --- a/components/ospcommon/sysinfo.cpp +++ b/components/ospcommon/sysinfo.cpp @@ -300,6 +300,7 @@ namespace ospcommon if (isa == AVX2) return "AVX2"; if (isa == KNC) return "KNC"; if (isa == AVX512KNL) return "AVX512KNL"; + if (isa == AVX512SKX) return "AVX512SKX"; return "UNKNOWN"; } } diff --git a/components/ospcommon/sysinfo.h b/components/ospcommon/sysinfo.h index c077867a4e..c744273984 100644 --- a/components/ospcommon/sysinfo.h +++ b/components/ospcommon/sysinfo.h @@ -101,8 +101,8 @@ namespace ospcommon static const int AVXI = AVX | CPU_FEATURE_F16C | CPU_FEATURE_RDRAND; static const int AVX2 = AVXI | CPU_FEATURE_AVX2 | CPU_FEATURE_FMA3 | CPU_FEATURE_BMI1 | CPU_FEATURE_BMI2 | CPU_FEATURE_LZCNT; static const int KNC = CPU_FEATURE_KNC; - static const int AVX512F = AVX2 | CPU_FEATURE_AVX512F; // FIXME: shouldn't we also test for the CPU_FEATURE_AVX512VL flag? - static const int AVX512KNL = AVX512F | CPU_FEATURE_AVX512PF | CPU_FEATURE_AVX512ER | CPU_FEATURE_AVX512CD; + static const int AVX512KNL = AVX2 | CPU_FEATURE_AVX512F | CPU_FEATURE_AVX512PF | CPU_FEATURE_AVX512ER | CPU_FEATURE_AVX512CD; + static const int AVX512SKX = AVX2 | CPU_FEATURE_AVX512F | CPU_FEATURE_AVX512DQ | CPU_FEATURE_AVX512CD | CPU_FEATURE_AVX512BW | CPU_FEATURE_AVX512VL; /*! converts ISA bitvector into a string */ OSPCOMMON_INTERFACE std::string stringOfISA(int features); diff --git a/components/ospcommon/tasking/TaskSys.cpp b/components/ospcommon/tasking/TaskSys.cpp index e21939174a..121d5550df 100644 --- a/components/ospcommon/tasking/TaskSys.cpp +++ b/components/ospcommon/tasking/TaskSys.cpp @@ -16,194 +16,203 @@ #include "TaskSys.h" //ospray -#include "../sysinfo.h" -#include "../thread.h" +#include "../platform.h" //stl -#include #include #include namespace ospcommon { - - struct TaskSys { - bool initialized {false}; - bool running {false}; - - void init(size_t maxNumRenderTasks); - static TaskSys global; - static void *threadStub(void *); - inline Ref getNextActiveTask(); - - //! Queue of tasks that have ALREADY been acitvated, and that are ready - //! to run - __aligned(64) Task *volatile activeListFirst {nullptr}; - __aligned(64) Task *volatile activeListLast {nullptr}; - - std::mutex __aligned(64) mutex; - std::condition_variable __aligned(64) tasksAvailable; - - void threadFunction(); - - std::vector threads; - - ~TaskSys(); - }; - - TaskSys __aligned(64) TaskSys::global; - - inline void Task::workOnIt() - { - size_t myCompleted = 0; - while (1) { - const size_t thisJobID = numJobsStarted++; - if (thisJobID >= numJobsInTask) - break; - - run(thisJobID); - ++myCompleted; + namespace tasking { + + struct TaskSys + { + TaskSys(); + ~TaskSys(); + + void init(int maxNumRenderTasks); + void createWorkerThreads(int numThreads); + Task *getNextActiveTask(); + void shutdownWorkerThreads(); + + // Data members // + + bool initialized {false}; + bool running {false}; + + static TaskSys global; + + //! Queue of tasks that have ALREADY been acitvated, which are ready + __aligned(64) Task *volatile activeListFirst {nullptr}; + __aligned(64) Task *volatile activeListLast {nullptr}; + + std::mutex __aligned(64) mutex; + std::condition_variable __aligned(64) tasksAvailable; + + std::vector threads; + }; + + // Task definitions /////////////////////////////////////////////////////// + + Task::Task() + : numJobsCompleted(), + numJobsStarted() + { } - if (myCompleted != 0) { - const size_t nowCompleted = (numJobsCompleted += myCompleted); - if (nowCompleted == numJobsInTask) { - SCOPED_LOCK(mutex); - status = Task::COMPLETED; - allJobsCompletedCond.notify_all(); + void Task::workOnIt() + { + int myCompleted = 0; + + while (true) { + const int thisJobID = numJobsStarted++; + + if (thisJobID >= numJobsInTask) + break; + + run(thisJobID); + ++myCompleted; + } + + if (myCompleted != 0) { + const int nowCompleted = (numJobsCompleted += myCompleted); + if (nowCompleted == numJobsInTask) { + SCOPED_LOCK(mutex); + status = Task::COMPLETED; + allJobsCompletedCond.notify_all(); + } } } - } - - void Task::wait(bool workOnIt) - { - if (status == Task::COMPLETED) { - return; - } - if (workOnIt) { + void Task::wait() + { + if (status == Task::COMPLETED) + return; + this->workOnIt(); + + std::unique_lock lock(mutex); + allJobsCompletedCond.wait(lock, [&](){return status == Task::COMPLETED;}); } - std::unique_lock lock(mutex); - allJobsCompletedCond.wait(lock, [&](){return status == Task::COMPLETED;}); - } + // TaskSys definitions //////////////////////////////////////////////////// - void Task::scheduleAndWait(size_t numJobs, ScheduleOrder order) - { - schedule(numJobs,order); - wait(); - } + TaskSys __aligned(64) TaskSys::global; - inline Ref TaskSys::getNextActiveTask() - { - while (1) { - std::unique_lock lock(mutex); - tasksAvailable.wait(lock, [&](){ - return !(activeListFirst == nullptr && running); - }); + inline TaskSys::TaskSys() + { + #ifdef OSPRAY_TASKING_INTERNAL + init(-1); + #endif + } + + inline TaskSys::~TaskSys() + { + shutdownWorkerThreads(); + } + + inline void TaskSys::init(int numThreads) + { + if (initialized) + shutdownWorkerThreads(); - if (!running) { - return nullptr; + initialized = true; + running = true; + + if (numThreads >= 0) { + numThreads = std::min(numThreads, + (int)std::thread::hardware_concurrency()); + } else { + numThreads = (int)std::thread::hardware_concurrency(); } - Ref front = activeListFirst; - if (front->numJobsStarted >= int(front->numJobsInTask)) { - if (activeListFirst == activeListLast) { - activeListFirst = activeListLast = nullptr; - } else { - activeListFirst = activeListFirst->next; - } - continue; + createWorkerThreads(numThreads); + } + + inline void TaskSys::createWorkerThreads(int numThreads) + { + for (int t = 1; t < numThreads; t++) { + threads.emplace_back([&](){ + while (true) { + Task *task = getNextActiveTask(); + + if (!running) + break; + + task->workOnIt(); + } + }); } - assert(front.ptr); - return front; } - } - - void Task::initTaskSystem(const size_t maxNumRenderTasks) - { - TaskSys::global.init(maxNumRenderTasks); - } - - void Task::schedule(size_t numJobs, ScheduleOrder order) - { - refInc(); - this->order = order; - numJobsInTask = numJobs; - status = Task::SCHEDULED; - if (numMissingDependencies == 0) - activate(); - } - - inline void Task::activate() - { - if (!TaskSys::global.initialized) - throw std::runtime_error("TASK SYSTEM NOT YET INITIALIZED"); + + inline Task *TaskSys::getNextActiveTask() { - SCOPED_LOCK(TaskSys::global.mutex); - bool wasEmpty = TaskSys::global.activeListFirst == nullptr; - if (wasEmpty) { - TaskSys::global.activeListFirst = TaskSys::global.activeListLast = this; - this->next = nullptr; - TaskSys::global.tasksAvailable.notify_all(); - } else { - if (order == Task::BACK_OF_QUEUE) { - this->next = nullptr; - TaskSys::global.activeListLast->next = this; - TaskSys::global.activeListLast = this; - } else { - this->next = TaskSys::global.activeListFirst; - TaskSys::global.activeListFirst = this; + while (true) { + std::unique_lock lock(mutex); + tasksAvailable.wait(lock, [&](){ + return !(activeListFirst == nullptr && running); + }); + + if (!running) + return nullptr; + + Task *front = activeListFirst; + if (front->numJobsStarted >= front->numJobsInTask) { + if (activeListFirst == activeListLast) + activeListFirst = activeListLast = nullptr; + else + activeListFirst = activeListFirst->next; + + continue; } + + return front; } - status = Task::ACTIVE; } - } - void TaskSys::threadFunction() - { - while (1) { - Ref task = getNextActiveTask(); - if (!running) { - return; - } - assert(task); - task->workOnIt(); + inline void TaskSys::shutdownWorkerThreads() + { + running = false; + tasksAvailable.notify_all(); + for (auto &thread : threads) + thread.join(); + threads.clear(); } - } - - TaskSys::~TaskSys() - { - running = false; - tasksAvailable.notify_all(); - for (auto &thread : threads) { - join(thread); + + // Interface definitions ////////////////////////////////////////////////// + + void initTaskSystemInternal(int maxNumRenderTasks) + { + TaskSys::global.init(maxNumRenderTasks); } - } - void *TaskSys::threadStub(void *) - { - TaskSys::global.threadFunction(); - return nullptr; - } + void scheduleTaskInternal(std::shared_ptr task, + int numJobs, + ScheduleOrder order) + { + task->numJobsInTask = numJobs; + task->status = Task::SCHEDULED; - void TaskSys::init(size_t numThreads) - { - if (initialized) - throw std::runtime_error("#osp: task system initialized twice!"); + auto *t_ptr = task.get(); - initialized = true; - running = true; + SCOPED_LOCK(TaskSys::global.mutex); + bool wasEmpty = TaskSys::global.activeListFirst == nullptr; + if (wasEmpty) { + TaskSys::global.activeListFirst = TaskSys::global.activeListLast = t_ptr; + task->next = nullptr; + TaskSys::global.tasksAvailable.notify_all(); + } else { + if (order == BACK_OF_QUEUE) { + task->next = nullptr; + TaskSys::global.activeListLast->next = t_ptr; + TaskSys::global.activeListLast = t_ptr; + } else { + task->next = TaskSys::global.activeListFirst; + TaskSys::global.activeListFirst = t_ptr; + } + } - if (numThreads != 0) { - numThreads = std::min(numThreads, - (size_t)std::thread::hardware_concurrency()); + task->status = Task::ACTIVE; } - /* generate all threads */ - for (size_t t = 1; t < numThreads; t++) { - threads.push_back(createThread((thread_func)TaskSys::threadStub, - (void*)-1,4*1024*1024,-1)); - } - } - + } // ::ospcommon::tasking } // ::ospcommon diff --git a/components/ospcommon/tasking/TaskSys.h b/components/ospcommon/tasking/TaskSys.h index a06be89366..48a4276f56 100644 --- a/components/ospcommon/tasking/TaskSys.h +++ b/components/ospcommon/tasking/TaskSys.h @@ -16,25 +16,17 @@ #pragma once -#include "../platform.h" -#include "../sysinfo.h" -#include "../RefCount.h" -// std -#include +#include "../common.h" +// stl +#include #include +#include namespace ospcommon { + namespace tasking { - struct OSPCOMMON_INTERFACE __aligned(64) Task : public RefCount { - - Task(const char *name = "no name"); - virtual ~Task(); - - // ------------------------------------------------------------------ - // interface for scheduling a new task into the task system - // ------------------------------------------------------------------ - - typedef enum { + enum ScheduleOrder + { /*! schedule job to the END of the job queue, meaning it'll get pulled only after all the ones already in the queue */ BACK_OF_QUEUE, @@ -42,92 +34,92 @@ namespace ospcommon { get processed even before other jobs that are already in the queue */ FRONT_OF_QUEUE - } ScheduleOrder; - - /*! the order in the queue that this job will get scheduled when - * activated */ - ScheduleOrder order; - - //! schedule the given task with the given number of sub-jobs. - void schedule(size_t numJobs, ScheduleOrder order=BACK_OF_QUEUE); - - //! same as schedule(), but also wait for all jobs to complete - void scheduleAndWait(size_t numJobs, ScheduleOrder order=BACK_OF_QUEUE); - - //! wait for the task to complete, optionally (by default) helping - //! to actually work on completing this task. - void wait(bool workOnIt = true); - - //! get name of the task (useful for debugging) - const char *getName(); - - /*! \brief initialize the task system with given number of worker - tasks. - - numThreads==-1 means 'use all that are available; numThreads=0 - means 'no worker thread, assume that whoever calls wait() will - do the work */ - static void initTaskSystem(const size_t numThreads); + }; - private: + struct OSPCOMMON_INTERFACE __aligned(64) Task + { + Task(); + virtual ~Task() = default; - //! Allow tasking system backend to access all parts of the class, but - //! prevent users from using data which is an implementation detail of the - //! task - friend struct TaskSys; + // ------------------------------------------------------------------ + // interface for scheduling a new task into the task system + // ------------------------------------------------------------------ - // ------------------------------------------------------------------ - // callback used to define what the task is doing - // ------------------------------------------------------------------ + //! wait for the task to complete, optionally (by default) helping + //! to actually work on completing this task. + void wait(); - virtual void run(size_t jobID) = 0; + // ------------------------------------------------------------------ + // callback used to define what the task is doing + // ------------------------------------------------------------------ - // ------------------------------------------------------------------ - // internal data for the tasking systme to manage the task - // ------------------------------------------------------------------ + virtual void run(int jobID) = 0; - //*! work on task until no more useful job available on this task - void workOnIt(); + // ------------------------------------------------------------------ + // internal data for the tasking systme to manage the task + // ------------------------------------------------------------------ - //! activate job, and insert into the task system. should never be - //! called by the user, only by the task(system) whenever the task - //! is a) scheduled and b) all dependencies are fulfilled - void activate(); + //! work on task until no more useful job available on this task + void workOnIt(); - __aligned(64) std::atomic_int numJobsCompleted; - __aligned(64) std::atomic_int numJobsStarted; - size_t numJobsInTask; + // Data members // - typedef enum { INITIALIZING, SCHEDULED, ACTIVE, COMPLETED } Status; - std::mutex __aligned(64) mutex; - Status volatile __aligned(64) status; - std::atomic_int __aligned(64) numMissingDependencies; - std::condition_variable __aligned(64) allDependenciesFulfilledCond; - std::condition_variable __aligned(64) allJobsCompletedCond; + __aligned(64) std::atomic_int numJobsCompleted; + __aligned(64) std::atomic_int numJobsStarted; + int numJobsInTask {0}; - __aligned(64) Task *volatile next; - const char *name; - }; + enum Status { INITIALIZING, SCHEDULED, ACTIVE, COMPLETED }; + std::mutex __aligned(64) mutex; + Status volatile __aligned(64) status {INITIALIZING}; + std::condition_variable __aligned(64) allDependenciesFulfilledCond; + std::condition_variable __aligned(64) allJobsCompletedCond; -// Inlined function definitions /////////////////////////////////////////////// + __aligned(64) Task *volatile next; + }; - __forceinline Task::Task(const char *name) - : numJobsCompleted(), - numJobsStarted(), - numJobsInTask(0), - status(Task::INITIALIZING), - numMissingDependencies(), - name(name) - { - } + // Public interface to the tasking system ///////////////////////////////// - __forceinline Task::~Task() - { - } + /*! \brief initialize the task system with given number of worker + tasks. - __forceinline const char *Task::getName() - { - return name; - } + numThreads==-1 means 'use all that are available; numThreads=0 + means 'no worker thread, assume that whoever calls wait() will + do the work */ + void OSPCOMMON_INTERFACE initTaskSystemInternal(int numThreads = -1); + //! schedule the given task with the given number of sub-jobs. + void scheduleTaskInternal(std::shared_ptr task, + int numJobs, + ScheduleOrder order = BACK_OF_QUEUE); + + template + inline void parallel_for_internal(int nTasks, TASK_T && fcn) + { + struct LocalTask : public Task + { + const TASK_T &t; + LocalTask(TASK_T&& fcn) : t(std::forward(fcn)) {} + void run(int taskIndex) override { t(taskIndex); } + }; + + auto task = std::make_shared(std::forward(fcn)); + scheduleTaskInternal(task, nTasks); + task->wait(); + } + + template + inline void schedule_internal(TASK_T && fcn) + { + struct LocalTask : public Task + { + TASK_T t; + LocalTask(TASK_T&& fcn) : t(std::forward(fcn)) {} + void run(int) override { t(); } + }; + + auto task = std::make_shared(std::forward(fcn)); + scheduleTaskInternal(task, 1, FRONT_OF_QUEUE); + } + + } // ::ospcommon::tasking } // ::ospcommon diff --git a/components/ospcommon/tasking/TaskingTypeTraits.h b/components/ospcommon/tasking/TaskingTypeTraits.h index a82151e0e9..0e13c6b447 100644 --- a/components/ospcommon/tasking/TaskingTypeTraits.h +++ b/components/ospcommon/tasking/TaskingTypeTraits.h @@ -21,49 +21,5 @@ namespace ospcommon { - //NOTE(jda) - This checks at compile time if T implements the method - // 'void T::operator()'. - template - struct has_operator_method - { - template class checker; - - template - static std::true_type test(checker *); - - template - static std::false_type test(...); - - using type = decltype(test(nullptr)); - static const bool value = std::is_same::value; - }; - - //NOTE(jda) - This checks at compile time if T implements the method - // 'void T::operator(P taskIndex)', where P is an integral type - // (must be short, int, uint, or size_t) at compile-time. To be used - // inside a static_assert(). - template - struct has_operator_method_with_integral_param - { - template - using t_param = void(T::*)(P) const; - using byte_t = unsigned char; - using operator_t = decltype(&T::operator()); - - using param_is_byte = std::is_same , operator_t>; - using param_is_short = std::is_same , operator_t>; - using param_is_int = std::is_same , operator_t>; - using param_is_unsigned = std::is_same, operator_t>; - using param_is_long = std::is_same , operator_t>; - using param_is_size_t = std::is_same , operator_t>; - - static const bool value = has_operator_method::value && - (param_is_byte::value || - param_is_short::value || - param_is_int::value || - param_is_unsigned::value || - param_is_long::value || - param_is_size_t::value); - }; } // ::ospcommon diff --git a/components/ospcommon/tasking/async.h b/components/ospcommon/tasking/async.h index a9fed32d2f..013d0a8d87 100644 --- a/components/ospcommon/tasking/async.h +++ b/components/ospcommon/tasking/async.h @@ -22,29 +22,31 @@ #include "schedule.h" namespace ospcommon { + namespace tasking { - template - using operator_return_t = typename std::result_of::type; + template + using operator_return_t = typename std::result_of::type; - // NOTE(jda) - This abstraction takes a lambda which should take captured - // variables by *value* to ensure no captured references race - // with the task itself. - template - inline auto async(TASK_T&& fcn) -> std::future> - { - static_assert(has_operator_method::value, - "ospcommon::schedule() requires the implementation of method " - "'RETURN_T TASK_T::operator()', where RETURN_T is the " - "return value of the passed in task."); + // NOTE(jda) - This abstraction takes a lambda which should take captured + // variables by *value* to ensure no captured references race + // with the task itself. + template + inline auto async(TASK_T&& fcn) -> std::future> + { + static_assert(traits::has_operator_method::value, + "ospcommon::async() requires the implementation of method " + "'RETURN_T TASK_T::operator()', where RETURN_T is the " + "return value of the passed in task."); - using package_t = std::packaged_task()>; + using package_t = std::packaged_task()>; - auto task = new package_t(std::forward(fcn)); - auto future = task->get_future(); + auto task = new package_t(std::forward(fcn)); + auto future = task->get_future(); - schedule([=](){ (*task)(); delete task; }); + schedule([=](){ (*task)(); delete task; }); - return future; - } + return future; + } + } // ::ospcommon::tasking } // ::ospcommon diff --git a/components/ospcommon/tasking/parallel_for.h b/components/ospcommon/tasking/parallel_for.h index 2e14ef0bae..3f05f55c10 100644 --- a/components/ospcommon/tasking/parallel_for.h +++ b/components/ospcommon/tasking/parallel_for.h @@ -16,37 +16,41 @@ #pragma once -#include "TaskingTypeTraits.h" +#include "../TypeTraits.h" #include "parallel_for.inl" namespace ospcommon { + namespace tasking { + + // NOTE(jda) - This abstraction wraps "fork-join" parallelism, with an + // implied synchronizsation after all of the tasks have run. + template + inline void parallel_for(int nTasks, TASK_T&& fcn) + { + using namespace traits; + static_assert(has_operator_method_with_integral_param::value, + "ospcommon::parallel_for() requires the implementation of " + "method 'void TASK_T::operator(P taskIndex), where P is of " + "type unsigned char, short, int, uint, long, or size_t."); + + parallel_for_impl(nTasks, std::forward(fcn)); + } - // NOTE(jda) - This abstraction wraps "fork-join" parallelism, with an implied - // synchronizsation after all of the tasks have run. - template - inline void parallel_for(int nTasks, TASK_T&& fcn) - { - static_assert(has_operator_method_with_integral_param::value, - "ospcommon::parallel_for() requires the implementation of " - "method 'void TASK_T::operator(P taskIndex), where P is of " - "type unsigned char, short, int, uint, long, or size_t."); - - parallel_for_impl(nTasks, std::forward(fcn)); - } - - // NOTE(jda) - Allow serial version of parallel_for() without the need to - // change the entire tasking system backend - template - inline void serial_for(int nTasks, const TASK_T& fcn) - { - static_assert(has_operator_method_with_integral_param::value, - "ospcommon::serial_for() requires the implementation of " - "method 'void TASK_T::operator(P taskIndex), where P is of " - "type unsigned char, short, int, uint, long, or size_t."); - - for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { - fcn(taskIndex); + // NOTE(jda) - Allow serial version of parallel_for() without the need to + // change the entire tasking system backend + template + inline void serial_for(int nTasks, const TASK_T& fcn) + { + using namespace traits; + static_assert(has_operator_method_with_integral_param::value, + "ospcommon::serial_for() requires the implementation of " + "method 'void TASK_T::operator(P taskIndex), where P is of " + "type unsigned char, short, int, uint, long, or size_t."); + + for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { + fcn(taskIndex); + } } - } + } // ::ospcommon::tasking } //::ospcommon diff --git a/components/ospcommon/tasking/parallel_for.inl b/components/ospcommon/tasking/parallel_for.inl index 6c62436b81..535d5efddf 100644 --- a/components/ospcommon/tasking/parallel_for.inl +++ b/components/ospcommon/tasking/parallel_for.inl @@ -24,38 +24,50 @@ # include #elif defined(OSPRAY_TASKING_INTERNAL) # include "ospcommon/tasking/TaskSys.h" +#elif defined(OSPRAY_TASKING_LIBDISPATCH) +# include "dispatch/dispatch.h" #endif namespace ospcommon { + namespace tasking { - template - inline void parallel_for_impl(int nTasks, TASK_T&& fcn) - { +#if defined(OSPRAY_TASKING_LIBDISPATCH) + template + inline void callFcn_T(void *_task, size_t taskIndex) + { + auto &task = *((TASK_T*)_task); + task(taskIndex); + } +#endif + + template + inline void parallel_for_impl(int nTasks, TASK_T&& fcn) + { #ifdef OSPRAY_TASKING_TBB - tbb::parallel_for(0, nTasks, 1, fcn); + tbb::parallel_for(0, nTasks, 1, std::forward(fcn)); #elif defined(OSPRAY_TASKING_CILK) - cilk_for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { - fcn(taskIndex); - } + cilk_for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { + fcn(taskIndex); + } #elif defined(OSPRAY_TASKING_OMP) -# pragma omp parallel for schedule(dynamic) - for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { - fcn(taskIndex); - } +# pragma omp parallel for schedule(dynamic) + for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { + fcn(taskIndex); + } #elif defined(OSPRAY_TASKING_INTERNAL) - struct LocalTask : public Task { - const TASK_T &t; - LocalTask(const TASK_T& fcn) : Task("LocalTask"), t(fcn) {} - void run(size_t taskIndex) override { t(taskIndex); } - }; - - Ref task = new LocalTask(fcn); - task->scheduleAndWait(nTasks); + parallel_for_internal(nTasks, std::forward(fcn)); +#elif defined(OSPRAY_TASKING_LIBDISPATCH) + dispatch_apply_f(nTasks, + dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, + 0), + &fcn, + &callFcn_T); #else // Debug (no tasking system) - for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { - fcn(taskIndex); - } + for (int taskIndex = 0; taskIndex < nTasks; ++taskIndex) { + fcn(taskIndex); + } #endif - } + } -} //::ospcommon + } // ::ospcommon::tasking +} // ::ospcommon diff --git a/components/ospcommon/tasking/schedule.h b/components/ospcommon/tasking/schedule.h index 730d1ea49b..725614dc58 100644 --- a/components/ospcommon/tasking/schedule.h +++ b/components/ospcommon/tasking/schedule.h @@ -16,26 +16,28 @@ #pragma once -#include "TaskingTypeTraits.h" +#include "../TypeTraits.h" #include "schedule.inl" namespace ospcommon { + namespace tasking { - // NOTE(jda) - This abstraction takes a lambda which should take captured - // variables by *value* to ensure no captured references race - // with the task itself. + // NOTE(jda) - This abstraction takes a lambda which should take captured + // variables by *value* to ensure no captured references race + // with the task itself. - // NOTE(jda) - No priority is associated with this call, but could be added - // later with a hint enum, using a default value for the priority - // to not require specifying it. - template - inline void schedule(TASK_T&& fcn) - { - static_assert(has_operator_method::value, - "ospcommon::schedule() requires the implementation of method " - "'void TASK_T::operator()'."); + // NOTE(jda) - No priority is associated with this call, but could be added + // later with a hint enum, using a default value for the + // priority to not require specifying it. + template + inline void schedule(TASK_T&& fcn) + { + static_assert(traits::has_operator_method::value, + "ospcommon::schedule() requires the implementation of " + "method 'void TASK_T::operator()'."); - schedule_impl(std::forward(fcn)); - } + schedule_impl(std::forward(fcn)); + } + } // ::ospcommon::tasking } // ::ospcommon diff --git a/components/ospcommon/tasking/schedule.inl b/components/ospcommon/tasking/schedule.inl index 0ca694758a..7bbabb8fb6 100644 --- a/components/ospcommon/tasking/schedule.inl +++ b/components/ospcommon/tasking/schedule.inl @@ -16,6 +16,8 @@ #pragma once +#include + #ifdef OSPRAY_TASKING_TBB # include #elif defined(OSPRAY_TASKING_CILK) @@ -25,36 +27,30 @@ #endif namespace ospcommon { + namespace tasking { - template - inline void schedule_impl(TASK_T&& fcn) - { -#ifdef OSPRAY_TASKING_TBB - struct LocalTBBTask : public tbb::task + template + inline void schedule_impl(TASK_T&& fcn) { - TASK_T func; - tbb::task* execute() override { func(); return nullptr; } - LocalTBBTask(TASK_T&& f) : func(std::forward(f)) {} - }; +#ifdef OSPRAY_TASKING_TBB + struct LocalTBBTask : public tbb::task + { + TASK_T func; + tbb::task* execute() override { func(); return nullptr; } + LocalTBBTask(TASK_T&& f) : func(std::forward(f)) {} + }; - auto *tbb_node = + auto *tbb_node = new(tbb::task::allocate_root())LocalTBBTask(std::forward(fcn)); - tbb::task::enqueue(*tbb_node); + tbb::task::enqueue(*tbb_node); #elif defined(OSPRAY_TASKING_CILK) - cilk_spawn fcn(); + cilk_spawn fcn(); #elif defined(OSPRAY_TASKING_INTERNAL) - struct LocalTask : public Task { - TASK_T t; - LocalTask(TASK_T&& fcn) - : Task("LocalTask"), t(std::forward(fcn)) {} - void run(size_t) override { t(); } - }; - - Ref task = new LocalTask(std::forward(fcn)); - task->schedule(1, Task::FRONT_OF_QUEUE); + schedule_internal(std::forward(fcn)); #else// OpenMP or Debug --> synchronous! - fcn(); + fcn(); #endif - } + } + } // ::ospcommon::tasking } // ::ospcommon diff --git a/components/ospcommon/tasking/tasking_system_handle.cpp b/components/ospcommon/tasking/tasking_system_handle.cpp index 0d115369f0..c76a63edd3 100644 --- a/components/ospcommon/tasking/tasking_system_handle.cpp +++ b/components/ospcommon/tasking/tasking_system_handle.cpp @@ -27,55 +27,67 @@ # include "TaskSys.h" #endif +#include + #include "../intrinsics.h" #include "../common.h" namespace ospcommon { + namespace tasking { - struct tasking_system_handle - { - tasking_system_handle(int numThreads) : - numThreads(numThreads) + struct tasking_system_handle + { + tasking_system_handle(int numThreads) : + numThreads(numThreads) #if defined(OSPRAY_TASKING_TBB) - , tbb_init(numThreads) + , tbb_init(numThreads) #endif - { + { #if defined(OSPRAY_TASKING_CILK) - __cilkrts_set_param("nworkers", std::to_string(numThreads).c_str()); + __cilkrts_set_param("nworkers", std::to_string(numThreads).c_str()); #elif defined(OSPRAY_TASKING_OMP) - if (numThreads > 0) omp_set_num_threads(numThreads); + if (numThreads > 0) omp_set_num_threads(numThreads); #elif defined(OSPRAY_TASKING_INTERNAL) - try { - Task::initTaskSystem(numThreads < 0 ? -1 : numThreads); - } catch (const std::runtime_error &e) { - std::cerr << "WARNING: " << e.what() << std::endl; - } + initTaskSystemInternal(numThreads < 0 ? -1 : numThreads); #endif - } + } - int numThreads {-1}; + int numThreads {-1}; #if defined(OSPRAY_TASKING_TBB) - tbb::task_scheduler_init tbb_init; + tbb::task_scheduler_init tbb_init; #endif - }; + }; - static std::unique_ptr g_tasking_handle; + static std::unique_ptr g_tasking_handle; - void initTaskingSystem(int numThreads) - { - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); - _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); + void initTaskingSystem(int numThreads) + { + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); #if defined(OSPRAY_TASKING_TBB) - if (!g_tasking_handle.get()) + if (!g_tasking_handle.get()) + g_tasking_handle = make_unique(numThreads); + else { + g_tasking_handle->tbb_init.terminate(); + g_tasking_handle->tbb_init.initialize(numThreads); + } +#else g_tasking_handle = make_unique(numThreads); - else { - g_tasking_handle->tbb_init.terminate(); - g_tasking_handle->tbb_init.initialize(numThreads); +#endif } -#else - g_tasking_handle = make_unique(numThreads); + + void deAffinitizeCores() + { +#ifdef __linux__ + cpu_set_t validCores; + CPU_ZERO(&validCores); + for (int i=0;i form +// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11 + +#ifdef __cplusplus + +# if __cplusplus >= 201103L +# define CATCH_CPP11_OR_GREATER +# endif + +# if __cplusplus >= 201402L +# define CATCH_CPP14_OR_GREATER +# endif + +#endif + +#ifdef __clang__ + +# if __has_feature(cxx_nullptr) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if __has_feature(cxx_noexcept) +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# if defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) +# endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Borland +#ifdef __BORLANDC__ + +#endif // __BORLANDC__ + +//////////////////////////////////////////////////////////////////////////////// +// EDG +#ifdef __EDG_VERSION__ + +#endif // __EDG_VERSION__ + +//////////////////////////////////////////////////////////////////////////////// +// Digital Mars +#ifdef __DMC__ + +#endif // __DMC__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +# if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" ) +# endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH + +#if (_MSC_VER >= 1600) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// + +// Use variadic macros if the compiler supports them +#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \ + ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \ + ( defined __GNUC__ && __GNUC__ >= 3 ) || \ + ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L ) + +#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS + +#endif + +// Use __COUNTER__ if the compiler supports it +#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \ + ( defined __GNUC__ && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \ + ( defined __clang__ && __clang_major__ >= 3 ) + +#define CATCH_INTERNAL_CONFIG_COUNTER + +#endif + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(CATCH_CPP11_OR_GREATER) + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) +# define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM +# endif + +# ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# define CATCH_INTERNAL_CONFIG_CPP11_TUPLE +# endif + +# ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) +# define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG +# endif + +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) +# define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +# define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) +# define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE +# endif +# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) +# define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS +# endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NULLPTR +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_IS_ENUM +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TUPLE +#endif +#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS) +# define CATCH_CONFIG_VARIADIC_MACROS +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_LONG_LONG +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_UNIQUE_PTR +#endif +// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for +// analytics) because, at time of writing, __COUNTER__ is not properly handled by it. +// This does not affect compilation +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_SHUFFLE +#endif +# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11) +# define CATCH_CONFIG_CPP11_TYPE_TRAITS +# endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif + +// noexcept support: +#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT) +# define CATCH_NOEXCEPT noexcept +# define CATCH_NOEXCEPT_IS(x) noexcept(x) +#else +# define CATCH_NOEXCEPT throw() +# define CATCH_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CATCH_CONFIG_CPP11_NULLPTR +# define CATCH_NULL nullptr +#else +# define CATCH_NULL NULL +#endif + +// override support +#ifdef CATCH_CONFIG_CPP11_OVERRIDE +# define CATCH_OVERRIDE override +#else +# define CATCH_OVERRIDE +#endif + +// unique_ptr support +#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR +# define CATCH_AUTO_PTR( T ) std::unique_ptr +#else +# define CATCH_AUTO_PTR( T ) std::auto_ptr +#endif + +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr +#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr ) + +#include +#include + +namespace Catch { + + struct IConfig; + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { +#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; +#else + NonCopyable( NonCopyable const& info ); + NonCopyable& operator = ( NonCopyable const& ); +#endif + + protected: + NonCopyable() {} + virtual ~NonCopyable(); + }; + + class SafeBool { + public: + typedef void (SafeBool::*type)() const; + + static type makeSafe( bool value ) { + return value ? &SafeBool::trueValue : 0; + } + private: + void trueValue() const {} + }; + + template + inline void deleteAll( ContainerT& container ) { + typename ContainerT::const_iterator it = container.begin(); + typename ContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete *it; + } + template + inline void deleteAllValues( AssociativeContainerT& container ) { + typename AssociativeContainerT::const_iterator it = container.begin(); + typename AssociativeContainerT::const_iterator itEnd = container.end(); + for(; it != itEnd; ++it ) + delete it->second; + } + + bool startsWith( std::string const& s, std::string const& prefix ); + bool startsWith( std::string const& s, char prefix ); + bool endsWith( std::string const& s, std::string const& suffix ); + bool endsWith( std::string const& s, char suffix ); + bool contains( std::string const& s, std::string const& infix ); + void toLowerInPlace( std::string& s ); + std::string toLower( std::string const& s ); + std::string trim( std::string const& str ); + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ); + + struct pluralise { + pluralise( std::size_t count, std::string const& label ); + + friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ); + + std::size_t m_count; + std::string m_label; + }; + + struct SourceLineInfo { + + SourceLineInfo(); + SourceLineInfo( char const* _file, std::size_t _line ); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SourceLineInfo(SourceLineInfo const& other) = default; + SourceLineInfo( SourceLineInfo && ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo& operator = ( SourceLineInfo && ) = default; +# endif + bool empty() const; + bool operator == ( SourceLineInfo const& other ) const; + bool operator < ( SourceLineInfo const& other ) const; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // This is just here to avoid compiler warnings with macro constants and boolean literals + inline bool isTrue( bool value ){ return value; } + inline bool alwaysTrue() { return true; } + inline bool alwaysFalse() { return false; } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ); + + void seedRng( IConfig const& config ); + unsigned int rngSeed(); + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() { + return std::string(); + } + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) +#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO ); + +namespace Catch { + + class NotImplementedException : public std::exception + { + public: + NotImplementedException( SourceLineInfo const& lineInfo ); + NotImplementedException( NotImplementedException const& ) {} + + virtual ~NotImplementedException() CATCH_NOEXCEPT {} + + virtual const char* what() const CATCH_NOEXCEPT; + + private: + std::string m_what; + SourceLineInfo m_lineInfo; + }; + +} // end namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO ) + +// #included from: internal/catch_context.h +#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED + +// #included from: catch_interfaces_generators.h +#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED + +#include + +namespace Catch { + + struct IGeneratorInfo { + virtual ~IGeneratorInfo(); + virtual bool moveNext() = 0; + virtual std::size_t getCurrentIndex() const = 0; + }; + + struct IGeneratorsForTest { + virtual ~IGeneratorsForTest(); + + virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0; + virtual bool moveNext() = 0; + }; + + IGeneratorsForTest* createGeneratorsForTest(); + +} // end namespace Catch + +// #included from: catch_ptr.hpp +#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + // An intrusive reference counting smart pointer. + // T must implement addRef() and release() methods + // typically implementing the IShared interface + template + class Ptr { + public: + Ptr() : m_p( CATCH_NULL ){} + Ptr( T* p ) : m_p( p ){ + if( m_p ) + m_p->addRef(); + } + Ptr( Ptr const& other ) : m_p( other.m_p ){ + if( m_p ) + m_p->addRef(); + } + ~Ptr(){ + if( m_p ) + m_p->release(); + } + void reset() { + if( m_p ) + m_p->release(); + m_p = CATCH_NULL; + } + Ptr& operator = ( T* p ){ + Ptr temp( p ); + swap( temp ); + return *this; + } + Ptr& operator = ( Ptr const& other ){ + Ptr temp( other ); + swap( temp ); + return *this; + } + void swap( Ptr& other ) { std::swap( m_p, other.m_p ); } + T* get() const{ return m_p; } + T& operator*() const { return *m_p; } + T* operator->() const { return m_p; } + bool operator !() const { return m_p == CATCH_NULL; } + operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); } + + private: + T* m_p; + }; + + struct IShared : NonCopyable { + virtual ~IShared(); + virtual void addRef() const = 0; + virtual void release() const = 0; + }; + + template + struct SharedImpl : T { + + SharedImpl() : m_rc( 0 ){} + + virtual void addRef() const { + ++m_rc; + } + virtual void release() const { + if( --m_rc == 0 ) + delete this; + } + + mutable unsigned int m_rc; + }; + +} // end namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + + class TestCase; + class Stream; + struct IResultCapture; + struct IRunner; + struct IGeneratorsForTest; + struct IConfig; + + struct IContext + { + virtual ~IContext(); + + virtual IResultCapture* getResultCapture() = 0; + virtual IRunner* getRunner() = 0; + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0; + virtual bool advanceGeneratorsForCurrentTest() = 0; + virtual Ptr getConfig() const = 0; + }; + + struct IMutableContext : IContext + { + virtual ~IMutableContext(); + virtual void setResultCapture( IResultCapture* resultCapture ) = 0; + virtual void setRunner( IRunner* runner ) = 0; + virtual void setConfig( Ptr const& config ) = 0; + }; + + IContext& getCurrentContext(); + IMutableContext& getCurrentMutableContext(); + void cleanUpContext(); + Stream createStream( std::string const& streamName ); + +} + +// #included from: internal/catch_test_registry.hpp +#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED + +// #included from: catch_interfaces_testcase.h +#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED + +#include + +namespace Catch { + + class TestSpec; + + struct ITestCase : IShared { + virtual void invoke () const = 0; + protected: + virtual ~ITestCase(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +namespace Catch { + +template +class MethodTestCase : public SharedImpl { + +public: + MethodTestCase( void (C::*method)() ) : m_method( method ) {} + + virtual void invoke() const { + C obj; + (obj.*m_method)(); + } + +private: + virtual ~MethodTestCase() {} + + void (C::*m_method)(); +}; + +typedef void(*TestFunction)(); + +struct NameAndDesc { + NameAndDesc( const char* _name = "", const char* _description= "" ) + : name( _name ), description( _description ) + {} + + const char* name; + const char* description; +}; + +void registerTestCase + ( ITestCase* testCase, + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ); + +struct AutoReg { + + AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + + template + AutoReg + ( void (C::*method)(), + char const* className, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + registerTestCase + ( new MethodTestCase( method ), + className, + nameAndDesc, + lineInfo ); + } + + ~AutoReg(); + +private: + AutoReg( AutoReg const& ); + void operator= ( AutoReg const& ); +}; + +void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ); + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( ... ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\ + namespace{ \ + struct TestName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void TestName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); + +#else + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \ + static void TestName(); \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\ + static void TestName() + #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \ + INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \ + namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\ + namespace{ \ + struct TestCaseName : ClassName{ \ + void test(); \ + }; \ + Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \ + } \ + void TestCaseName::test() + #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\ + INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc ) + + /////////////////////////////////////////////////////////////////////////////// + #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \ + Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); +#endif + +// #included from: internal/catch_capture.hpp +#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED + +// #included from: catch_result_builder.h +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED + +// #included from: catch_result_type.h +#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED + +namespace Catch { + + // ResultWas::OfType enum + struct ResultWas { enum OfType { + Unknown = -1, + Ok = 0, + Info = 1, + Warning = 2, + + FailureBit = 0x10, + + ExpressionFailed = FailureBit | 1, + ExplicitFailure = FailureBit | 2, + + Exception = 0x100 | FailureBit, + + ThrewException = Exception | 1, + DidntThrowException = Exception | 2, + + FatalErrorCondition = 0x200 | FailureBit + + }; }; + + inline bool isOk( ResultWas::OfType resultType ) { + return ( resultType & ResultWas::FailureBit ) == 0; + } + inline bool isJustInfo( int flags ) { + return flags == ResultWas::Info; + } + + // ResultDisposition::Flags enum + struct ResultDisposition { enum Flags { + Normal = 0x01, + + ContinueOnFailure = 0x02, // Failures fail test, but execution continues + FalseTest = 0x04, // Prefix expression with ! + SuppressFail = 0x08 // Failures are reported but do not fail the test + }; }; + + inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) { + return static_cast( static_cast( lhs ) | static_cast( rhs ) ); + } + + inline bool shouldContinueOnFailure( int flags ) { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; } + inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; } + inline bool shouldSuppressFailure( int flags ) { return ( flags & ResultDisposition::SuppressFail ) != 0; } + +} // end namespace Catch + +// #included from: catch_assertionresult.h +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED + +#include + +namespace Catch { + + struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison; + + struct DecomposedExpression + { + virtual ~DecomposedExpression() {} + virtual bool isBinaryExpression() const { + return false; + } + virtual void reconstructExpression( std::string& dest ) const = 0; + + // Only simple binary comparisons can be decomposed. + // If more complex check is required then wrap sub-expressions in parentheses. + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& ); + template STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& ); + }; + + struct AssertionInfo + { + AssertionInfo() {} + AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ); + + std::string macroName; + SourceLineInfo lineInfo; + std::string capturedExpression; + ResultDisposition::Flags resultDisposition; + }; + + struct AssertionResultData + { + AssertionResultData() : decomposedExpression( CATCH_NULL ) + , resultType( ResultWas::Unknown ) + , negated( false ) + , parenthesized( false ) {} + + void negate( bool parenthesize ) { + negated = !negated; + parenthesized = parenthesize; + if( resultType == ResultWas::Ok ) + resultType = ResultWas::ExpressionFailed; + else if( resultType == ResultWas::ExpressionFailed ) + resultType = ResultWas::Ok; + } + + std::string const& reconstructExpression() const { + if( decomposedExpression != CATCH_NULL ) { + decomposedExpression->reconstructExpression( reconstructedExpression ); + if( parenthesized ) { + reconstructedExpression.insert( 0, 1, '(' ); + reconstructedExpression.append( 1, ')' ); + } + if( negated ) { + reconstructedExpression.insert( 0, 1, '!' ); + } + decomposedExpression = CATCH_NULL; + } + return reconstructedExpression; + } + + mutable DecomposedExpression const* decomposedExpression; + mutable std::string reconstructedExpression; + std::string message; + ResultWas::OfType resultType; + bool negated; + bool parenthesized; + }; + + class AssertionResult { + public: + AssertionResult(); + AssertionResult( AssertionInfo const& info, AssertionResultData const& data ); + ~AssertionResult(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionResult( AssertionResult const& ) = default; + AssertionResult( AssertionResult && ) = default; + AssertionResult& operator = ( AssertionResult const& ) = default; + AssertionResult& operator = ( AssertionResult && ) = default; +# endif + + bool isOk() const; + bool succeeded() const; + ResultWas::OfType getResultType() const; + bool hasExpression() const; + bool hasMessage() const; + std::string getExpression() const; + std::string getExpressionInMacro() const; + bool hasExpandedExpression() const; + std::string getExpandedExpression() const; + std::string getMessage() const; + SourceLineInfo getSourceInfo() const; + std::string getTestMacroName() const; + void discardDecomposedExpression() const; + void expandDecomposedExpression() const; + + protected: + AssertionInfo m_info; + AssertionResultData m_resultData; + }; + +} // end namespace Catch + +// #included from: catch_matchers.hpp +#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED + +namespace Catch { +namespace Matchers { + namespace Impl { + + namespace Generic { + template class AllOf; + template class AnyOf; + template class Not; + } + + template + struct Matcher : SharedImpl + { + typedef ExpressionT ExpressionType; + + virtual ~Matcher() {} + virtual Ptr clone() const = 0; + virtual bool match( ExpressionT const& expr ) const = 0; + virtual std::string toString() const = 0; + + Generic::AllOf operator && ( Matcher const& other ) const; + Generic::AnyOf operator || ( Matcher const& other ) const; + Generic::Not operator ! () const; + }; + + template + struct MatcherImpl : Matcher { + + virtual Ptr > clone() const { + return Ptr >( new DerivedT( static_cast( *this ) ) ); + } + }; + + namespace Generic { + template + class Not : public MatcherImpl, ExpressionT> { + public: + explicit Not( Matcher const& matcher ) : m_matcher(matcher.clone()) {} + Not( Not const& other ) : m_matcher( other.m_matcher ) {} + + virtual bool match( ExpressionT const& expr ) const CATCH_OVERRIDE { + return !m_matcher->match( expr ); + } + + virtual std::string toString() const CATCH_OVERRIDE { + return "not " + m_matcher->toString(); + } + private: + Ptr< Matcher > m_matcher; + }; + + template + class AllOf : public MatcherImpl, ExpressionT> { + public: + + AllOf() {} + AllOf( AllOf const& other ) : m_matchers( other.m_matchers ) {} + + AllOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( !m_matchers[i]->match( expr ) ) + return false; + return true; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " and "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AllOf operator && ( Matcher const& other ) const { + AllOf allOfExpr( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + template + class AnyOf : public MatcherImpl, ExpressionT> { + public: + + AnyOf() {} + AnyOf( AnyOf const& other ) : m_matchers( other.m_matchers ) {} + + AnyOf& add( Matcher const& matcher ) { + m_matchers.push_back( matcher.clone() ); + return *this; + } + virtual bool match( ExpressionT const& expr ) const + { + for( std::size_t i = 0; i < m_matchers.size(); ++i ) + if( m_matchers[i]->match( expr ) ) + return true; + return false; + } + virtual std::string toString() const { + std::ostringstream oss; + oss << "( "; + for( std::size_t i = 0; i < m_matchers.size(); ++i ) { + if( i != 0 ) + oss << " or "; + oss << m_matchers[i]->toString(); + } + oss << " )"; + return oss.str(); + } + + AnyOf operator || ( Matcher const& other ) const { + AnyOf anyOfExpr( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + private: + std::vector > > m_matchers; + }; + + } // namespace Generic + + template + Generic::AllOf Matcher::operator && ( Matcher const& other ) const { + Generic::AllOf allOfExpr; + allOfExpr.add( *this ); + allOfExpr.add( other ); + return allOfExpr; + } + + template + Generic::AnyOf Matcher::operator || ( Matcher const& other ) const { + Generic::AnyOf anyOfExpr; + anyOfExpr.add( *this ); + anyOfExpr.add( other ); + return anyOfExpr; + } + + template + Generic::Not Matcher::operator ! () const { + return Generic::Not( *this ); + } + + namespace StdString { + + inline std::string makeString( std::string const& str ) { return str; } + inline std::string makeString( const char* str ) { return str ? std::string( str ) : std::string(); } + + struct CasedString + { + CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_str( adjustString( str ) ) + {} + std::string adjustString( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No + ? toLower( str ) + : str; + + } + std::string toStringSuffix() const + { + return m_caseSensitivity == CaseSensitive::No + ? " (case insensitive)" + : std::string(); + } + CaseSensitive::Choice m_caseSensitivity; + std::string m_str; + }; + + struct Equals : MatcherImpl { + Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( str, caseSensitivity ) + {} + Equals( Equals const& other ) : m_data( other.m_data ){} + + virtual ~Equals(); + + virtual bool match( std::string const& expr ) const { + return m_data.m_str == m_data.adjustString( expr );; + } + virtual std::string toString() const { + return "equals: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct Contains : MatcherImpl { + Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + Contains( Contains const& other ) : m_data( other.m_data ){} + + virtual ~Contains(); + + virtual bool match( std::string const& expr ) const { + return m_data.adjustString( expr ).find( m_data.m_str ) != std::string::npos; + } + virtual std::string toString() const { + return "contains: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct StartsWith : MatcherImpl { + StartsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + + StartsWith( StartsWith const& other ) : m_data( other.m_data ){} + + virtual ~StartsWith(); + + virtual bool match( std::string const& expr ) const { + return startsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "starts with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + + struct EndsWith : MatcherImpl { + EndsWith( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) + : m_data( substr, caseSensitivity ){} + EndsWith( EndsWith const& other ) : m_data( other.m_data ){} + + virtual ~EndsWith(); + + virtual bool match( std::string const& expr ) const { + return endsWith( m_data.adjustString( expr ), m_data.m_str ); + } + virtual std::string toString() const { + return "ends with: \"" + m_data.m_str + '"' + m_data.toStringSuffix(); + } + + CasedString m_data; + }; + } // namespace StdString + } // namespace Impl + + // The following functions create the actual matcher objects. + // This allows the types to be inferred + template + inline Impl::Generic::Not Not( Impl::Matcher const& m ) { + return Impl::Generic::Not( m ); + } + + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AllOf AllOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AllOf().add( m1 ).add( m2 ).add( m3 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ); + } + template + inline Impl::Generic::AnyOf AnyOf( Impl::Matcher const& m1, + Impl::Matcher const& m2, + Impl::Matcher const& m3 ) { + return Impl::Generic::AnyOf().add( m1 ).add( m2 ).add( m3 ); + } + + inline Impl::StdString::Equals Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( str, caseSensitivity ); + } + inline Impl::StdString::Equals Equals( const char* str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Equals( Impl::StdString::makeString( str ), caseSensitivity ); + } + inline Impl::StdString::Contains Contains( std::string const& substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( substr, caseSensitivity ); + } + inline Impl::StdString::Contains Contains( const char* substr, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes ) { + return Impl::StdString::Contains( Impl::StdString::makeString( substr ), caseSensitivity ); + } + inline Impl::StdString::StartsWith StartsWith( std::string const& substr ) { + return Impl::StdString::StartsWith( substr ); + } + inline Impl::StdString::StartsWith StartsWith( const char* substr ) { + return Impl::StdString::StartsWith( Impl::StdString::makeString( substr ) ); + } + inline Impl::StdString::EndsWith EndsWith( std::string const& substr ) { + return Impl::StdString::EndsWith( substr ); + } + inline Impl::StdString::EndsWith EndsWith( const char* substr ) { + return Impl::StdString::EndsWith( Impl::StdString::makeString( substr ) ); + } + +} // namespace Matchers + +using namespace Matchers; + +} // namespace Catch + +namespace Catch { + + struct TestFailureException{}; + + template class ExpressionLhs; + + struct CopyableStream { + CopyableStream() {} + CopyableStream( CopyableStream const& other ) { + oss << other.oss.str(); + } + CopyableStream& operator=( CopyableStream const& other ) { + oss.str(std::string()); + oss << other.oss.str(); + return *this; + } + std::ostringstream oss; + }; + + class ResultBuilder : public DecomposedExpression { + public: + ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg = "" ); + + template + ExpressionLhs operator <= ( T const& operand ); + ExpressionLhs operator <= ( bool value ); + + template + ResultBuilder& operator << ( T const& value ) { + m_stream.oss << value; + return *this; + } + + ResultBuilder& setResultType( ResultWas::OfType result ); + ResultBuilder& setResultType( bool result ); + + void endExpression( DecomposedExpression const& expr ); + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE; + + AssertionResult build() const; + AssertionResult build( DecomposedExpression const& expr ) const; + + void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal ); + void captureResult( ResultWas::OfType resultType ); + void captureExpression(); + void captureExpectedException( std::string const& expectedMessage ); + void captureExpectedException( Matchers::Impl::Matcher const& matcher ); + void handleResult( AssertionResult const& result ); + void react(); + bool shouldDebugBreak() const; + bool allowThrows() const; + + template + void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString ); + + private: + AssertionInfo m_assertionInfo; + AssertionResultData m_data; + CopyableStream m_stream; + + bool m_shouldDebugBreak; + bool m_shouldThrow; + }; + +} // namespace Catch + +// Include after due to circular dependency: +// #included from: catch_expression_lhs.hpp +#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED + +// #included from: catch_evaluate.hpp +#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable:4389) // '==' : signed/unsigned mismatch +#endif + +#include + +namespace Catch { +namespace Internal { + + enum Operator { + IsEqualTo, + IsNotEqualTo, + IsLessThan, + IsGreaterThan, + IsLessThanOrEqualTo, + IsGreaterThanOrEqualTo + }; + + template struct OperatorTraits { static const char* getName(){ return "*error*"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "=="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "!="; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<"; } }; + template<> struct OperatorTraits { static const char* getName(){ return ">"; } }; + template<> struct OperatorTraits { static const char* getName(){ return "<="; } }; + template<> struct OperatorTraits{ static const char* getName(){ return ">="; } }; + + template + inline T& opCast(T const& t) { return const_cast(t); } + +// nullptr_t support based on pull request #154 from Konstantin Baumann +#ifdef CATCH_CONFIG_CPP11_NULLPTR + inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; } +#endif // CATCH_CONFIG_CPP11_NULLPTR + + // So the compare overloads can be operator agnostic we convey the operator as a template + // enum, which is used to specialise an Evaluator for doing the comparison. + template + class Evaluator{}; + + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs) { + return bool( opCast( lhs ) == opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) != opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) < opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) > opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) >= opCast( rhs ) ); + } + }; + template + struct Evaluator { + static bool evaluate( T1 const& lhs, T2 const& rhs ) { + return bool( opCast( lhs ) <= opCast( rhs ) ); + } + }; + + template + bool applyEvaluator( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // This level of indirection allows us to specialise for integer types + // to avoid signed/ unsigned warnings + + // "base" overload + template + bool compare( T1 const& lhs, T2 const& rhs ) { + return Evaluator::evaluate( lhs, rhs ); + } + + // unsigned X to int + template bool compare( unsigned int lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, int rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // unsigned X to long + template bool compare( unsigned int lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned long lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + template bool compare( unsigned char lhs, long rhs ) { + return applyEvaluator( lhs, static_cast( rhs ) ); + } + + // int to unsigned X + template bool compare( int lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( int lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // long to unsigned X + template bool compare( long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long (when comparing against NULL) + template bool compare( long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + + // pointer to int (when comparing against NULL) + template bool compare( int lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, int rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG + // long long to unsigned X + template bool compare( long long lhs, unsigned int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( long long lhs, unsigned char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // unsigned long long to X + template bool compare( unsigned long long lhs, int rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, long long rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + template bool compare( unsigned long long lhs, char rhs ) { + return applyEvaluator( static_cast( lhs ), rhs ); + } + + // pointer to long long (when comparing against NULL) + template bool compare( long long lhs, T* rhs ) { + return Evaluator::evaluate( reinterpret_cast( lhs ), rhs ); + } + template bool compare( T* lhs, long long rhs ) { + return Evaluator::evaluate( lhs, reinterpret_cast( rhs ) ); + } +#endif // CATCH_CONFIG_CPP11_LONG_LONG + +#ifdef CATCH_CONFIG_CPP11_NULLPTR + // pointer to nullptr_t (when comparing against nullptr) + template bool compare( std::nullptr_t, T* rhs ) { + return Evaluator::evaluate( nullptr, rhs ); + } + template bool compare( T* lhs, std::nullptr_t ) { + return Evaluator::evaluate( lhs, nullptr ); + } +#endif // CATCH_CONFIG_CPP11_NULLPTR + +} // end of namespace Internal +} // end of namespace Catch + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +// #included from: catch_tostring.h +#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED + +#include +#include +#include +#include +#include + +#ifdef __OBJC__ +// #included from: catch_objc_arc.hpp +#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED + +#import + +#ifdef __has_feature +#define CATCH_ARC_ENABLED __has_feature(objc_arc) +#else +#define CATCH_ARC_ENABLED 0 +#endif + +void arcSafeRelease( NSObject* obj ); +id performOptionalSelector( id obj, SEL sel ); + +#if !CATCH_ARC_ENABLED +inline void arcSafeRelease( NSObject* obj ) { + [obj release]; +} +inline id performOptionalSelector( id obj, SEL sel ) { + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; + return nil; +} +#define CATCH_UNSAFE_UNRETAINED +#define CATCH_ARC_STRONG +#else +inline void arcSafeRelease( NSObject* ){} +inline id performOptionalSelector( id obj, SEL sel ) { +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" +#endif + if( [obj respondsToSelector: sel] ) + return [obj performSelector: sel]; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + return nil; +} +#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained +#define CATCH_ARC_STRONG __strong +#endif + +#endif + +#ifdef CATCH_CONFIG_CPP11_TUPLE +#include +#endif + +#ifdef CATCH_CONFIG_CPP11_IS_ENUM +#include +#endif + +namespace Catch { + +// Why we're here. +template +std::string toString( T const& value ); + +// Built in overloads + +std::string toString( std::string const& value ); +std::string toString( std::wstring const& value ); +std::string toString( const char* const value ); +std::string toString( char* const value ); +std::string toString( const wchar_t* const value ); +std::string toString( wchar_t* const value ); +std::string toString( int value ); +std::string toString( unsigned long value ); +std::string toString( unsigned int value ); +std::string toString( const double value ); +std::string toString( const float value ); +std::string toString( bool value ); +std::string toString( char value ); +std::string toString( signed char value ); +std::string toString( unsigned char value ); + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ); +std::string toString( unsigned long long value ); +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ); +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ); + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ); + std::string toString( NSObject* const& nsObject ); +#endif + +namespace Detail { + + extern const std::string unprintableString; + + struct BorgType { + template BorgType( T const& ); + }; + + struct TrueType { char sizer[1]; }; + struct FalseType { char sizer[2]; }; + + TrueType& testStreamable( std::ostream& ); + FalseType testStreamable( FalseType ); + + FalseType operator<<( std::ostream const&, BorgType const& ); + + template + struct IsStreamInsertable { + static std::ostream &s; + static T const&t; + enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) }; + }; + +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template::value + > + struct EnumStringMaker + { + static std::string convert( T const& ) { return unprintableString; } + }; + + template + struct EnumStringMaker + { + static std::string convert( T const& v ) + { + return ::Catch::toString( + static_cast::type>(v) + ); + } + }; +#endif + template + struct StringMakerBase { +#if defined(CATCH_CONFIG_CPP11_IS_ENUM) + template + static std::string convert( T const& v ) + { + return EnumStringMaker::convert( v ); + } +#else + template + static std::string convert( T const& ) { return unprintableString; } +#endif + }; + + template<> + struct StringMakerBase { + template + static std::string convert( T const& _value ) { + std::ostringstream oss; + oss << _value; + return oss.str(); + } + }; + + std::string rawMemoryToString( const void *object, std::size_t size ); + + template + inline std::string rawMemoryToString( const T& object ) { + return rawMemoryToString( &object, sizeof(object) ); + } + +} // end namespace Detail + +template +struct StringMaker : + Detail::StringMakerBase::value> {}; + +template +struct StringMaker { + template + static std::string convert( U* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +template +struct StringMaker { + static std::string convert( R C::* p ) { + if( !p ) + return "NULL"; + else + return Detail::rawMemoryToString( p ); + } +}; + +namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ); +} + +//template +//struct StringMaker > { +// static std::string convert( std::vector const& v ) { +// return Detail::rangeToString( v.begin(), v.end() ); +// } +//}; + +template +std::string toString( std::vector const& v ) { + return Detail::rangeToString( v.begin(), v.end() ); +} + +#ifdef CATCH_CONFIG_CPP11_TUPLE + +// toString for tuples +namespace TupleDetail { + template< + typename Tuple, + std::size_t N = 0, + bool = (N < std::tuple_size::value) + > + struct ElementPrinter { + static void print( const Tuple& tuple, std::ostream& os ) + { + os << ( N ? ", " : " " ) + << Catch::toString(std::get(tuple)); + ElementPrinter::print(tuple,os); + } + }; + + template< + typename Tuple, + std::size_t N + > + struct ElementPrinter { + static void print( const Tuple&, std::ostream& ) {} + }; + +} + +template +struct StringMaker> { + + static std::string convert( const std::tuple& tuple ) + { + std::ostringstream os; + os << '{'; + TupleDetail::ElementPrinter>::print( tuple, os ); + os << " }"; + return os.str(); + } +}; +#endif // CATCH_CONFIG_CPP11_TUPLE + +namespace Detail { + template + std::string makeString( T const& value ) { + return StringMaker::convert( value ); + } +} // end namespace Detail + +/// \brief converts any type to a string +/// +/// The default template forwards on to ostringstream - except when an +/// ostringstream overload does not exist - in which case it attempts to detect +/// that and writes {?}. +/// Overload (not specialise) this template for custom typs that you don't want +/// to provide an ostream overload for. +template +std::string toString( T const& value ) { + return StringMaker::convert( value ); +} + + namespace Detail { + template + std::string rangeToString( InputIterator first, InputIterator last ) { + std::ostringstream oss; + oss << "{ "; + if( first != last ) { + oss << Catch::toString( *first ); + for( ++first ; first != last ; ++first ) + oss << ", " << Catch::toString( *first ); + } + oss << " }"; + return oss.str(); + } +} + +} // end namespace Catch + +namespace Catch { + +template +class BinaryExpression; + +template +class MatchExpression; + +// Wraps the LHS of an expression and overloads comparison operators +// for also capturing those and RHS (if any) +template +class ExpressionLhs : public DecomposedExpression { +public: + ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {} + + template + BinaryExpression + operator == ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + BinaryExpression + operator != ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + BinaryExpression + operator < ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + BinaryExpression + operator > ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + BinaryExpression + operator <= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + template + BinaryExpression + operator >= ( RhsT const& rhs ) { + return captureExpression( rhs ); + } + + BinaryExpression operator == ( bool rhs ) { + return captureExpression( rhs ); + } + + BinaryExpression operator != ( bool rhs ) { + return captureExpression( rhs ); + } + + void endExpression() { + m_truthy = m_lhs ? true : false; + m_rb + .setResultType( m_truthy ) + .endExpression( *this ); + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + dest = Catch::toString( m_truthy ); + } + +private: + template + BinaryExpression captureExpression( RhsT& rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + + template + BinaryExpression captureExpression( bool rhs ) const { + return BinaryExpression( m_rb, m_lhs, rhs ); + } + +private: + ResultBuilder& m_rb; + T m_lhs; + bool m_truthy; +}; + +template +class BinaryExpression : public DecomposedExpression { +public: + BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs ) + : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {} + + void endExpression() const { + m_rb + .setResultType( Internal::compare( m_lhs, m_rhs ) ) + .endExpression( *this ); + } + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string lhs = Catch::toString( m_lhs ); + std::string rhs = Catch::toString( m_rhs ); + char delim = lhs.size() + rhs.size() < 40 && + lhs.find('\n') == std::string::npos && + rhs.find('\n') == std::string::npos ? ' ' : '\n'; + dest.reserve( 7 + lhs.size() + rhs.size() ); + // 2 for spaces around operator + // 2 for operator + // 2 for parentheses (conditionally added later) + // 1 for negation (conditionally added later) + dest = lhs; + dest += delim; + dest += Internal::OperatorTraits::getName(); + dest += delim; + dest += rhs; + } + +private: + ResultBuilder& m_rb; + LhsT m_lhs; + RhsT m_rhs; +}; + +template +class MatchExpression : public DecomposedExpression { +public: + MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString ) + : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {} + + virtual bool isBinaryExpression() const CATCH_OVERRIDE { + return true; + } + + virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE { + std::string matcherAsString = m_matcher.toString(); + dest = Catch::toString( m_arg ); + dest += ' '; + if( matcherAsString == Detail::unprintableString ) + dest += m_matcherString; + else + dest += matcherAsString; + } + +private: + ArgT m_arg; + MatcherT m_matcher; + char const* m_matcherString; +}; + +} // end namespace Catch + + +namespace Catch { + + template + inline ExpressionLhs ResultBuilder::operator <= ( T const& operand ) { + return ExpressionLhs( *this, operand ); + } + + inline ExpressionLhs ResultBuilder::operator <= ( bool value ) { + return ExpressionLhs( *this, value ); + } + + template + inline void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher, + char const* matcherString ) { + MatchExpression expr( arg, matcher, matcherString ); + setResultType( matcher.match( arg ) ); + endExpression( expr ); + } + +} // namespace Catch + +// #included from: catch_message.h +#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED + +#include + +namespace Catch { + + struct MessageInfo { + MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ); + + std::string macroName; + SourceLineInfo lineInfo; + ResultWas::OfType type; + std::string message; + unsigned int sequence; + + bool operator == ( MessageInfo const& other ) const { + return sequence == other.sequence; + } + bool operator < ( MessageInfo const& other ) const { + return sequence < other.sequence; + } + private: + static unsigned int globalCount; + }; + + struct MessageBuilder { + MessageBuilder( std::string const& macroName, + SourceLineInfo const& lineInfo, + ResultWas::OfType type ) + : m_info( macroName, lineInfo, type ) + {} + + template + MessageBuilder& operator << ( T const& value ) { + m_stream << value; + return *this; + } + + MessageInfo m_info; + std::ostringstream m_stream; + }; + + class ScopedMessage { + public: + ScopedMessage( MessageBuilder const& builder ); + ScopedMessage( ScopedMessage const& other ); + ~ScopedMessage(); + + MessageInfo m_info; + }; + +} // end namespace Catch + +// #included from: catch_interfaces_capture.h +#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + class AssertionResult; + struct AssertionInfo; + struct SectionInfo; + struct SectionEndInfo; + struct MessageInfo; + class ScopedMessageBuilder; + struct Counts; + + struct IResultCapture { + + virtual ~IResultCapture(); + + virtual void assertionEnded( AssertionResult const& result ) = 0; + virtual bool sectionStarted( SectionInfo const& sectionInfo, + Counts& assertions ) = 0; + virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0; + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0; + virtual void pushScopedMessage( MessageInfo const& message ) = 0; + virtual void popScopedMessage( MessageInfo const& message ) = 0; + + virtual std::string getCurrentTestName() const = 0; + virtual const AssertionResult* getLastResult() const = 0; + + virtual void handleFatalErrorCondition( std::string const& message ) = 0; + }; + + IResultCapture& getResultCapture(); +} + +// #included from: catch_debugger.h +#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED + +// #included from: catch_platform.h +#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED + +#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) +# define CATCH_PLATFORM_MAC +#elif defined(__IPHONE_OS_VERSION_MIN_REQUIRED) +# define CATCH_PLATFORM_IPHONE +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +# define CATCH_PLATFORM_WINDOWS +# if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX) +# define CATCH_DEFINES_NOMINMAX +# endif +# if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN) +# define CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# endif +#endif + +#include + +namespace Catch{ + + bool isDebuggerActive(); + void writeToDebugConsole( std::string const& text ); +} + +#ifdef CATCH_PLATFORM_MAC + + // The following code snippet based on: + // http://cocoawithlove.com/2008/03/break-into-debugger.html + #if defined(__ppc64__) || defined(__ppc__) + #define CATCH_TRAP() \ + __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \ + : : : "memory","r0","r3","r4" ) + #else + #define CATCH_TRAP() __asm__("int $3\n" : : ) + #endif + +#elif defined(CATCH_PLATFORM_LINUX) + // If we can use inline assembler, do it because this allows us to break + // directly at the location of the failing check instead of breaking inside + // raise() called from it, i.e. one stack frame below. + #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64)) + #define CATCH_TRAP() asm volatile ("int $3") + #else // Fall back to the generic way. + #include + + #define CATCH_TRAP() raise(SIGTRAP) + #endif +#elif defined(_MSC_VER) + #define CATCH_TRAP() __debugbreak() +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) void __stdcall DebugBreak(); + #define CATCH_TRAP() DebugBreak() +#endif + +#ifdef CATCH_TRAP + #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } +#else + #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue(); +#endif + +// #included from: catch_interfaces_runner.h +#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED + +namespace Catch { + class TestCase; + + struct IRunner { + virtual ~IRunner(); + virtual bool aborting() const = 0; + }; +} + +// #included from: catch_type_traits.hpp +#define TWOBLUECUBES_CATCH_TYPE_TRAITS_HPP_INCLUDED + +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + +namespace Catch { + +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + + template + using add_lvalue_reference = std::add_lvalue_reference; + + template + using add_const = std::add_const; + +#else + + template + struct add_const { + typedef const T type; + }; + + template + struct add_lvalue_reference { + typedef T& type; + }; + template + struct add_lvalue_reference { + typedef T& type; + }; + // No && overload, because that is C++11, in which case we have + // proper type_traits implementation from the standard library + +#endif + +} + +/////////////////////////////////////////////////////////////////////////////// +// In the event of a failure works out if the debugger needs to be invoked +// and/or an exception thrown and takes appropriate action. +// This needs to be done as a macro so the debugger will stop in the user +// source code rather than in Catch library code +#define INTERNAL_CATCH_REACT( resultBuilder ) \ + if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \ + resultBuilder.react(); + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + ( __catchResult <= expr ).endExpression(); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::isTrue( false && static_cast( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look + // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&. + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_ELSE( expr, resultDisposition, macroName ) \ + INTERNAL_CATCH_TEST( expr, resultDisposition, macroName ); \ + if( !Catch::getResultCapture().getLastResult()->succeeded() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_NO_THROW( expr, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS( expr, resultDisposition, matcher, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \ + if( __catchResult.allowThrows() ) \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( ... ) { \ + __catchResult.captureExpectedException( matcher ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_THROWS_AS( expr, exceptionType, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \ + if( __catchResult.allowThrows() ) \ + try { \ + static_cast(expr); \ + __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \ + } \ + catch( Catch::add_const::type>::type ) { \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + } \ + catch( ... ) { \ + __catchResult.useActiveException( resultDisposition ); \ + } \ + else \ + __catchResult.captureResult( Catch::ResultWas::Ok ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +/////////////////////////////////////////////////////////////////////////////// +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, ... ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#else + #define INTERNAL_CATCH_MSG( messageType, resultDisposition, macroName, log ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \ + __catchResult << log + ::Catch::StreamEndStop(); \ + __catchResult.captureResult( messageType ); \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) +#endif + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_INFO( log, macroName ) \ + Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log; + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CHECK_THAT( arg, matcher, resultDisposition, macroName ) \ + do { \ + Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \ + try { \ + __catchResult.captureMatch( arg, matcher, #matcher ); \ + } catch( ... ) { \ + __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \ + } \ + INTERNAL_CATCH_REACT( __catchResult ) \ + } while( Catch::alwaysFalse() ) + +// #included from: internal/catch_section.h +#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED + +// #included from: catch_section_info.h +#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED + +// #included from: catch_totals.hpp +#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED + +#include + +namespace Catch { + + struct Counts { + Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {} + + Counts operator - ( Counts const& other ) const { + Counts diff; + diff.passed = passed - other.passed; + diff.failed = failed - other.failed; + diff.failedButOk = failedButOk - other.failedButOk; + return diff; + } + Counts& operator += ( Counts const& other ) { + passed += other.passed; + failed += other.failed; + failedButOk += other.failedButOk; + return *this; + } + + std::size_t total() const { + return passed + failed + failedButOk; + } + bool allPassed() const { + return failed == 0 && failedButOk == 0; + } + bool allOk() const { + return failed == 0; + } + + std::size_t passed; + std::size_t failed; + std::size_t failedButOk; + }; + + struct Totals { + + Totals operator - ( Totals const& other ) const { + Totals diff; + diff.assertions = assertions - other.assertions; + diff.testCases = testCases - other.testCases; + return diff; + } + + Totals delta( Totals const& prevTotals ) const { + Totals diff = *this - prevTotals; + if( diff.assertions.failed > 0 ) + ++diff.testCases.failed; + else if( diff.assertions.failedButOk > 0 ) + ++diff.testCases.failedButOk; + else + ++diff.testCases.passed; + return diff; + } + + Totals& operator += ( Totals const& other ) { + assertions += other.assertions; + testCases += other.testCases; + return *this; + } + + Counts assertions; + Counts testCases; + }; +} + +#include + +namespace Catch { + + struct SectionInfo { + SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description = std::string() ); + + std::string name; + std::string description; + SourceLineInfo lineInfo; + }; + + struct SectionEndInfo { + SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds ) + : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds ) + {} + + SectionInfo sectionInfo; + Counts prevAssertions; + double durationInSeconds; + }; + +} // end namespace Catch + +// #included from: catch_timer.h +#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED + +#ifdef CATCH_PLATFORM_WINDOWS +typedef unsigned long long uint64_t; +#else +#include +#endif + +namespace Catch { + + class Timer { + public: + Timer() : m_ticks( 0 ) {} + void start(); + unsigned int getElapsedMicroseconds() const; + unsigned int getElapsedMilliseconds() const; + double getElapsedSeconds() const; + + private: + uint64_t m_ticks; + }; + +} // namespace Catch + +#include + +namespace Catch { + + class Section : NonCopyable { + public: + Section( SectionInfo const& info ); + ~Section(); + + // This indicates whether the section should be executed or not + operator bool() const; + + private: + SectionInfo m_info; + + std::string m_name; + Counts m_assertions; + bool m_sectionIncluded; + Timer m_timer; + }; + +} // end namespace Catch + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define INTERNAL_CATCH_SECTION( ... ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) +#else + #define INTERNAL_CATCH_SECTION( name, desc ) \ + if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) ) +#endif + +// #included from: internal/catch_generators.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + +template +struct IGenerator { + virtual ~IGenerator() {} + virtual T getValue( std::size_t index ) const = 0; + virtual std::size_t size () const = 0; +}; + +template +class BetweenGenerator : public IGenerator { +public: + BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){} + + virtual T getValue( std::size_t index ) const { + return m_from+static_cast( index ); + } + + virtual std::size_t size() const { + return static_cast( 1+m_to-m_from ); + } + +private: + + T m_from; + T m_to; +}; + +template +class ValuesGenerator : public IGenerator { +public: + ValuesGenerator(){} + + void add( T value ) { + m_values.push_back( value ); + } + + virtual T getValue( std::size_t index ) const { + return m_values[index]; + } + + virtual std::size_t size() const { + return m_values.size(); + } + +private: + std::vector m_values; +}; + +template +class CompositeGenerator { +public: + CompositeGenerator() : m_totalSize( 0 ) {} + + // *** Move semantics, similar to auto_ptr *** + CompositeGenerator( CompositeGenerator& other ) + : m_fileInfo( other.m_fileInfo ), + m_totalSize( 0 ) + { + move( other ); + } + + CompositeGenerator& setFileInfo( const char* fileInfo ) { + m_fileInfo = fileInfo; + return *this; + } + + ~CompositeGenerator() { + deleteAll( m_composed ); + } + + operator T () const { + size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize ); + + typename std::vector*>::const_iterator it = m_composed.begin(); + typename std::vector*>::const_iterator itEnd = m_composed.end(); + for( size_t index = 0; it != itEnd; ++it ) + { + const IGenerator* generator = *it; + if( overallIndex >= index && overallIndex < index + generator->size() ) + { + return generator->getValue( overallIndex-index ); + } + index += generator->size(); + } + CATCH_INTERNAL_ERROR( "Indexed past end of generated range" ); + return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so + } + + void add( const IGenerator* generator ) { + m_totalSize += generator->size(); + m_composed.push_back( generator ); + } + + CompositeGenerator& then( CompositeGenerator& other ) { + move( other ); + return *this; + } + + CompositeGenerator& then( T value ) { + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( value ); + add( valuesGen ); + return *this; + } + +private: + + void move( CompositeGenerator& other ) { + std::copy( other.m_composed.begin(), other.m_composed.end(), std::back_inserter( m_composed ) ); + m_totalSize += other.m_totalSize; + other.m_composed.clear(); + } + + std::vector*> m_composed; + std::string m_fileInfo; + size_t m_totalSize; +}; + +namespace Generators +{ + template + CompositeGenerator between( T from, T to ) { + CompositeGenerator generators; + generators.add( new BetweenGenerator( from, to ) ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3 ){ + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + generators.add( valuesGen ); + return generators; + } + + template + CompositeGenerator values( T val1, T val2, T val3, T val4 ) { + CompositeGenerator generators; + ValuesGenerator* valuesGen = new ValuesGenerator(); + valuesGen->add( val1 ); + valuesGen->add( val2 ); + valuesGen->add( val3 ); + valuesGen->add( val4 ); + generators.add( valuesGen ); + return generators; + } + +} // end namespace Generators + +using namespace Generators; + +} // end namespace Catch + +#define INTERNAL_CATCH_LINESTR2( line ) #line +#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line ) + +#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" ) + +// #included from: internal/catch_interfaces_exception.h +#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED + +#include +#include + +// #included from: catch_interfaces_registry_hub.h +#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED + +#include + +namespace Catch { + + class TestCase; + struct ITestCaseRegistry; + struct IExceptionTranslatorRegistry; + struct IExceptionTranslator; + struct IReporterRegistry; + struct IReporterFactory; + + struct IRegistryHub { + virtual ~IRegistryHub(); + + virtual IReporterRegistry const& getReporterRegistry() const = 0; + virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0; + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0; + }; + + struct IMutableRegistryHub { + virtual ~IMutableRegistryHub(); + virtual void registerReporter( std::string const& name, Ptr const& factory ) = 0; + virtual void registerListener( Ptr const& factory ) = 0; + virtual void registerTest( TestCase const& testInfo ) = 0; + virtual void registerTranslator( const IExceptionTranslator* translator ) = 0; + }; + + IRegistryHub& getRegistryHub(); + IMutableRegistryHub& getMutableRegistryHub(); + void cleanUp(); + std::string translateActiveException(); + +} + +namespace Catch { + + typedef std::string(*exceptionTranslateFunction)(); + + struct IExceptionTranslator; + typedef std::vector ExceptionTranslators; + + struct IExceptionTranslator { + virtual ~IExceptionTranslator(); + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0; + }; + + struct IExceptionTranslatorRegistry { + virtual ~IExceptionTranslatorRegistry(); + + virtual std::string translateActiveException() const = 0; + }; + + class ExceptionTranslatorRegistrar { + template + class ExceptionTranslator : public IExceptionTranslator { + public: + + ExceptionTranslator( std::string(*translateFunction)( T& ) ) + : m_translateFunction( translateFunction ) + {} + + virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE { + try { + if( it == itEnd ) + throw; + else + return (*it)->translate( it+1, itEnd ); + } + catch( T& ex ) { + return m_translateFunction( ex ); + } + } + + protected: + std::string(*m_translateFunction)( T& ); + }; + + public: + template + ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) { + getMutableRegistryHub().registerTranslator + ( new ExceptionTranslator( translateFunction ) ); + } + }; +} + +/////////////////////////////////////////////////////////////////////////////// +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \ + static std::string translatorName( signature ); \ + namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\ + static std::string translatorName( signature ) + +#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature ) + +// #included from: internal/catch_approx.hpp +#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED + +#include +#include + +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) +#include +#endif + +namespace Catch { +namespace Detail { + + class Approx { + public: + explicit Approx ( double value ) + : m_epsilon( std::numeric_limits::epsilon()*100 ), + m_scale( 1.0 ), + m_value( value ) + {} + + Approx( Approx const& other ) + : m_epsilon( other.m_epsilon ), + m_scale( other.m_scale ), + m_value( other.m_value ) + {} + + static Approx custom() { + return Approx( 0 ); + } + + Approx operator()( double value ) { + Approx approx( value ); + approx.epsilon( m_epsilon ); + approx.scale( m_scale ); + return approx; + } + +#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) + template ::value>::type> + friend bool operator == ( const T& lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + auto lhs_v = double(lhs); + return std::fabs( lhs_v - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs_v), std::fabs(rhs.m_value) ) ); + } + + template ::value>::type> + friend bool operator == ( Approx const& lhs, const T& rhs ) { + return operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator != ( T lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + template ::value>::type> + friend bool operator != ( Approx const& lhs, T rhs ) { + return !operator==( rhs, lhs ); + } + + template ::value>::type> + friend bool operator <= ( T lhs, Approx const& rhs ) + { + return double(lhs) < rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator <= ( Approx const& lhs, T rhs ) + { + return lhs.m_value < double(rhs) || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( T lhs, Approx const& rhs ) + { + return double(lhs) > rhs.m_value || lhs == rhs; + } + + template ::value>::type> + friend bool operator >= ( Approx const& lhs, T rhs ) + { + return lhs.m_value > double(rhs) || lhs == rhs; + } +#else + friend bool operator == ( double lhs, Approx const& rhs ) { + // Thanks to Richard Harris for his help refining this formula + return std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) ); + } + + friend bool operator == ( Approx const& lhs, double rhs ) { + return operator==( rhs, lhs ); + } + + friend bool operator != ( double lhs, Approx const& rhs ) { + return !operator==( lhs, rhs ); + } + + friend bool operator != ( Approx const& lhs, double rhs ) { + return !operator==( rhs, lhs ); + } + + friend bool operator <= ( double lhs, Approx const& rhs ) + { + return lhs < rhs.m_value || lhs == rhs; + } + + friend bool operator <= ( Approx const& lhs, double rhs ) + { + return lhs.m_value < rhs || lhs == rhs; + } + + friend bool operator >= ( double lhs, Approx const& rhs ) + { + return lhs > rhs.m_value || lhs == rhs; + } + + friend bool operator >= ( Approx const& lhs, double rhs ) + { + return lhs.m_value > rhs || lhs == rhs; + } +#endif + + Approx& epsilon( double newEpsilon ) { + m_epsilon = newEpsilon; + return *this; + } + + Approx& scale( double newScale ) { + m_scale = newScale; + return *this; + } + + std::string toString() const { + std::ostringstream oss; + oss << "Approx( " << Catch::toString( m_value ) << " )"; + return oss.str(); + } + + private: + double m_epsilon; + double m_scale; + double m_value; + }; +} + +template<> +inline std::string toString( Detail::Approx const& value ) { + return value.toString(); +} + +} // end namespace Catch + +// #included from: internal/catch_interfaces_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED + +// #included from: catch_tag_alias.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED + +#include + +namespace Catch { + + struct TagAlias { + TagAlias( std::string _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {} + + std::string tag; + SourceLineInfo lineInfo; + }; + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } +// #included from: catch_option.hpp +#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED + +namespace Catch { + + // An optional type + template + class Option { + public: + Option() : nullableValue( CATCH_NULL ) {} + Option( T const& _value ) + : nullableValue( new( storage ) T( _value ) ) + {} + Option( Option const& _other ) + : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL ) + {} + + ~Option() { + reset(); + } + + Option& operator= ( Option const& _other ) { + if( &_other != this ) { + reset(); + if( _other ) + nullableValue = new( storage ) T( *_other ); + } + return *this; + } + Option& operator = ( T const& _value ) { + reset(); + nullableValue = new( storage ) T( _value ); + return *this; + } + + void reset() { + if( nullableValue ) + nullableValue->~T(); + nullableValue = CATCH_NULL; + } + + T& operator*() { return *nullableValue; } + T const& operator*() const { return *nullableValue; } + T* operator->() { return nullableValue; } + const T* operator->() const { return nullableValue; } + + T valueOr( T const& defaultValue ) const { + return nullableValue ? *nullableValue : defaultValue; + } + + bool some() const { return nullableValue != CATCH_NULL; } + bool none() const { return nullableValue == CATCH_NULL; } + + bool operator !() const { return nullableValue == CATCH_NULL; } + operator SafeBool::type() const { + return SafeBool::makeSafe( some() ); + } + + private: + T* nullableValue; + char storage[sizeof(T)]; + }; + +} // end namespace Catch + +namespace Catch { + + struct ITagAliasRegistry { + virtual ~ITagAliasRegistry(); + virtual Option find( std::string const& alias ) const = 0; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0; + + static ITagAliasRegistry const& get(); + }; + +} // end namespace Catch + +// These files are included here so the single_include script doesn't put them +// in the conditionally compiled sections +// #included from: internal/catch_test_case_info.h +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED + +#include +#include + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +namespace Catch { + + struct ITestCase; + + struct TestCaseInfo { + enum SpecialProperties{ + None = 0, + IsHidden = 1 << 1, + ShouldFail = 1 << 2, + MayFail = 1 << 3, + Throws = 1 << 4, + NonPortable = 1 << 5 + }; + + TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ); + + TestCaseInfo( TestCaseInfo const& other ); + + friend void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ); + + bool isHidden() const; + bool throws() const; + bool okToFail() const; + bool expectedToFail() const; + + std::string name; + std::string className; + std::string description; + std::set tags; + std::set lcaseTags; + std::string tagsAsString; + SourceLineInfo lineInfo; + SpecialProperties properties; + }; + + class TestCase : public TestCaseInfo { + public: + + TestCase( ITestCase* testCase, TestCaseInfo const& info ); + TestCase( TestCase const& other ); + + TestCase withName( std::string const& _newName ) const; + + void invoke() const; + + TestCaseInfo const& getTestCaseInfo() const; + + void swap( TestCase& other ); + bool operator == ( TestCase const& other ) const; + bool operator < ( TestCase const& other ) const; + TestCase& operator = ( TestCase const& other ); + + private: + Ptr test; + }; + + TestCase makeTestCase( ITestCase* testCase, + std::string const& className, + std::string const& name, + std::string const& description, + SourceLineInfo const& lineInfo ); +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + +#ifdef __OBJC__ +// #included from: internal/catch_objc.hpp +#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED + +#import + +#include + +// NB. Any general catch headers included here must be included +// in catch.hpp first to make sure they are included by the single +// header for non obj-usage + +/////////////////////////////////////////////////////////////////////////////// +// This protocol is really only here for (self) documenting purposes, since +// all its methods are optional. +@protocol OcFixture + +@optional + +-(void) setUp; +-(void) tearDown; + +@end + +namespace Catch { + + class OcMethod : public SharedImpl { + + public: + OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {} + + virtual void invoke() const { + id obj = [[m_cls alloc] init]; + + performOptionalSelector( obj, @selector(setUp) ); + performOptionalSelector( obj, m_sel ); + performOptionalSelector( obj, @selector(tearDown) ); + + arcSafeRelease( obj ); + } + private: + virtual ~OcMethod() {} + + Class m_cls; + SEL m_sel; + }; + + namespace Detail{ + + inline std::string getAnnotation( Class cls, + std::string const& annotationName, + std::string const& testCaseName ) { + NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()]; + SEL sel = NSSelectorFromString( selStr ); + arcSafeRelease( selStr ); + id value = performOptionalSelector( cls, sel ); + if( value ) + return [(NSString*)value UTF8String]; + return ""; + } + } + + inline size_t registerTestMethods() { + size_t noTestMethods = 0; + int noClasses = objc_getClassList( CATCH_NULL, 0 ); + + Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses); + objc_getClassList( classes, noClasses ); + + for( int c = 0; c < noClasses; c++ ) { + Class cls = classes[c]; + { + u_int count; + Method* methods = class_copyMethodList( cls, &count ); + for( u_int m = 0; m < count ; m++ ) { + SEL selector = method_getName(methods[m]); + std::string methodName = sel_getName(selector); + if( startsWith( methodName, "Catch_TestCase_" ) ) { + std::string testCaseName = methodName.substr( 15 ); + std::string name = Detail::getAnnotation( cls, "Name", testCaseName ); + std::string desc = Detail::getAnnotation( cls, "Description", testCaseName ); + const char* className = class_getName( cls ); + + getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) ); + noTestMethods++; + } + } + free(methods); + } + } + return noTestMethods; + } + + namespace Matchers { + namespace Impl { + namespace NSStringMatchers { + + template + struct StringHolder : MatcherImpl{ + StringHolder( NSString* substr ) : m_substr( [substr copy] ){} + StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){} + StringHolder() { + arcSafeRelease( m_substr ); + } + + NSString* m_substr; + }; + + struct Equals : StringHolder { + Equals( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str isEqualToString:m_substr]; + } + + virtual std::string toString() const { + return "equals string: " + Catch::toString( m_substr ); + } + }; + + struct Contains : StringHolder { + Contains( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location != NSNotFound; + } + + virtual std::string toString() const { + return "contains string: " + Catch::toString( m_substr ); + } + }; + + struct StartsWith : StringHolder { + StartsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == 0; + } + + virtual std::string toString() const { + return "starts with: " + Catch::toString( m_substr ); + } + }; + struct EndsWith : StringHolder { + EndsWith( NSString* substr ) : StringHolder( substr ){} + + virtual bool match( ExpressionType const& str ) const { + return (str != nil || m_substr == nil ) && + [str rangeOfString:m_substr].location == [str length] - [m_substr length]; + } + + virtual std::string toString() const { + return "ends with: " + Catch::toString( m_substr ); + } + }; + + } // namespace NSStringMatchers + } // namespace Impl + + inline Impl::NSStringMatchers::Equals + Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); } + + inline Impl::NSStringMatchers::Contains + Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); } + + inline Impl::NSStringMatchers::StartsWith + StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); } + + inline Impl::NSStringMatchers::EndsWith + EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); } + + } // namespace Matchers + + using namespace Matchers; + +} // namespace Catch + +/////////////////////////////////////////////////////////////////////////////// +#define OC_TEST_CASE( name, desc )\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \ +{\ +return @ name; \ +}\ ++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \ +{ \ +return @ desc; \ +} \ +-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test ) + +#endif + +#ifdef CATCH_IMPL +// #included from: internal/catch_impl.hpp +#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED + +// Collect all the implementation files together here +// These are the equivalent of what would usually be cpp files + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wweak-vtables" +#endif + +// #included from: ../catch_session.hpp +#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED + +// #included from: internal/catch_commandline.hpp +#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED + +// #included from: catch_config.hpp +#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED + +// #included from: catch_test_spec_parser.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_test_spec.hpp +#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// #included from: catch_wildcard_pattern.hpp +#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED + +#include + +namespace Catch +{ + class WildcardPattern { + enum WildcardPosition { + NoWildcard = 0, + WildcardAtStart = 1, + WildcardAtEnd = 2, + WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd + }; + + public: + + WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity ) + : m_caseSensitivity( caseSensitivity ), + m_wildcard( NoWildcard ), + m_pattern( adjustCase( pattern ) ) + { + if( startsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 1 ); + m_wildcard = WildcardAtStart; + } + if( endsWith( m_pattern, '*' ) ) { + m_pattern = m_pattern.substr( 0, m_pattern.size()-1 ); + m_wildcard = static_cast( m_wildcard | WildcardAtEnd ); + } + } + virtual ~WildcardPattern(); + virtual bool matches( std::string const& str ) const { + switch( m_wildcard ) { + case NoWildcard: + return m_pattern == adjustCase( str ); + case WildcardAtStart: + return endsWith( adjustCase( str ), m_pattern ); + case WildcardAtEnd: + return startsWith( adjustCase( str ), m_pattern ); + case WildcardAtBothEnds: + return contains( adjustCase( str ), m_pattern ); + } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + throw std::logic_error( "Unknown enum" ); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + } + private: + std::string adjustCase( std::string const& str ) const { + return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str; + } + CaseSensitive::Choice m_caseSensitivity; + WildcardPosition m_wildcard; + std::string m_pattern; + }; +} + +#include +#include + +namespace Catch { + + class TestSpec { + struct Pattern : SharedImpl<> { + virtual ~Pattern(); + virtual bool matches( TestCaseInfo const& testCase ) const = 0; + }; + class NamePattern : public Pattern { + public: + NamePattern( std::string const& name ) + : m_wildcardPattern( toLower( name ), CaseSensitive::No ) + {} + virtual ~NamePattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return m_wildcardPattern.matches( toLower( testCase.name ) ); + } + private: + WildcardPattern m_wildcardPattern; + }; + + class TagPattern : public Pattern { + public: + TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {} + virtual ~TagPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { + return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end(); + } + private: + std::string m_tag; + }; + + class ExcludedPattern : public Pattern { + public: + ExcludedPattern( Ptr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {} + virtual ~ExcludedPattern(); + virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); } + private: + Ptr m_underlyingPattern; + }; + + struct Filter { + std::vector > m_patterns; + + bool matches( TestCaseInfo const& testCase ) const { + // All patterns in a filter must match for the filter to be a match + for( std::vector >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) { + if( !(*it)->matches( testCase ) ) + return false; + } + return true; + } + }; + + public: + bool hasFilters() const { + return !m_filters.empty(); + } + bool matches( TestCaseInfo const& testCase ) const { + // A TestSpec matches if any filter matches + for( std::vector::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it ) + if( it->matches( testCase ) ) + return true; + return false; + } + + private: + std::vector m_filters; + + friend class TestSpecParser; + }; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +namespace Catch { + + class TestSpecParser { + enum Mode{ None, Name, QuotedName, Tag, EscapedName }; + Mode m_mode; + bool m_exclusion; + std::size_t m_start, m_pos; + std::string m_arg; + std::vector m_escapeChars; + TestSpec::Filter m_currentFilter; + TestSpec m_testSpec; + ITagAliasRegistry const* m_tagAliases; + + public: + TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {} + + TestSpecParser& parse( std::string const& arg ) { + m_mode = None; + m_exclusion = false; + m_start = std::string::npos; + m_arg = m_tagAliases->expandAliases( arg ); + m_escapeChars.clear(); + for( m_pos = 0; m_pos < m_arg.size(); ++m_pos ) + visitChar( m_arg[m_pos] ); + if( m_mode == Name ) + addPattern(); + return *this; + } + TestSpec testSpec() { + addFilter(); + return m_testSpec; + } + private: + void visitChar( char c ) { + if( m_mode == None ) { + switch( c ) { + case ' ': return; + case '~': m_exclusion = true; return; + case '[': return startNewMode( Tag, ++m_pos ); + case '"': return startNewMode( QuotedName, ++m_pos ); + case '\\': return escape(); + default: startNewMode( Name, m_pos ); break; + } + } + if( m_mode == Name ) { + if( c == ',' ) { + addPattern(); + addFilter(); + } + else if( c == '[' ) { + if( subString() == "exclude:" ) + m_exclusion = true; + else + addPattern(); + startNewMode( Tag, ++m_pos ); + } + else if( c == '\\' ) + escape(); + } + else if( m_mode == EscapedName ) + m_mode = Name; + else if( m_mode == QuotedName && c == '"' ) + addPattern(); + else if( m_mode == Tag && c == ']' ) + addPattern(); + } + void startNewMode( Mode mode, std::size_t start ) { + m_mode = mode; + m_start = start; + } + void escape() { + if( m_mode == None ) + m_start = m_pos; + m_mode = EscapedName; + m_escapeChars.push_back( m_pos ); + } + std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); } + template + void addPattern() { + std::string token = subString(); + for( size_t i = 0; i < m_escapeChars.size(); ++i ) + token = token.substr( 0, m_escapeChars[i]-i ) + token.substr( m_escapeChars[i]+1-i ); + m_escapeChars.clear(); + if( startsWith( token, "exclude:" ) ) { + m_exclusion = true; + token = token.substr( 8 ); + } + if( !token.empty() ) { + Ptr pattern = new T( token ); + if( m_exclusion ) + pattern = new TestSpec::ExcludedPattern( pattern ); + m_currentFilter.m_patterns.push_back( pattern ); + } + m_exclusion = false; + m_mode = None; + } + void addFilter() { + if( !m_currentFilter.m_patterns.empty() ) { + m_testSpec.m_filters.push_back( m_currentFilter ); + m_currentFilter = TestSpec::Filter(); + } + } + }; + inline TestSpec parseTestSpec( std::string const& arg ) { + return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec(); + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// #included from: catch_interfaces_config.h +#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct Verbosity { enum Level { + NoOutput = 0, + Quiet, + Normal + }; }; + + struct WarnAbout { enum What { + Nothing = 0x00, + NoAssertions = 0x01 + }; }; + + struct ShowDurations { enum OrNot { + DefaultForReporter, + Always, + Never + }; }; + struct RunTests { enum InWhatOrder { + InDeclarationOrder, + InLexicographicalOrder, + InRandomOrder + }; }; + struct UseColour { enum YesOrNo { + Auto, + Yes, + No + }; }; + + class TestSpec; + + struct IConfig : IShared { + + virtual ~IConfig(); + + virtual bool allowThrows() const = 0; + virtual std::ostream& stream() const = 0; + virtual std::string name() const = 0; + virtual bool includeSuccessfulResults() const = 0; + virtual bool shouldDebugBreak() const = 0; + virtual bool warnAboutMissingAssertions() const = 0; + virtual int abortAfter() const = 0; + virtual bool showInvisibles() const = 0; + virtual ShowDurations::OrNot showDurations() const = 0; + virtual TestSpec const& testSpec() const = 0; + virtual RunTests::InWhatOrder runOrder() const = 0; + virtual unsigned int rngSeed() const = 0; + virtual UseColour::YesOrNo useColour() const = 0; + virtual std::vector const& getSectionsToRun() const = 0; + + }; +} + +// #included from: catch_stream.h +#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED + +// #included from: catch_streambuf.h +#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED + +#include + +namespace Catch { + + class StreamBufBase : public std::streambuf { + public: + virtual ~StreamBufBase() CATCH_NOEXCEPT; + }; +} + +#include +#include +#include +#include + +namespace Catch { + + std::ostream& cout(); + std::ostream& cerr(); + + struct IStream { + virtual ~IStream() CATCH_NOEXCEPT; + virtual std::ostream& stream() const = 0; + }; + + class FileStream : public IStream { + mutable std::ofstream m_ofs; + public: + FileStream( std::string const& filename ); + virtual ~FileStream() CATCH_NOEXCEPT; + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class CoutStream : public IStream { + mutable std::ostream m_os; + public: + CoutStream(); + virtual ~CoutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; + + class DebugOutStream : public IStream { + CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf; + mutable std::ostream m_os; + public: + DebugOutStream(); + virtual ~DebugOutStream() CATCH_NOEXCEPT; + + public: // IStream + virtual std::ostream& stream() const CATCH_OVERRIDE; + }; +} + +#include +#include +#include +#include + +#ifndef CATCH_CONFIG_CONSOLE_WIDTH +#define CATCH_CONFIG_CONSOLE_WIDTH 80 +#endif + +namespace Catch { + + struct ConfigData { + + ConfigData() + : listTests( false ), + listTags( false ), + listReporters( false ), + listTestNamesOnly( false ), + showSuccessfulTests( false ), + shouldDebugBreak( false ), + noThrow( false ), + showHelp( false ), + showInvisibles( false ), + filenamesAsTags( false ), + abortAfter( -1 ), + rngSeed( 0 ), + verbosity( Verbosity::Normal ), + warnings( WarnAbout::Nothing ), + showDurations( ShowDurations::DefaultForReporter ), + runOrder( RunTests::InDeclarationOrder ), + useColour( UseColour::Auto ) + {} + + bool listTests; + bool listTags; + bool listReporters; + bool listTestNamesOnly; + + bool showSuccessfulTests; + bool shouldDebugBreak; + bool noThrow; + bool showHelp; + bool showInvisibles; + bool filenamesAsTags; + + int abortAfter; + unsigned int rngSeed; + + Verbosity::Level verbosity; + WarnAbout::What warnings; + ShowDurations::OrNot showDurations; + RunTests::InWhatOrder runOrder; + UseColour::YesOrNo useColour; + + std::string outputFilename; + std::string name; + std::string processName; + + std::vector reporterNames; + std::vector testsOrTags; + std::vector sectionsToRun; + }; + + class Config : public SharedImpl { + private: + Config( Config const& other ); + Config& operator = ( Config const& other ); + virtual void dummy(); + public: + + Config() + {} + + Config( ConfigData const& data ) + : m_data( data ), + m_stream( openStream() ) + { + if( !data.testsOrTags.empty() ) { + TestSpecParser parser( ITagAliasRegistry::get() ); + for( std::size_t i = 0; i < data.testsOrTags.size(); ++i ) + parser.parse( data.testsOrTags[i] ); + m_testSpec = parser.testSpec(); + } + } + + virtual ~Config() {} + + std::string const& getFilename() const { + return m_data.outputFilename ; + } + + bool listTests() const { return m_data.listTests; } + bool listTestNamesOnly() const { return m_data.listTestNamesOnly; } + bool listTags() const { return m_data.listTags; } + bool listReporters() const { return m_data.listReporters; } + + std::string getProcessName() const { return m_data.processName; } + + std::vector const& getReporterNames() const { return m_data.reporterNames; } + std::vector const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; } + + virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; } + + bool showHelp() const { return m_data.showHelp; } + + // IConfig interface + virtual bool allowThrows() const CATCH_OVERRIDE { return !m_data.noThrow; } + virtual std::ostream& stream() const CATCH_OVERRIDE { return m_stream->stream(); } + virtual std::string name() const CATCH_OVERRIDE { return m_data.name.empty() ? m_data.processName : m_data.name; } + virtual bool includeSuccessfulResults() const CATCH_OVERRIDE { return m_data.showSuccessfulTests; } + virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE { return m_data.warnings & WarnAbout::NoAssertions; } + virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; } + virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE { return m_data.runOrder; } + virtual unsigned int rngSeed() const CATCH_OVERRIDE { return m_data.rngSeed; } + virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE { return m_data.useColour; } + virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; } + virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; } + virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; } + + private: + + IStream const* openStream() { + if( m_data.outputFilename.empty() ) + return new CoutStream(); + else if( m_data.outputFilename[0] == '%' ) { + if( m_data.outputFilename == "%debug" ) + return new DebugOutStream(); + else + throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename ); + } + else + return new FileStream( m_data.outputFilename ); + } + ConfigData m_data; + + CATCH_AUTO_PTR( IStream const ) m_stream; + TestSpec m_testSpec; + }; + +} // end namespace Catch + +// #included from: catch_clara.h +#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED + +// Use Catch's value for console width (store Clara's off to the side, if present) +#ifdef CLARA_CONFIG_CONSOLE_WIDTH +#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#undef CLARA_CONFIG_CONSOLE_WIDTH +#endif +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +// Declare Clara inside the Catch namespace +#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch { +// #included from: ../external/clara.h + +// Version 0.0.2.4 + +// Only use header guard if we are not using an outer namespace +#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE) + +#ifndef STITCH_CLARA_OPEN_NAMESPACE +#define TWOBLUECUBES_CLARA_H_INCLUDED +#define STITCH_CLARA_OPEN_NAMESPACE +#define STITCH_CLARA_CLOSE_NAMESPACE +#else +#define STITCH_CLARA_CLOSE_NAMESPACE } +#endif + +#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE + +// ----------- #included from tbc_text_format.h ----------- + +// Only use header guard if we are not using an outer namespace +#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE) +#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +#define TBC_TEXT_FORMAT_H_INCLUDED +#endif + +#include +#include +#include +#include + +// Use optional outer namespace +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ), + tabChar( '\t' ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + TextAttributes& setTabChar( char _value ) { tabChar = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + char tabChar; // If this char is seen the indent is changed to current pos + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + std::string wrappableChars = " [({.,/|\\-"; + std::size_t indent = _attr.initialIndent != std::string::npos + ? _attr.initialIndent + : _attr.indent; + std::string remainder = _str; + + while( !remainder.empty() ) { + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + std::size_t tabPos = std::string::npos; + std::size_t width = (std::min)( remainder.size(), _attr.width - indent ); + std::size_t pos = remainder.find_first_of( '\n' ); + if( pos <= width ) { + width = pos; + } + pos = remainder.find_last_of( _attr.tabChar, width ); + if( pos != std::string::npos ) { + tabPos = pos; + if( remainder[width] == '\n' ) + width--; + remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 ); + } + + if( width == remainder.size() ) { + spliceLine( indent, remainder, width ); + } + else if( remainder[width] == '\n' ) { + spliceLine( indent, remainder, width ); + if( width <= 1 || remainder.size() != 1 ) + remainder = remainder.substr( 1 ); + indent = _attr.indent; + } + else { + pos = remainder.find_last_of( wrappableChars, width ); + if( pos != std::string::npos && pos > 0 ) { + spliceLine( indent, remainder, pos ); + if( remainder[0] == ' ' ) + remainder = remainder.substr( 1 ); + } + else { + spliceLine( indent, remainder, width-1 ); + lines.back() += "-"; + } + if( lines.size() == 1 ) + indent = _attr.indent; + if( tabPos != std::string::npos ) + indent += tabPos; + } + } + } + + void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) { + lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) ); + _remainder = _remainder.substr( _pos ); + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TBC_TEXT_FORMAT_H_INCLUDED + +// ----------- end of #include from tbc_text_format.h ----------- +// ........... back in clara.h + +#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE + +// ----------- #included from clara_compilers.h ----------- + +#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED +#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// Detect a number of compiler features - mostly C++11/14 conformance - by compiler +// The following features are defined: +// +// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported? +// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported? +// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods +// CLARA_CONFIG_CPP11_OVERRIDE : is override supported? +// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr) + +// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported? + +// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported? + +// In general each macro has a _NO_ form +// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11 + +#ifdef __clang__ + +#if __has_feature(cxx_nullptr) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#if __has_feature(cxx_noexcept) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// GCC +#ifdef __GNUC__ + +#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +// - otherwise more recent versions define __cplusplus >= 201103L +// and will get picked up below + +#endif // __GNUC__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#ifdef _MSC_VER + +#if (_MSC_VER >= 1600) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015)) +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// C++ language feature support + +// catch all support for C++11 +#if defined(__cplusplus) && __cplusplus >= 201103L + +#define CLARA_CPP11_OR_GREATER + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) +#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT +#endif + +#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS +#endif + +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) +#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE +#endif +#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) +#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR +#endif + +#endif // __cplusplus >= 201103L + +// Now set the actual defines based on the above + anything the user has configured +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NULLPTR +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_NOEXCEPT +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_GENERATED_METHODS +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_OVERRIDE +#endif +#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11) +#define CLARA_CONFIG_CPP11_UNIQUE_PTR +#endif + +// noexcept support: +#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT) +#define CLARA_NOEXCEPT noexcept +# define CLARA_NOEXCEPT_IS(x) noexcept(x) +#else +#define CLARA_NOEXCEPT throw() +# define CLARA_NOEXCEPT_IS(x) +#endif + +// nullptr support +#ifdef CLARA_CONFIG_CPP11_NULLPTR +#define CLARA_NULL nullptr +#else +#define CLARA_NULL NULL +#endif + +// override support +#ifdef CLARA_CONFIG_CPP11_OVERRIDE +#define CLARA_OVERRIDE override +#else +#define CLARA_OVERRIDE +#endif + +// unique_ptr support +#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR +# define CLARA_AUTO_PTR( T ) std::unique_ptr +#else +# define CLARA_AUTO_PTR( T ) std::auto_ptr +#endif + +#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED + +// ----------- end of #include from clara_compilers.h ----------- +// ........... back in clara.h + +#include +#include +#include + +#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) +#define CLARA_PLATFORM_WINDOWS +#endif + +// Use optional outer namespace +#ifdef STITCH_CLARA_OPEN_NAMESPACE +STITCH_CLARA_OPEN_NAMESPACE +#endif + +namespace Clara { + + struct UnpositionalTag {}; + + extern UnpositionalTag _; + +#ifdef CLARA_CONFIG_MAIN + UnpositionalTag _; +#endif + + namespace Detail { + +#ifdef CLARA_CONSOLE_WIDTH + const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + using namespace Tbc; + + inline bool startsWith( std::string const& str, std::string const& prefix ) { + return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix; + } + + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + template struct RemoveConstRef{ typedef T type; }; + + template struct IsBool { static const bool value = false; }; + template<> struct IsBool { static const bool value = true; }; + + template + void convertInto( std::string const& _source, T& _dest ) { + std::stringstream ss; + ss << _source; + ss >> _dest; + if( ss.fail() ) + throw std::runtime_error( "Unable to convert " + _source + " to destination type" ); + } + inline void convertInto( std::string const& _source, std::string& _dest ) { + _dest = _source; + } + char toLowerCh(char c) { + return static_cast( ::tolower( c ) ); + } + inline void convertInto( std::string const& _source, bool& _dest ) { + std::string sourceLC = _source; + std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh ); + if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" ) + _dest = true; + else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" ) + _dest = false; + else + throw std::runtime_error( "Expected a boolean value but did not recognise:\n '" + _source + "'" ); + } + + template + struct IArgFunction { + virtual ~IArgFunction() {} +#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS + IArgFunction() = default; + IArgFunction( IArgFunction const& ) = default; +#endif + virtual void set( ConfigT& config, std::string const& value ) const = 0; + virtual bool takesArg() const = 0; + virtual IArgFunction* clone() const = 0; + }; + + template + class BoundArgFunction { + public: + BoundArgFunction() : functionObj( CLARA_NULL ) {} + BoundArgFunction( IArgFunction* _functionObj ) : functionObj( _functionObj ) {} + BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {} + BoundArgFunction& operator = ( BoundArgFunction const& other ) { + IArgFunction* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL; + delete functionObj; + functionObj = newFunctionObj; + return *this; + } + ~BoundArgFunction() { delete functionObj; } + + void set( ConfigT& config, std::string const& value ) const { + functionObj->set( config, value ); + } + bool takesArg() const { return functionObj->takesArg(); } + + bool isSet() const { + return functionObj != CLARA_NULL; + } + private: + IArgFunction* functionObj; + }; + + template + struct NullBinder : IArgFunction{ + virtual void set( C&, std::string const& ) const {} + virtual bool takesArg() const { return true; } + virtual IArgFunction* clone() const { return new NullBinder( *this ); } + }; + + template + struct BoundDataMember : IArgFunction{ + BoundDataMember( M C::* _member ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + convertInto( stringValue, p.*member ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundDataMember( *this ); } + M C::* member; + }; + template + struct BoundUnaryMethod : IArgFunction{ + BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + (p.*member)( value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundUnaryMethod( *this ); } + void (C::*member)( M ); + }; + template + struct BoundNullaryMethod : IArgFunction{ + BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {} + virtual void set( C& p, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + (p.*member)(); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundNullaryMethod( *this ); } + void (C::*member)(); + }; + + template + struct BoundUnaryFunction : IArgFunction{ + BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + bool value; + convertInto( stringValue, value ); + if( value ) + function( obj ); + } + virtual bool takesArg() const { return false; } + virtual IArgFunction* clone() const { return new BoundUnaryFunction( *this ); } + void (*function)( C& ); + }; + + template + struct BoundBinaryFunction : IArgFunction{ + BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {} + virtual void set( C& obj, std::string const& stringValue ) const { + typename RemoveConstRef::type value; + convertInto( stringValue, value ); + function( obj, value ); + } + virtual bool takesArg() const { return !IsBool::value; } + virtual IArgFunction* clone() const { return new BoundBinaryFunction( *this ); } + void (*function)( C&, T ); + }; + + } // namespace Detail + + inline std::vector argsToVector( int argc, char const* const* const argv ) { + std::vector args( static_cast( argc ) ); + for( std::size_t i = 0; i < static_cast( argc ); ++i ) + args[i] = argv[i]; + + return args; + } + + class Parser { + enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional }; + Mode mode; + std::size_t from; + bool inQuotes; + public: + + struct Token { + enum Type { Positional, ShortOpt, LongOpt }; + Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {} + Type type; + std::string data; + }; + + Parser() : mode( None ), from( 0 ), inQuotes( false ){} + + void parseIntoTokens( std::vector const& args, std::vector& tokens ) { + const std::string doubleDash = "--"; + for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i ) + parseIntoTokens( args[i], tokens); + } + + void parseIntoTokens( std::string const& arg, std::vector& tokens ) { + for( std::size_t i = 0; i <= arg.size(); ++i ) { + char c = arg[i]; + if( c == '"' ) + inQuotes = !inQuotes; + mode = handleMode( i, c, arg, tokens ); + } + } + Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + switch( mode ) { + case None: return handleNone( i, c ); + case MaybeShortOpt: return handleMaybeShortOpt( i, c ); + case ShortOpt: + case LongOpt: + case SlashOpt: return handleOpt( i, c, arg, tokens ); + case Positional: return handlePositional( i, c, arg, tokens ); + default: throw std::logic_error( "Unknown mode" ); + } + } + + Mode handleNone( std::size_t i, char c ) { + if( inQuotes ) { + from = i; + return Positional; + } + switch( c ) { + case '-': return MaybeShortOpt; +#ifdef CLARA_PLATFORM_WINDOWS + case '/': from = i+1; return SlashOpt; +#endif + default: from = i; return Positional; + } + } + Mode handleMaybeShortOpt( std::size_t i, char c ) { + switch( c ) { + case '-': from = i+1; return LongOpt; + default: from = i; return ShortOpt; + } + } + Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( std::string( ":=\0", 3 ).find( c ) == std::string::npos ) + return mode; + + std::string optName = arg.substr( from, i-from ); + if( mode == ShortOpt ) + for( std::size_t j = 0; j < optName.size(); ++j ) + tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) ); + else if( mode == SlashOpt && optName.size() == 1 ) + tokens.push_back( Token( Token::ShortOpt, optName ) ); + else + tokens.push_back( Token( Token::LongOpt, optName ) ); + return None; + } + Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector& tokens ) { + if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos ) + return mode; + + std::string data = arg.substr( from, i-from ); + tokens.push_back( Token( Token::Positional, data ) ); + return None; + } + }; + + template + struct CommonArgProperties { + CommonArgProperties() {} + CommonArgProperties( Detail::BoundArgFunction const& _boundField ) : boundField( _boundField ) {} + + Detail::BoundArgFunction boundField; + std::string description; + std::string detail; + std::string placeholder; // Only value if boundField takes an arg + + bool takesArg() const { + return !placeholder.empty(); + } + void validate() const { + if( !boundField.isSet() ) + throw std::logic_error( "option not bound" ); + } + }; + struct OptionArgProperties { + std::vector shortNames; + std::string longName; + + bool hasShortName( std::string const& shortName ) const { + return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end(); + } + bool hasLongName( std::string const& _longName ) const { + return _longName == longName; + } + }; + struct PositionalArgProperties { + PositionalArgProperties() : position( -1 ) {} + int position; // -1 means non-positional (floating) + + bool isFixedPositional() const { + return position != -1; + } + }; + + template + class CommandLine { + + struct Arg : CommonArgProperties, OptionArgProperties, PositionalArgProperties { + Arg() {} + Arg( Detail::BoundArgFunction const& _boundField ) : CommonArgProperties( _boundField ) {} + + using CommonArgProperties::placeholder; // !TBD + + std::string dbgName() const { + if( !longName.empty() ) + return "--" + longName; + if( !shortNames.empty() ) + return "-" + shortNames[0]; + return "positional args"; + } + std::string commands() const { + std::ostringstream oss; + bool first = true; + std::vector::const_iterator it = shortNames.begin(), itEnd = shortNames.end(); + for(; it != itEnd; ++it ) { + if( first ) + first = false; + else + oss << ", "; + oss << "-" << *it; + } + if( !longName.empty() ) { + if( !first ) + oss << ", "; + oss << "--" << longName; + } + if( !placeholder.empty() ) + oss << " <" << placeholder << ">"; + return oss.str(); + } + }; + + typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr; + + friend void addOptName( Arg& arg, std::string const& optName ) + { + if( optName.empty() ) + return; + if( Detail::startsWith( optName, "--" ) ) { + if( !arg.longName.empty() ) + throw std::logic_error( "Only one long opt may be specified. '" + + arg.longName + + "' already specified, now attempting to add '" + + optName + "'" ); + arg.longName = optName.substr( 2 ); + } + else if( Detail::startsWith( optName, "-" ) ) + arg.shortNames.push_back( optName.substr( 1 ) ); + else + throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" ); + } + friend void setPositionalArg( Arg& arg, int position ) + { + arg.position = position; + } + + class ArgBuilder { + public: + ArgBuilder( Arg* arg ) : m_arg( arg ) {} + + // Bind a non-boolean data member (requires placeholder string) + template + void bind( M C::* field, std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + m_arg->placeholder = placeholder; + } + // Bind a boolean data member (no placeholder required) + template + void bind( bool C::* field ) { + m_arg->boundField = new Detail::BoundDataMember( field ); + } + + // Bind a method taking a single, non-boolean argument (requires a placeholder string) + template + void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + m_arg->placeholder = placeholder; + } + + // Bind a method taking a single, boolean argument (no placeholder string required) + template + void bind( void (C::* unaryMethod)( bool ) ) { + m_arg->boundField = new Detail::BoundUnaryMethod( unaryMethod ); + } + + // Bind a method that takes no arguments (will be called if opt is present) + template + void bind( void (C::* nullaryMethod)() ) { + m_arg->boundField = new Detail::BoundNullaryMethod( nullaryMethod ); + } + + // Bind a free function taking a single argument - the object to operate on (no placeholder string required) + template + void bind( void (* unaryFunction)( C& ) ) { + m_arg->boundField = new Detail::BoundUnaryFunction( unaryFunction ); + } + + // Bind a free function taking a single argument - the object to operate on (requires a placeholder string) + template + void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) { + m_arg->boundField = new Detail::BoundBinaryFunction( binaryFunction ); + m_arg->placeholder = placeholder; + } + + ArgBuilder& describe( std::string const& description ) { + m_arg->description = description; + return *this; + } + ArgBuilder& detail( std::string const& detail ) { + m_arg->detail = detail; + return *this; + } + + protected: + Arg* m_arg; + }; + + class OptBuilder : public ArgBuilder { + public: + OptBuilder( Arg* arg ) : ArgBuilder( arg ) {} + OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {} + + OptBuilder& operator[]( std::string const& optName ) { + addOptName( *ArgBuilder::m_arg, optName ); + return *this; + } + }; + + public: + + CommandLine() + : m_boundProcessName( new Detail::NullBinder() ), + m_highestSpecifiedArgPosition( 0 ), + m_throwOnUnrecognisedTokens( false ) + {} + CommandLine( CommandLine const& other ) + : m_boundProcessName( other.m_boundProcessName ), + m_options ( other.m_options ), + m_positionalArgs( other.m_positionalArgs ), + m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ), + m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens ) + { + if( other.m_floatingArg.get() ) + m_floatingArg.reset( new Arg( *other.m_floatingArg ) ); + } + + CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) { + m_throwOnUnrecognisedTokens = shouldThrow; + return *this; + } + + OptBuilder operator[]( std::string const& optName ) { + m_options.push_back( Arg() ); + addOptName( m_options.back(), optName ); + OptBuilder builder( &m_options.back() ); + return builder; + } + + ArgBuilder operator[]( int position ) { + m_positionalArgs.insert( std::make_pair( position, Arg() ) ); + if( position > m_highestSpecifiedArgPosition ) + m_highestSpecifiedArgPosition = position; + setPositionalArg( m_positionalArgs[position], position ); + ArgBuilder builder( &m_positionalArgs[position] ); + return builder; + } + + // Invoke this with the _ instance + ArgBuilder operator[]( UnpositionalTag ) { + if( m_floatingArg.get() ) + throw std::logic_error( "Only one unpositional argument can be added" ); + m_floatingArg.reset( new Arg() ); + ArgBuilder builder( m_floatingArg.get() ); + return builder; + } + + template + void bindProcessName( M C::* field ) { + m_boundProcessName = new Detail::BoundDataMember( field ); + } + template + void bindProcessName( void (C::*_unaryMethod)( M ) ) { + m_boundProcessName = new Detail::BoundUnaryMethod( _unaryMethod ); + } + + void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const { + typename std::vector::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it; + std::size_t maxWidth = 0; + for( it = itBegin; it != itEnd; ++it ) + maxWidth = (std::max)( maxWidth, it->commands().size() ); + + for( it = itBegin; it != itEnd; ++it ) { + Detail::Text usage( it->commands(), Detail::TextAttributes() + .setWidth( maxWidth+indent ) + .setIndent( indent ) ); + Detail::Text desc( it->description, Detail::TextAttributes() + .setWidth( width - maxWidth - 3 ) ); + + for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) { + std::string usageCol = i < usage.size() ? usage[i] : ""; + os << usageCol; + + if( i < desc.size() && !desc[i].empty() ) + os << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' ) + << desc[i]; + os << "\n"; + } + } + } + std::string optUsage() const { + std::ostringstream oss; + optUsage( oss ); + return oss.str(); + } + + void argSynopsis( std::ostream& os ) const { + for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) { + if( i > 1 ) + os << " "; + typename std::map::const_iterator it = m_positionalArgs.find( i ); + if( it != m_positionalArgs.end() ) + os << "<" << it->second.placeholder << ">"; + else if( m_floatingArg.get() ) + os << "<" << m_floatingArg->placeholder << ">"; + else + throw std::logic_error( "non consecutive positional arguments with no floating args" ); + } + // !TBD No indication of mandatory args + if( m_floatingArg.get() ) { + if( m_highestSpecifiedArgPosition > 1 ) + os << " "; + os << "[<" << m_floatingArg->placeholder << "> ...]"; + } + } + std::string argSynopsis() const { + std::ostringstream oss; + argSynopsis( oss ); + return oss.str(); + } + + void usage( std::ostream& os, std::string const& procName ) const { + validate(); + os << "usage:\n " << procName << " "; + argSynopsis( os ); + if( !m_options.empty() ) { + os << " [options]\n\nwhere options are: \n"; + optUsage( os, 2 ); + } + os << "\n"; + } + std::string usage( std::string const& procName ) const { + std::ostringstream oss; + usage( oss, procName ); + return oss.str(); + } + + ConfigT parse( std::vector const& args ) const { + ConfigT config; + parseInto( args, config ); + return config; + } + + std::vector parseInto( std::vector const& args, ConfigT& config ) const { + std::string processName = args[0]; + std::size_t lastSlash = processName.find_last_of( "/\\" ); + if( lastSlash != std::string::npos ) + processName = processName.substr( lastSlash+1 ); + m_boundProcessName.set( config, processName ); + std::vector tokens; + Parser parser; + parser.parseIntoTokens( args, tokens ); + return populate( tokens, config ); + } + + std::vector populate( std::vector const& tokens, ConfigT& config ) const { + validate(); + std::vector unusedTokens = populateOptions( tokens, config ); + unusedTokens = populateFixedArgs( unusedTokens, config ); + unusedTokens = populateFloatingArgs( unusedTokens, config ); + return unusedTokens; + } + + std::vector populateOptions( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + std::vector errors; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::vector::const_iterator it = m_options.begin(), itEnd = m_options.end(); + for(; it != itEnd; ++it ) { + Arg const& arg = *it; + + try { + if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) || + ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) { + if( arg.takesArg() ) { + if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional ) + errors.push_back( "Expected argument to option: " + token.data ); + else + arg.boundField.set( config, tokens[++i].data ); + } + else { + arg.boundField.set( config, "true" ); + } + break; + } + } + catch( std::exception& ex ) { + errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" ); + } + } + if( it == itEnd ) { + if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens ) + unusedTokens.push_back( token ); + else if( errors.empty() && m_throwOnUnrecognisedTokens ) + errors.push_back( "unrecognised option: " + token.data ); + } + } + if( !errors.empty() ) { + std::ostringstream oss; + for( std::vector::const_iterator it = errors.begin(), itEnd = errors.end(); + it != itEnd; + ++it ) { + if( it != errors.begin() ) + oss << "\n"; + oss << *it; + } + throw std::runtime_error( oss.str() ); + } + return unusedTokens; + } + std::vector populateFixedArgs( std::vector const& tokens, ConfigT& config ) const { + std::vector unusedTokens; + int position = 1; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + typename std::map::const_iterator it = m_positionalArgs.find( position ); + if( it != m_positionalArgs.end() ) + it->second.boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + if( token.type == Parser::Token::Positional ) + position++; + } + return unusedTokens; + } + std::vector populateFloatingArgs( std::vector const& tokens, ConfigT& config ) const { + if( !m_floatingArg.get() ) + return tokens; + std::vector unusedTokens; + for( std::size_t i = 0; i < tokens.size(); ++i ) { + Parser::Token const& token = tokens[i]; + if( token.type == Parser::Token::Positional ) + m_floatingArg->boundField.set( config, token.data ); + else + unusedTokens.push_back( token ); + } + return unusedTokens; + } + + void validate() const + { + if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() ) + throw std::logic_error( "No options or arguments specified" ); + + for( typename std::vector::const_iterator it = m_options.begin(), + itEnd = m_options.end(); + it != itEnd; ++it ) + it->validate(); + } + + private: + Detail::BoundArgFunction m_boundProcessName; + std::vector m_options; + std::map m_positionalArgs; + ArgAutoPtr m_floatingArg; + int m_highestSpecifiedArgPosition; + bool m_throwOnUnrecognisedTokens; + }; + +} // end namespace Clara + +STITCH_CLARA_CLOSE_NAMESPACE +#undef STITCH_CLARA_OPEN_NAMESPACE +#undef STITCH_CLARA_CLOSE_NAMESPACE + +#endif // TWOBLUECUBES_CLARA_H_INCLUDED +#undef STITCH_CLARA_OPEN_NAMESPACE + +// Restore Clara's value for console width, if present +#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#include +#include + +namespace Catch { + + inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; } + inline void abortAfterX( ConfigData& config, int x ) { + if( x < 1 ) + throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" ); + config.abortAfter = x; + } + inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); } + inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); } + inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); } + + inline void addWarning( ConfigData& config, std::string const& _warning ) { + if( _warning == "NoAssertions" ) + config.warnings = static_cast( config.warnings | WarnAbout::NoAssertions ); + else + throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' ); + } + inline void setOrder( ConfigData& config, std::string const& order ) { + if( startsWith( "declared", order ) ) + config.runOrder = RunTests::InDeclarationOrder; + else if( startsWith( "lexical", order ) ) + config.runOrder = RunTests::InLexicographicalOrder; + else if( startsWith( "random", order ) ) + config.runOrder = RunTests::InRandomOrder; + else + throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' ); + } + inline void setRngSeed( ConfigData& config, std::string const& seed ) { + if( seed == "time" ) { + config.rngSeed = static_cast( std::time(0) ); + } + else { + std::stringstream ss; + ss << seed; + ss >> config.rngSeed; + if( ss.fail() ) + throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" ); + } + } + inline void setVerbosity( ConfigData& config, int level ) { + // !TBD: accept strings? + config.verbosity = static_cast( level ); + } + inline void setShowDurations( ConfigData& config, bool _showDurations ) { + config.showDurations = _showDurations + ? ShowDurations::Always + : ShowDurations::Never; + } + inline void setUseColour( ConfigData& config, std::string const& value ) { + std::string mode = toLower( value ); + + if( mode == "yes" ) + config.useColour = UseColour::Yes; + else if( mode == "no" ) + config.useColour = UseColour::No; + else if( mode == "auto" ) + config.useColour = UseColour::Auto; + else + throw std::runtime_error( "colour mode must be one of: auto, yes or no" ); + } + inline void forceColour( ConfigData& config ) { + config.useColour = UseColour::Yes; + } + inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) { + std::ifstream f( _filename.c_str() ); + if( !f.is_open() ) + throw std::domain_error( "Unable to load input file: " + _filename ); + + std::string line; + while( std::getline( f, line ) ) { + line = trim(line); + if( !line.empty() && !startsWith( line, '#' ) ) { + if( !startsWith( line, '"' ) ) + line = '"' + line + '"'; + addTestOrTags( config, line + ',' ); + } + } + } + + inline Clara::CommandLine makeCommandLineParser() { + + using namespace Clara; + CommandLine cli; + + cli.bindProcessName( &ConfigData::processName ); + + cli["-?"]["-h"]["--help"] + .describe( "display usage information" ) + .bind( &ConfigData::showHelp ); + + cli["-l"]["--list-tests"] + .describe( "list all/matching test cases" ) + .bind( &ConfigData::listTests ); + + cli["-t"]["--list-tags"] + .describe( "list all/matching tags" ) + .bind( &ConfigData::listTags ); + + cli["-s"]["--success"] + .describe( "include successful tests in output" ) + .bind( &ConfigData::showSuccessfulTests ); + + cli["-b"]["--break"] + .describe( "break into debugger on failure" ) + .bind( &ConfigData::shouldDebugBreak ); + + cli["-e"]["--nothrow"] + .describe( "skip exception tests" ) + .bind( &ConfigData::noThrow ); + + cli["-i"]["--invisibles"] + .describe( "show invisibles (tabs, newlines)" ) + .bind( &ConfigData::showInvisibles ); + + cli["-o"]["--out"] + .describe( "output filename" ) + .bind( &ConfigData::outputFilename, "filename" ); + + cli["-r"]["--reporter"] +// .placeholder( "name[:filename]" ) + .describe( "reporter to use (defaults to console)" ) + .bind( &addReporterName, "name" ); + + cli["-n"]["--name"] + .describe( "suite name" ) + .bind( &ConfigData::name, "name" ); + + cli["-a"]["--abort"] + .describe( "abort at first failure" ) + .bind( &abortAfterFirst ); + + cli["-x"]["--abortx"] + .describe( "abort after x failures" ) + .bind( &abortAfterX, "no. failures" ); + + cli["-w"]["--warn"] + .describe( "enable warnings" ) + .bind( &addWarning, "warning name" ); + +// - needs updating if reinstated +// cli.into( &setVerbosity ) +// .describe( "level of verbosity (0=no output)" ) +// .shortOpt( "v") +// .longOpt( "verbosity" ) +// .placeholder( "level" ); + + cli[_] + .describe( "which test or tests to use" ) + .bind( &addTestOrTags, "test name, pattern or tags" ); + + cli["-d"]["--durations"] + .describe( "show test durations" ) + .bind( &setShowDurations, "yes|no" ); + + cli["-f"]["--input-file"] + .describe( "load test names to run from a file" ) + .bind( &loadTestNamesFromFile, "filename" ); + + cli["-#"]["--filenames-as-tags"] + .describe( "adds a tag for the filename" ) + .bind( &ConfigData::filenamesAsTags ); + + cli["-c"]["--section"] + .describe( "specify section to run" ) + .bind( &addSectionToRun, "section name" ); + + // Less common commands which don't have a short form + cli["--list-test-names-only"] + .describe( "list all/matching test cases names only" ) + .bind( &ConfigData::listTestNamesOnly ); + + cli["--list-reporters"] + .describe( "list all reporters" ) + .bind( &ConfigData::listReporters ); + + cli["--order"] + .describe( "test case order (defaults to decl)" ) + .bind( &setOrder, "decl|lex|rand" ); + + cli["--rng-seed"] + .describe( "set a specific seed for random numbers" ) + .bind( &setRngSeed, "'time'|number" ); + + cli["--force-colour"] + .describe( "force colourised output (deprecated)" ) + .bind( &forceColour ); + + cli["--use-colour"] + .describe( "should output be colourised" ) + .bind( &setUseColour, "yes|no" ); + + return cli; + } + +} // end namespace Catch + +// #included from: internal/catch_list.hpp +#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED + +// #included from: catch_text.h +#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED + +#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH + +#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch +// #included from: ../external/tbc_text_format.h +// Only use header guard if we are not using an outer namespace +#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +# endif +# else +# define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED +# endif +#endif +#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#include +#include +#include + +// Use optional outer namespace +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE { +#endif + +namespace Tbc { + +#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH + const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH; +#else + const unsigned int consoleWidth = 80; +#endif + + struct TextAttributes { + TextAttributes() + : initialIndent( std::string::npos ), + indent( 0 ), + width( consoleWidth-1 ) + {} + + TextAttributes& setInitialIndent( std::size_t _value ) { initialIndent = _value; return *this; } + TextAttributes& setIndent( std::size_t _value ) { indent = _value; return *this; } + TextAttributes& setWidth( std::size_t _value ) { width = _value; return *this; } + + std::size_t initialIndent; // indent of first line, or npos + std::size_t indent; // indent of subsequent lines, or all if initialIndent is npos + std::size_t width; // maximum width of text, including indent. Longer text will wrap + }; + + class Text { + public: + Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() ) + : attr( _attr ) + { + const std::string wrappableBeforeChars = "[({<\t"; + const std::string wrappableAfterChars = "])}>-,./|\\"; + const std::string wrappableInsteadOfChars = " \n\r"; + std::string indent = _attr.initialIndent != std::string::npos + ? std::string( _attr.initialIndent, ' ' ) + : std::string( _attr.indent, ' ' ); + + typedef std::string::const_iterator iterator; + iterator it = _str.begin(); + const iterator strEnd = _str.end(); + + while( it != strEnd ) { + + if( lines.size() >= 1000 ) { + lines.push_back( "... message truncated due to excessive size" ); + return; + } + + std::string suffix; + std::size_t width = (std::min)( static_cast( strEnd-it ), _attr.width-static_cast( indent.size() ) ); + iterator itEnd = it+width; + iterator itNext = _str.end(); + + iterator itNewLine = std::find( it, itEnd, '\n' ); + if( itNewLine != itEnd ) + itEnd = itNewLine; + + if( itEnd != strEnd ) { + bool foundWrapPoint = false; + iterator findIt = itEnd; + do { + if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) { + itEnd = findIt+1; + itNext = findIt+1; + foundWrapPoint = true; + } + else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) { + itEnd = findIt; + itNext = findIt; + foundWrapPoint = true; + } + else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) { + itNext = findIt+1; + itEnd = findIt; + foundWrapPoint = true; + } + if( findIt == it ) + break; + else + --findIt; + } + while( !foundWrapPoint ); + + if( !foundWrapPoint ) { + // No good wrap char, so we'll break mid word and add a hyphen + --itEnd; + itNext = itEnd; + suffix = "-"; + } + else { + while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos ) + --itEnd; + } + } + lines.push_back( indent + std::string( it, itEnd ) + suffix ); + + if( indent.size() != _attr.indent ) + indent = std::string( _attr.indent, ' ' ); + it = itNext; + } + } + + typedef std::vector::const_iterator const_iterator; + + const_iterator begin() const { return lines.begin(); } + const_iterator end() const { return lines.end(); } + std::string const& last() const { return lines.back(); } + std::size_t size() const { return lines.size(); } + std::string const& operator[]( std::size_t _index ) const { return lines[_index]; } + std::string toString() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + + inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) { + for( Text::const_iterator it = _text.begin(), itEnd = _text.end(); + it != itEnd; ++it ) { + if( it != _text.begin() ) + _stream << "\n"; + _stream << *it; + } + return _stream; + } + + private: + std::string str; + TextAttributes attr; + std::vector lines; + }; + +} // end namespace Tbc + +#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE +} // end outer namespace +#endif + +#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED +#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE + +namespace Catch { + using Tbc::Text; + using Tbc::TextAttributes; +} + +// #included from: catch_console_colour.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED + +namespace Catch { + + struct Colour { + enum Code { + None = 0, + + White, + Red, + Green, + Blue, + Cyan, + Yellow, + Grey, + + Bright = 0x10, + + BrightRed = Bright | Red, + BrightGreen = Bright | Green, + LightGrey = Bright | Grey, + BrightWhite = Bright | White, + + // By intention + FileName = LightGrey, + Warning = Yellow, + ResultError = BrightRed, + ResultSuccess = BrightGreen, + ResultExpectedFailure = Warning, + + Error = BrightRed, + Success = Green, + + OriginalExpression = Cyan, + ReconstructedExpression = Yellow, + + SecondaryText = LightGrey, + Headers = White + }; + + // Use constructed object for RAII guard + Colour( Code _colourCode ); + Colour( Colour const& other ); + ~Colour(); + + // Use static method for one-shot changes + static void use( Code _colourCode ); + + private: + bool m_moved; + }; + + inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; } + +} // end namespace Catch + +// #included from: catch_interfaces_reporter.h +#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED + +#include +#include +#include + +namespace Catch +{ + struct ReporterConfig { + explicit ReporterConfig( Ptr const& _fullConfig ) + : m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {} + + ReporterConfig( Ptr const& _fullConfig, std::ostream& _stream ) + : m_stream( &_stream ), m_fullConfig( _fullConfig ) {} + + std::ostream& stream() const { return *m_stream; } + Ptr fullConfig() const { return m_fullConfig; } + + private: + std::ostream* m_stream; + Ptr m_fullConfig; + }; + + struct ReporterPreferences { + ReporterPreferences() + : shouldRedirectStdOut( false ) + {} + + bool shouldRedirectStdOut; + }; + + template + struct LazyStat : Option { + LazyStat() : used( false ) {} + LazyStat& operator=( T const& _value ) { + Option::operator=( _value ); + used = false; + return *this; + } + void reset() { + Option::reset(); + used = false; + } + bool used; + }; + + struct TestRunInfo { + TestRunInfo( std::string const& _name ) : name( _name ) {} + std::string name; + }; + struct GroupInfo { + GroupInfo( std::string const& _name, + std::size_t _groupIndex, + std::size_t _groupsCount ) + : name( _name ), + groupIndex( _groupIndex ), + groupsCounts( _groupsCount ) + {} + + std::string name; + std::size_t groupIndex; + std::size_t groupsCounts; + }; + + struct AssertionStats { + AssertionStats( AssertionResult const& _assertionResult, + std::vector const& _infoMessages, + Totals const& _totals ) + : assertionResult( _assertionResult ), + infoMessages( _infoMessages ), + totals( _totals ) + { + if( assertionResult.hasMessage() ) { + // Copy message into messages list. + // !TBD This should have been done earlier, somewhere + MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() ); + builder << assertionResult.getMessage(); + builder.m_info.message = builder.m_stream.str(); + + infoMessages.push_back( builder.m_info ); + } + } + virtual ~AssertionStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + AssertionStats( AssertionStats const& ) = default; + AssertionStats( AssertionStats && ) = default; + AssertionStats& operator = ( AssertionStats const& ) = default; + AssertionStats& operator = ( AssertionStats && ) = default; +# endif + + AssertionResult assertionResult; + std::vector infoMessages; + Totals totals; + }; + + struct SectionStats { + SectionStats( SectionInfo const& _sectionInfo, + Counts const& _assertions, + double _durationInSeconds, + bool _missingAssertions ) + : sectionInfo( _sectionInfo ), + assertions( _assertions ), + durationInSeconds( _durationInSeconds ), + missingAssertions( _missingAssertions ) + {} + virtual ~SectionStats(); +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + SectionStats( SectionStats const& ) = default; + SectionStats( SectionStats && ) = default; + SectionStats& operator = ( SectionStats const& ) = default; + SectionStats& operator = ( SectionStats && ) = default; +# endif + + SectionInfo sectionInfo; + Counts assertions; + double durationInSeconds; + bool missingAssertions; + }; + + struct TestCaseStats { + TestCaseStats( TestCaseInfo const& _testInfo, + Totals const& _totals, + std::string const& _stdOut, + std::string const& _stdErr, + bool _aborting ) + : testInfo( _testInfo ), + totals( _totals ), + stdOut( _stdOut ), + stdErr( _stdErr ), + aborting( _aborting ) + {} + virtual ~TestCaseStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestCaseStats( TestCaseStats const& ) = default; + TestCaseStats( TestCaseStats && ) = default; + TestCaseStats& operator = ( TestCaseStats const& ) = default; + TestCaseStats& operator = ( TestCaseStats && ) = default; +# endif + + TestCaseInfo testInfo; + Totals totals; + std::string stdOut; + std::string stdErr; + bool aborting; + }; + + struct TestGroupStats { + TestGroupStats( GroupInfo const& _groupInfo, + Totals const& _totals, + bool _aborting ) + : groupInfo( _groupInfo ), + totals( _totals ), + aborting( _aborting ) + {} + TestGroupStats( GroupInfo const& _groupInfo ) + : groupInfo( _groupInfo ), + aborting( false ) + {} + virtual ~TestGroupStats(); + +# ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestGroupStats( TestGroupStats const& ) = default; + TestGroupStats( TestGroupStats && ) = default; + TestGroupStats& operator = ( TestGroupStats const& ) = default; + TestGroupStats& operator = ( TestGroupStats && ) = default; +# endif + + GroupInfo groupInfo; + Totals totals; + bool aborting; + }; + + struct TestRunStats { + TestRunStats( TestRunInfo const& _runInfo, + Totals const& _totals, + bool _aborting ) + : runInfo( _runInfo ), + totals( _totals ), + aborting( _aborting ) + {} + virtual ~TestRunStats(); + +# ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS + TestRunStats( TestRunStats const& _other ) + : runInfo( _other.runInfo ), + totals( _other.totals ), + aborting( _other.aborting ) + {} +# else + TestRunStats( TestRunStats const& ) = default; + TestRunStats( TestRunStats && ) = default; + TestRunStats& operator = ( TestRunStats const& ) = default; + TestRunStats& operator = ( TestRunStats && ) = default; +# endif + + TestRunInfo runInfo; + Totals totals; + bool aborting; + }; + + class MultipleReporters; + + struct IStreamingReporter : IShared { + virtual ~IStreamingReporter(); + + // Implementing class must also provide the following static method: + // static std::string getDescription(); + + virtual ReporterPreferences getPreferences() const = 0; + + virtual void noMatchingTestCases( std::string const& spec ) = 0; + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0; + virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0; + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0; + virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0; + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0; + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0; + + virtual void sectionEnded( SectionStats const& sectionStats ) = 0; + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0; + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0; + virtual void testRunEnded( TestRunStats const& testRunStats ) = 0; + + virtual void skipTest( TestCaseInfo const& testInfo ) = 0; + + virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; } + }; + + struct IReporterFactory : IShared { + virtual ~IReporterFactory(); + virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0; + virtual std::string getDescription() const = 0; + }; + + struct IReporterRegistry { + typedef std::map > FactoryMap; + typedef std::vector > Listeners; + + virtual ~IReporterRegistry(); + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const = 0; + virtual FactoryMap const& getFactories() const = 0; + virtual Listeners const& getListeners() const = 0; + }; + + Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ); + +} + +#include +#include + +namespace Catch { + + inline std::size_t listTests( Config const& config ) { + + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Matching test cases:\n"; + else { + Catch::cout() << "All available test cases:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::size_t matchedTests = 0; + TextAttributes nameAttr, tagsAttr; + nameAttr.setInitialIndent( 2 ).setIndent( 4 ); + tagsAttr.setIndent( 6 ); + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + Colour::Code colour = testCaseInfo.isHidden() + ? Colour::SecondaryText + : Colour::None; + Colour colourGuard( colour ); + + Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl; + if( !testCaseInfo.tags.empty() ) + Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl; + } + + if( !config.testSpec().hasFilters() ) + Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl; + else + Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl; + return matchedTests; + } + + inline std::size_t listTestsNamesOnly( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( !config.testSpec().hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + std::size_t matchedTests = 0; + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + matchedTests++; + TestCaseInfo const& testCaseInfo = it->getTestCaseInfo(); + if( startsWith( testCaseInfo.name, '#' ) ) + Catch::cout() << '"' << testCaseInfo.name << '"' << std::endl; + else + Catch::cout() << testCaseInfo.name << std::endl; + } + return matchedTests; + } + + struct TagInfo { + TagInfo() : count ( 0 ) {} + void add( std::string const& spelling ) { + ++count; + spellings.insert( spelling ); + } + std::string all() const { + std::string out; + for( std::set::const_iterator it = spellings.begin(), itEnd = spellings.end(); + it != itEnd; + ++it ) + out += "[" + *it + "]"; + return out; + } + std::set spellings; + std::size_t count; + }; + + inline std::size_t listTags( Config const& config ) { + TestSpec testSpec = config.testSpec(); + if( config.testSpec().hasFilters() ) + Catch::cout() << "Tags for matching test cases:\n"; + else { + Catch::cout() << "All available tags:\n"; + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec(); + } + + std::map tagCounts; + + std::vector matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config ); + for( std::vector::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end(); + it != itEnd; + ++it ) { + for( std::set::const_iterator tagIt = it->getTestCaseInfo().tags.begin(), + tagItEnd = it->getTestCaseInfo().tags.end(); + tagIt != tagItEnd; + ++tagIt ) { + std::string tagName = *tagIt; + std::string lcaseTagName = toLower( tagName ); + std::map::iterator countIt = tagCounts.find( lcaseTagName ); + if( countIt == tagCounts.end() ) + countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first; + countIt->second.add( tagName ); + } + } + + for( std::map::const_iterator countIt = tagCounts.begin(), + countItEnd = tagCounts.end(); + countIt != countItEnd; + ++countIt ) { + std::ostringstream oss; + oss << " " << std::setw(2) << countIt->second.count << " "; + Text wrapper( countIt->second.all(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( oss.str().size() ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) ); + Catch::cout() << oss.str() << wrapper << '\n'; + } + Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl; + return tagCounts.size(); + } + + inline std::size_t listReporters( Config const& /*config*/ ) { + Catch::cout() << "Available reporters:\n"; + IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories(); + IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it; + std::size_t maxNameLen = 0; + for(it = itBegin; it != itEnd; ++it ) + maxNameLen = (std::max)( maxNameLen, it->first.size() ); + + for(it = itBegin; it != itEnd; ++it ) { + Text wrapper( it->second->getDescription(), TextAttributes() + .setInitialIndent( 0 ) + .setIndent( 7+maxNameLen ) + .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) ); + Catch::cout() << " " + << it->first + << ':' + << std::string( maxNameLen - it->first.size() + 2, ' ' ) + << wrapper << '\n'; + } + Catch::cout() << std::endl; + return factories.size(); + } + + inline Option list( Config const& config ) { + Option listedCount; + if( config.listTests() ) + listedCount = listedCount.valueOr(0) + listTests( config ); + if( config.listTestNamesOnly() ) + listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config ); + if( config.listTags() ) + listedCount = listedCount.valueOr(0) + listTags( config ); + if( config.listReporters() ) + listedCount = listedCount.valueOr(0) + listReporters( config ); + return listedCount; + } + +} // end namespace Catch + +// #included from: internal/catch_run_context.hpp +#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED + +// #included from: catch_test_case_tracker.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED + +#include +#include +#include +#include +#include +#include + +namespace Catch { +namespace TestCaseTracking { + + struct NameAndLocation { + std::string name; + SourceLineInfo location; + + NameAndLocation( std::string const& _name, SourceLineInfo const& _location ) + : name( _name ), + location( _location ) + {} + }; + + struct ITracker : SharedImpl<> { + virtual ~ITracker(); + + // static queries + virtual NameAndLocation const& nameAndLocation() const = 0; + + // dynamic queries + virtual bool isComplete() const = 0; // Successfully completed or failed + virtual bool isSuccessfullyCompleted() const = 0; + virtual bool isOpen() const = 0; // Started but not complete + virtual bool hasChildren() const = 0; + + virtual ITracker& parent() = 0; + + // actions + virtual void close() = 0; // Successfully complete + virtual void fail() = 0; + virtual void markAsNeedingAnotherRun() = 0; + + virtual void addChild( Ptr const& child ) = 0; + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0; + virtual void openChild() = 0; + + // Debug/ checking + virtual bool isSectionTracker() const = 0; + virtual bool isIndexTracker() const = 0; + }; + + class TrackerContext { + + enum RunState { + NotStarted, + Executing, + CompletedCycle + }; + + Ptr m_rootTracker; + ITracker* m_currentTracker; + RunState m_runState; + + public: + + static TrackerContext& instance() { + static TrackerContext s_instance; + return s_instance; + } + + TrackerContext() + : m_currentTracker( CATCH_NULL ), + m_runState( NotStarted ) + {} + + ITracker& startRun(); + + void endRun() { + m_rootTracker.reset(); + m_currentTracker = CATCH_NULL; + m_runState = NotStarted; + } + + void startCycle() { + m_currentTracker = m_rootTracker.get(); + m_runState = Executing; + } + void completeCycle() { + m_runState = CompletedCycle; + } + + bool completedCycle() const { + return m_runState == CompletedCycle; + } + ITracker& currentTracker() { + return *m_currentTracker; + } + void setCurrentTracker( ITracker* tracker ) { + m_currentTracker = tracker; + } + }; + + class TrackerBase : public ITracker { + protected: + enum CycleState { + NotStarted, + Executing, + ExecutingChildren, + NeedsAnotherRun, + CompletedSuccessfully, + Failed + }; + class TrackerHasName { + NameAndLocation m_nameAndLocation; + public: + TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {} + bool operator ()( Ptr const& tracker ) { + return + tracker->nameAndLocation().name == m_nameAndLocation.name && + tracker->nameAndLocation().location == m_nameAndLocation.location; + } + }; + typedef std::vector > Children; + NameAndLocation m_nameAndLocation; + TrackerContext& m_ctx; + ITracker* m_parent; + Children m_children; + CycleState m_runState; + public: + TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : m_nameAndLocation( nameAndLocation ), + m_ctx( ctx ), + m_parent( parent ), + m_runState( NotStarted ) + {} + virtual ~TrackerBase(); + + virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE { + return m_nameAndLocation; + } + virtual bool isComplete() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully || m_runState == Failed; + } + virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE { + return m_runState == CompletedSuccessfully; + } + virtual bool isOpen() const CATCH_OVERRIDE { + return m_runState != NotStarted && !isComplete(); + } + virtual bool hasChildren() const CATCH_OVERRIDE { + return !m_children.empty(); + } + + virtual void addChild( Ptr const& child ) CATCH_OVERRIDE { + m_children.push_back( child ); + } + + virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE { + Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) ); + return( it != m_children.end() ) + ? it->get() + : CATCH_NULL; + } + virtual ITracker& parent() CATCH_OVERRIDE { + assert( m_parent ); // Should always be non-null except for root + return *m_parent; + } + + virtual void openChild() CATCH_OVERRIDE { + if( m_runState != ExecutingChildren ) { + m_runState = ExecutingChildren; + if( m_parent ) + m_parent->openChild(); + } + } + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; } + virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; } + + void open() { + m_runState = Executing; + moveToThis(); + if( m_parent ) + m_parent->openChild(); + } + + virtual void close() CATCH_OVERRIDE { + + // Close any still open children (e.g. generators) + while( &m_ctx.currentTracker() != this ) + m_ctx.currentTracker().close(); + + switch( m_runState ) { + case NotStarted: + case CompletedSuccessfully: + case Failed: + throw std::logic_error( "Illogical state" ); + + case NeedsAnotherRun: + break;; + + case Executing: + m_runState = CompletedSuccessfully; + break; + case ExecutingChildren: + if( m_children.empty() || m_children.back()->isComplete() ) + m_runState = CompletedSuccessfully; + break; + + default: + throw std::logic_error( "Unexpected state" ); + } + moveToParent(); + m_ctx.completeCycle(); + } + virtual void fail() CATCH_OVERRIDE { + m_runState = Failed; + if( m_parent ) + m_parent->markAsNeedingAnotherRun(); + moveToParent(); + m_ctx.completeCycle(); + } + virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE { + m_runState = NeedsAnotherRun; + } + private: + void moveToParent() { + assert( m_parent ); + m_ctx.setCurrentTracker( m_parent ); + } + void moveToThis() { + m_ctx.setCurrentTracker( this ); + } + }; + + class SectionTracker : public TrackerBase { + std::vector m_filters; + public: + SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ) + : TrackerBase( nameAndLocation, ctx, parent ) + { + if( parent ) { + while( !parent->isSectionTracker() ) + parent = &parent->parent(); + + SectionTracker& parentSection = static_cast( *parent ); + addNextFilters( parentSection.m_filters ); + } + } + virtual ~SectionTracker(); + + virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; } + + static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) { + SectionTracker* section = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isSectionTracker() ); + section = static_cast( childTracker ); + } + else { + section = new SectionTracker( nameAndLocation, ctx, ¤tTracker ); + currentTracker.addChild( section ); + } + if( !ctx.completedCycle() ) + section->tryOpen(); + return *section; + } + + void tryOpen() { + if( !isComplete() && (m_filters.empty() || m_filters[0].empty() || m_filters[0] == m_nameAndLocation.name ) ) + open(); + } + + void addInitialFilters( std::vector const& filters ) { + if( !filters.empty() ) { + m_filters.push_back(""); // Root - should never be consulted + m_filters.push_back(""); // Test Case - not a section filter + std::copy( filters.begin(), filters.end(), std::back_inserter( m_filters ) ); + } + } + void addNextFilters( std::vector const& filters ) { + if( filters.size() > 1 ) + std::copy( filters.begin()+1, filters.end(), std::back_inserter( m_filters ) ); + } + }; + + class IndexTracker : public TrackerBase { + int m_size; + int m_index; + public: + IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size ) + : TrackerBase( nameAndLocation, ctx, parent ), + m_size( size ), + m_index( -1 ) + {} + virtual ~IndexTracker(); + + virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; } + + static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) { + IndexTracker* tracker = CATCH_NULL; + + ITracker& currentTracker = ctx.currentTracker(); + if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) { + assert( childTracker ); + assert( childTracker->isIndexTracker() ); + tracker = static_cast( childTracker ); + } + else { + tracker = new IndexTracker( nameAndLocation, ctx, ¤tTracker, size ); + currentTracker.addChild( tracker ); + } + + if( !ctx.completedCycle() && !tracker->isComplete() ) { + if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun ) + tracker->moveNext(); + tracker->open(); + } + + return *tracker; + } + + int index() const { return m_index; } + + void moveNext() { + m_index++; + m_children.clear(); + } + + virtual void close() CATCH_OVERRIDE { + TrackerBase::close(); + if( m_runState == CompletedSuccessfully && m_index < m_size-1 ) + m_runState = Executing; + } + }; + + inline ITracker& TrackerContext::startRun() { + m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL ); + m_currentTracker = CATCH_NULL; + m_runState = Executing; + return *m_rootTracker; + } + +} // namespace TestCaseTracking + +using TestCaseTracking::ITracker; +using TestCaseTracking::TrackerContext; +using TestCaseTracking::SectionTracker; +using TestCaseTracking::IndexTracker; + +} // namespace Catch + +// #included from: catch_fatal_condition.hpp +#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED + +namespace Catch { + + // Report the error condition + inline void reportFatal( std::string const& message ) { + IContext& context = Catch::getCurrentContext(); + IResultCapture* resultCapture = context.getResultCapture(); + resultCapture->handleFatalErrorCondition( message ); + } + +} // namespace Catch + +#if defined ( CATCH_PLATFORM_WINDOWS ) ///////////////////////////////////////// +// #included from: catch_windows_h_proxy.h + +#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED + +#ifdef CATCH_DEFINES_NOMINMAX +# define NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif + +#ifdef __AFXDLL +#include +#else +#include +#endif + +#ifdef CATCH_DEFINES_NOMINMAX +# undef NOMINMAX +#endif +#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN +# undef WIN32_LEAN_AND_MEAN +#endif + + +# if !defined ( CATCH_CONFIG_WINDOWS_SEH ) + +namespace Catch { + struct FatalConditionHandler { + void reset() {} + }; +} + +# else // CATCH_CONFIG_WINDOWS_SEH is defined + +namespace Catch { + + struct SignalDefs { DWORD id; const char* name; }; + extern SignalDefs signalDefs[]; + // There is no 1-1 mapping between signals and windows exceptions. + // Windows can easily distinguish between SO and SigSegV, + // but SigInt, SigTerm, etc are handled differently. + SignalDefs signalDefs[] = { + { EXCEPTION_ILLEGAL_INSTRUCTION, "SIGILL - Illegal instruction signal" }, + { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" }, + { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" }, + { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" }, + }; + + struct FatalConditionHandler { + + static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) { + for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) { + reset(); + reportFatal(signalDefs[i].name); + } + } + // If its not an exception we care about, pass it along. + // This stops us from eating debugger breaks etc. + return EXCEPTION_CONTINUE_SEARCH; + } + + FatalConditionHandler() { + isSet = true; + // 32k seems enough for Catch to handle stack overflow, + // but the value was found experimentally, so there is no strong guarantee + guaranteeSize = 32 * 1024; + exceptionHandlerHandle = CATCH_NULL; + // Register as first handler in current chain + exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException); + // Pass in guarantee size to be filled + SetThreadStackGuarantee(&guaranteeSize); + } + + static void reset() { + if (isSet) { + // Unregister handler and restore the old guarantee + RemoveVectoredExceptionHandler(exceptionHandlerHandle); + SetThreadStackGuarantee(&guaranteeSize); + exceptionHandlerHandle = CATCH_NULL; + isSet = false; + } + } + + ~FatalConditionHandler() { + reset(); + } + private: + static bool isSet; + static ULONG guaranteeSize; + static PVOID exceptionHandlerHandle; + }; + + bool FatalConditionHandler::isSet = false; + ULONG FatalConditionHandler::guaranteeSize = 0; + PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL; + +} // namespace Catch + +# endif // CATCH_CONFIG_WINDOWS_SEH + +#else // Not Windows - assumed to be POSIX compatible ////////////////////////// + +#include + +namespace Catch { + + struct SignalDefs { + int id; + const char* name; + }; + extern SignalDefs signalDefs[]; + SignalDefs signalDefs[] = { + { SIGINT, "SIGINT - Terminal interrupt signal" }, + { SIGILL, "SIGILL - Illegal instruction signal" }, + { SIGFPE, "SIGFPE - Floating point error signal" }, + { SIGSEGV, "SIGSEGV - Segmentation violation signal" }, + { SIGTERM, "SIGTERM - Termination request signal" }, + { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" } + }; + + struct FatalConditionHandler { + + static bool isSet; + static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)]; + static stack_t oldSigStack; + static char altStackMem[SIGSTKSZ]; + + static void handleSignal( int sig ) { + std::string name = ""; + for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) { + SignalDefs &def = signalDefs[i]; + if (sig == def.id) { + name = def.name; + break; + } + } + reset(); + reportFatal(name); + raise( sig ); + } + + FatalConditionHandler() { + isSet = true; + stack_t sigStack; + sigStack.ss_sp = altStackMem; + sigStack.ss_size = SIGSTKSZ; + sigStack.ss_flags = 0; + sigaltstack(&sigStack, &oldSigStack); + struct sigaction sa = { 0 }; + + sa.sa_handler = handleSignal; + sa.sa_flags = SA_ONSTACK; + for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) { + sigaction(signalDefs[i].id, &sa, &oldSigActions[i]); + } + } + + ~FatalConditionHandler() { + reset(); + } + static void reset() { + if( isSet ) { + // Set signals back to previous values -- hopefully nobody overwrote them in the meantime + for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) { + sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL); + } + // Return the old stack + sigaltstack(&oldSigStack, CATCH_NULL); + isSet = false; + } + } + }; + + bool FatalConditionHandler::isSet = false; + struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {}; + stack_t FatalConditionHandler::oldSigStack = {}; + char FatalConditionHandler::altStackMem[SIGSTKSZ] = {}; + +} // namespace Catch + +#endif // not Windows + +#include +#include + +namespace Catch { + + class StreamRedirect { + + public: + StreamRedirect( std::ostream& stream, std::string& targetString ) + : m_stream( stream ), + m_prevBuf( stream.rdbuf() ), + m_targetString( targetString ) + { + stream.rdbuf( m_oss.rdbuf() ); + } + + ~StreamRedirect() { + m_targetString += m_oss.str(); + m_stream.rdbuf( m_prevBuf ); + } + + private: + std::ostream& m_stream; + std::streambuf* m_prevBuf; + std::ostringstream m_oss; + std::string& m_targetString; + }; + + /////////////////////////////////////////////////////////////////////////// + + class RunContext : public IResultCapture, public IRunner { + + RunContext( RunContext const& ); + void operator =( RunContext const& ); + + public: + + explicit RunContext( Ptr const& _config, Ptr const& reporter ) + : m_runInfo( _config->name() ), + m_context( getCurrentMutableContext() ), + m_activeTestCase( CATCH_NULL ), + m_config( _config ), + m_reporter( reporter ) + { + m_context.setRunner( this ); + m_context.setConfig( m_config ); + m_context.setResultCapture( this ); + m_reporter->testRunStarting( m_runInfo ); + } + + virtual ~RunContext() { + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) ); + } + + void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) ); + } + void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) { + m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) ); + } + + Totals runTest( TestCase const& testCase ) { + Totals prevTotals = m_totals; + + std::string redirectedCout; + std::string redirectedCerr; + + TestCaseInfo testInfo = testCase.getTestCaseInfo(); + + m_reporter->testCaseStarting( testInfo ); + + m_activeTestCase = &testCase; + + do { + ITracker& rootTracker = m_trackerContext.startRun(); + assert( rootTracker.isSectionTracker() ); + static_cast( rootTracker ).addInitialFilters( m_config->getSectionsToRun() ); + do { + m_trackerContext.startCycle(); + m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) ); + runCurrentTest( redirectedCout, redirectedCerr ); + } + while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() ); + } + // !TBD: deprecated - this will be replaced by indexed trackers + while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() ); + + Totals deltaTotals = m_totals.delta( prevTotals ); + if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) { + deltaTotals.assertions.failed++; + deltaTotals.testCases.passed--; + deltaTotals.testCases.failed++; + } + m_totals.testCases += deltaTotals.testCases; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + redirectedCout, + redirectedCerr, + aborting() ) ); + + m_activeTestCase = CATCH_NULL; + m_testCaseTracker = CATCH_NULL; + + return deltaTotals; + } + + Ptr config() const { + return m_config; + } + + private: // IResultCapture + + virtual void assertionEnded( AssertionResult const& result ) { + if( result.getResultType() == ResultWas::Ok ) { + m_totals.assertions.passed++; + } + else if( !result.isOk() ) { + m_totals.assertions.failed++; + } + + if( m_reporter->assertionEnded( AssertionStats( result, m_messages, m_totals ) ) ) + m_messages.clear(); + + // Reset working state + m_lastAssertionInfo = AssertionInfo( std::string(), m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition ); + m_lastResult = result; + } + + virtual bool sectionStarted ( + SectionInfo const& sectionInfo, + Counts& assertions + ) + { + ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) ); + if( !sectionTracker.isOpen() ) + return false; + m_activeSections.push_back( §ionTracker ); + + m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo; + + m_reporter->sectionStarting( sectionInfo ); + + assertions = m_totals.assertions; + + return true; + } + bool testForMissingAssertions( Counts& assertions ) { + if( assertions.total() != 0 ) + return false; + if( !m_config->warnAboutMissingAssertions() ) + return false; + if( m_trackerContext.currentTracker().hasChildren() ) + return false; + m_totals.assertions.failed++; + assertions.failed++; + return true; + } + + virtual void sectionEnded( SectionEndInfo const& endInfo ) { + Counts assertions = m_totals.assertions - endInfo.prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( !m_activeSections.empty() ) { + m_activeSections.back()->close(); + m_activeSections.pop_back(); + } + + m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) ); + m_messages.clear(); + } + + virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) { + if( m_unfinishedSections.empty() ) + m_activeSections.back()->fail(); + else + m_activeSections.back()->close(); + m_activeSections.pop_back(); + + m_unfinishedSections.push_back( endInfo ); + } + + virtual void pushScopedMessage( MessageInfo const& message ) { + m_messages.push_back( message ); + } + + virtual void popScopedMessage( MessageInfo const& message ) { + m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() ); + } + + virtual std::string getCurrentTestName() const { + return m_activeTestCase + ? m_activeTestCase->getTestCaseInfo().name + : std::string(); + } + + virtual const AssertionResult* getLastResult() const { + return &m_lastResult; + } + + virtual void handleFatalErrorCondition( std::string const& message ) { + ResultBuilder resultBuilder = makeUnexpectedResultBuilder(); + resultBuilder.setResultType( ResultWas::FatalErrorCondition ); + resultBuilder << message; + resultBuilder.captureExpression(); + + handleUnfinishedSections(); + + // Recreate section for test case (as we will lose the one that was in scope) + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + + Counts assertions; + assertions.failed = 1; + SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false ); + m_reporter->sectionEnded( testCaseSectionStats ); + + TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo(); + + Totals deltaTotals; + deltaTotals.testCases.failed = 1; + m_reporter->testCaseEnded( TestCaseStats( testInfo, + deltaTotals, + std::string(), + std::string(), + false ) ); + m_totals.testCases.failed++; + testGroupEnded( std::string(), m_totals, 1, 1 ); + m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) ); + } + + public: + // !TBD We need to do this another way! + bool aborting() const { + return m_totals.assertions.failed == static_cast( m_config->abortAfter() ); + } + + private: + + void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) { + TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo(); + SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description ); + m_reporter->sectionStarting( testCaseSection ); + Counts prevAssertions = m_totals.assertions; + double duration = 0; + try { + m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, std::string(), ResultDisposition::Normal ); + + seedRng( *m_config ); + + Timer timer; + timer.start(); + if( m_reporter->getPreferences().shouldRedirectStdOut ) { + StreamRedirect coutRedir( Catch::cout(), redirectedCout ); + StreamRedirect cerrRedir( Catch::cerr(), redirectedCerr ); + invokeActiveTestCase(); + } + else { + invokeActiveTestCase(); + } + duration = timer.getElapsedSeconds(); + } + catch( TestFailureException& ) { + // This just means the test was aborted due to failure + } + catch(...) { + makeUnexpectedResultBuilder().useActiveException(); + } + m_testCaseTracker->close(); + handleUnfinishedSections(); + m_messages.clear(); + + Counts assertions = m_totals.assertions - prevAssertions; + bool missingAssertions = testForMissingAssertions( assertions ); + + if( testCaseInfo.okToFail() ) { + std::swap( assertions.failedButOk, assertions.failed ); + m_totals.assertions.failed -= assertions.failedButOk; + m_totals.assertions.failedButOk += assertions.failedButOk; + } + + SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions ); + m_reporter->sectionEnded( testCaseSectionStats ); + } + + void invokeActiveTestCase() { + FatalConditionHandler fatalConditionHandler; // Handle signals + m_activeTestCase->invoke(); + fatalConditionHandler.reset(); + } + + private: + + ResultBuilder makeUnexpectedResultBuilder() const { + return ResultBuilder( m_lastAssertionInfo.macroName.c_str(), + m_lastAssertionInfo.lineInfo, + m_lastAssertionInfo.capturedExpression.c_str(), + m_lastAssertionInfo.resultDisposition ); + } + + void handleUnfinishedSections() { + // If sections ended prematurely due to an exception we stored their + // infos here so we can tear them down outside the unwind process. + for( std::vector::const_reverse_iterator it = m_unfinishedSections.rbegin(), + itEnd = m_unfinishedSections.rend(); + it != itEnd; + ++it ) + sectionEnded( *it ); + m_unfinishedSections.clear(); + } + + TestRunInfo m_runInfo; + IMutableContext& m_context; + TestCase const* m_activeTestCase; + ITracker* m_testCaseTracker; + ITracker* m_currentSectionTracker; + AssertionResult m_lastResult; + + Ptr m_config; + Totals m_totals; + Ptr m_reporter; + std::vector m_messages; + AssertionInfo m_lastAssertionInfo; + std::vector m_unfinishedSections; + std::vector m_activeSections; + TrackerContext m_trackerContext; + }; + + IResultCapture& getResultCapture() { + if( IResultCapture* capture = getCurrentContext().getResultCapture() ) + return *capture; + else + throw std::logic_error( "No result capture instance" ); + } + +} // end namespace Catch + +// #included from: internal/catch_version.h +#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED + +namespace Catch { + + // Versioning information + struct Version { + Version( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ); + + unsigned int const majorVersion; + unsigned int const minorVersion; + unsigned int const patchNumber; + + // buildNumber is only used if branchName is not null + std::string const branchName; + unsigned int const buildNumber; + + friend std::ostream& operator << ( std::ostream& os, Version const& version ); + + private: + void operator=( Version const& ); + }; + + extern Version libraryVersion; +} + +#include +#include +#include + +namespace Catch { + + Ptr createReporter( std::string const& reporterName, Ptr const& config ) { + Ptr reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() ); + if( !reporter ) { + std::ostringstream oss; + oss << "No reporter registered with name: '" << reporterName << "'"; + throw std::domain_error( oss.str() ); + } + return reporter; + } + + Ptr makeReporter( Ptr const& config ) { + std::vector reporters = config->getReporterNames(); + if( reporters.empty() ) + reporters.push_back( "console" ); + + Ptr reporter; + for( std::vector::const_iterator it = reporters.begin(), itEnd = reporters.end(); + it != itEnd; + ++it ) + reporter = addReporter( reporter, createReporter( *it, config ) ); + return reporter; + } + Ptr addListeners( Ptr const& config, Ptr reporters ) { + IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners(); + for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end(); + it != itEnd; + ++it ) + reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) ); + return reporters; + } + + Totals runTests( Ptr const& config ) { + + Ptr iconfig = config.get(); + + Ptr reporter = makeReporter( config ); + reporter = addListeners( iconfig, reporter ); + + RunContext context( iconfig, reporter ); + + Totals totals; + + context.testGroupStarting( config->name(), 1, 1 ); + + TestSpec testSpec = config->testSpec(); + if( !testSpec.hasFilters() ) + testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests + + std::vector const& allTestCases = getAllTestCasesSorted( *iconfig ); + for( std::vector::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end(); + it != itEnd; + ++it ) { + if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) ) + totals += context.runTest( *it ); + else + reporter->skipTest( *it ); + } + + context.testGroupEnded( iconfig->name(), totals, 1, 1 ); + return totals; + } + + void applyFilenamesAsTags( IConfig const& config ) { + std::vector const& tests = getAllTestCasesSorted( config ); + for(std::size_t i = 0; i < tests.size(); ++i ) { + TestCase& test = const_cast( tests[i] ); + std::set tags = test.tags; + + std::string filename = test.lineInfo.file; + std::string::size_type lastSlash = filename.find_last_of( "\\/" ); + if( lastSlash != std::string::npos ) + filename = filename.substr( lastSlash+1 ); + + std::string::size_type lastDot = filename.find_last_of( "." ); + if( lastDot != std::string::npos ) + filename = filename.substr( 0, lastDot ); + + tags.insert( "#" + filename ); + setTags( test, tags ); + } + } + + class Session : NonCopyable { + static bool alreadyInstantiated; + + public: + + struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; }; + + Session() + : m_cli( makeCommandLineParser() ) { + if( alreadyInstantiated ) { + std::string msg = "Only one instance of Catch::Session can ever be used"; + Catch::cerr() << msg << std::endl; + throw std::logic_error( msg ); + } + alreadyInstantiated = true; + } + ~Session() { + Catch::cleanUp(); + } + + void showHelp( std::string const& processName ) { + Catch::cout() << "\nCatch v" << libraryVersion << "\n"; + + m_cli.usage( Catch::cout(), processName ); + Catch::cout() << "For more detail usage please see the project docs\n" << std::endl; + } + + int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) { + try { + m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail ); + m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData ); + if( m_configData.showHelp ) + showHelp( m_configData.processName ); + m_config.reset(); + } + catch( std::exception& ex ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "\nError(s) in input:\n" + << Text( ex.what(), TextAttributes().setIndent(2) ) + << "\n\n"; + } + m_cli.usage( Catch::cout(), m_configData.processName ); + return (std::numeric_limits::max)(); + } + return 0; + } + + void useConfigData( ConfigData const& _configData ) { + m_configData = _configData; + m_config.reset(); + } + + int run( int argc, char const* const* const argv ) { + + int returnCode = applyCommandLine( argc, argv ); + if( returnCode == 0 ) + returnCode = run(); + return returnCode; + } + + int run() { + if( m_configData.showHelp ) + return 0; + + try + { + config(); // Force config to be constructed + + seedRng( *m_config ); + + if( m_configData.filenamesAsTags ) + applyFilenamesAsTags( *m_config ); + + // Handle list request + if( Option listed = list( config() ) ) + return static_cast( *listed ); + + return static_cast( runTests( m_config ).assertions.failed ); + } + catch( std::exception& ex ) { + Catch::cerr() << ex.what() << std::endl; + return (std::numeric_limits::max)(); + } + } + + Clara::CommandLine const& cli() const { + return m_cli; + } + std::vector const& unusedTokens() const { + return m_unusedTokens; + } + ConfigData& configData() { + return m_configData; + } + Config& config() { + if( !m_config ) + m_config = new Config( m_configData ); + return *m_config; + } + private: + Clara::CommandLine m_cli; + std::vector m_unusedTokens; + ConfigData m_configData; + Ptr m_config; + }; + + bool Session::alreadyInstantiated = false; + +} // end namespace Catch + +// #included from: catch_registry_hub.hpp +#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED + +// #included from: catch_test_case_registry_impl.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + struct RandomNumberGenerator { + typedef std::ptrdiff_t result_type; + + result_type operator()( result_type n ) const { return std::rand() % n; } + +#ifdef CATCH_CONFIG_CPP11_SHUFFLE + static constexpr result_type min() { return 0; } + static constexpr result_type max() { return 1000000; } + result_type operator()() const { return std::rand() % max(); } +#endif + template + static void shuffle( V& vector ) { + RandomNumberGenerator rng; +#ifdef CATCH_CONFIG_CPP11_SHUFFLE + std::shuffle( vector.begin(), vector.end(), rng ); +#else + std::random_shuffle( vector.begin(), vector.end(), rng ); +#endif + } + }; + + inline std::vector sortTests( IConfig const& config, std::vector const& unsortedTestCases ) { + + std::vector sorted = unsortedTestCases; + + switch( config.runOrder() ) { + case RunTests::InLexicographicalOrder: + std::sort( sorted.begin(), sorted.end() ); + break; + case RunTests::InRandomOrder: + { + seedRng( config ); + RandomNumberGenerator::shuffle( sorted ); + } + break; + case RunTests::InDeclarationOrder: + // already in declaration order + break; + } + return sorted; + } + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) { + return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() ); + } + + void enforceNoDuplicateTestCases( std::vector const& functions ) { + std::set seenFunctions; + for( std::vector::const_iterator it = functions.begin(), itEnd = functions.end(); + it != itEnd; + ++it ) { + std::pair::const_iterator, bool> prev = seenFunctions.insert( *it ); + if( !prev.second ) { + std::ostringstream ss; + + ss << Colour( Colour::Red ) + << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n" + << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n' + << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl; + + throw std::runtime_error(ss.str()); + } + } + } + + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ) { + std::vector filtered; + filtered.reserve( testCases.size() ); + for( std::vector::const_iterator it = testCases.begin(), itEnd = testCases.end(); + it != itEnd; + ++it ) + if( matchTest( *it, testSpec, config ) ) + filtered.push_back( *it ); + return filtered; + } + std::vector const& getAllTestCasesSorted( IConfig const& config ) { + return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config ); + } + + class TestRegistry : public ITestCaseRegistry { + public: + TestRegistry() + : m_currentSortOrder( RunTests::InDeclarationOrder ), + m_unnamedCount( 0 ) + {} + virtual ~TestRegistry(); + + virtual void registerTest( TestCase const& testCase ) { + std::string name = testCase.getTestCaseInfo().name; + if( name.empty() ) { + std::ostringstream oss; + oss << "Anonymous test case " << ++m_unnamedCount; + return registerTest( testCase.withName( oss.str() ) ); + } + m_functions.push_back( testCase ); + } + + virtual std::vector const& getAllTests() const { + return m_functions; + } + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const { + if( m_sortedFunctions.empty() ) + enforceNoDuplicateTestCases( m_functions ); + + if( m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) { + m_sortedFunctions = sortTests( config, m_functions ); + m_currentSortOrder = config.runOrder(); + } + return m_sortedFunctions; + } + + private: + std::vector m_functions; + mutable RunTests::InWhatOrder m_currentSortOrder; + mutable std::vector m_sortedFunctions; + size_t m_unnamedCount; + std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised + }; + + /////////////////////////////////////////////////////////////////////////// + + class FreeFunctionTestCase : public SharedImpl { + public: + + FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {} + + virtual void invoke() const { + m_fun(); + } + + private: + virtual ~FreeFunctionTestCase(); + + TestFunction m_fun; + }; + + inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) { + std::string className = classOrQualifiedMethodName; + if( startsWith( className, '&' ) ) + { + std::size_t lastColons = className.rfind( "::" ); + std::size_t penultimateColons = className.rfind( "::", lastColons-1 ); + if( penultimateColons == std::string::npos ) + penultimateColons = 1; + className = className.substr( penultimateColons, lastColons-penultimateColons ); + } + return className; + } + + void registerTestCase + ( ITestCase* testCase, + char const* classOrQualifiedMethodName, + NameAndDesc const& nameAndDesc, + SourceLineInfo const& lineInfo ) { + + getMutableRegistryHub().registerTest + ( makeTestCase + ( testCase, + extractClassName( classOrQualifiedMethodName ), + nameAndDesc.name, + nameAndDesc.description, + lineInfo ) ); + } + void registerTestCaseFunction + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo ); + } + + /////////////////////////////////////////////////////////////////////////// + + AutoReg::AutoReg + ( TestFunction function, + SourceLineInfo const& lineInfo, + NameAndDesc const& nameAndDesc ) { + registerTestCaseFunction( function, lineInfo, nameAndDesc ); + } + + AutoReg::~AutoReg() {} + +} // end namespace Catch + +// #included from: catch_reporter_registry.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED + +#include + +namespace Catch { + + class ReporterRegistry : public IReporterRegistry { + + public: + + virtual ~ReporterRegistry() CATCH_OVERRIDE {} + + virtual IStreamingReporter* create( std::string const& name, Ptr const& config ) const CATCH_OVERRIDE { + FactoryMap::const_iterator it = m_factories.find( name ); + if( it == m_factories.end() ) + return CATCH_NULL; + return it->second->create( ReporterConfig( config ) ); + } + + void registerReporter( std::string const& name, Ptr const& factory ) { + m_factories.insert( std::make_pair( name, factory ) ); + } + void registerListener( Ptr const& factory ) { + m_listeners.push_back( factory ); + } + + virtual FactoryMap const& getFactories() const CATCH_OVERRIDE { + return m_factories; + } + virtual Listeners const& getListeners() const CATCH_OVERRIDE { + return m_listeners; + } + + private: + FactoryMap m_factories; + Listeners m_listeners; + }; +} + +// #included from: catch_exception_translator_registry.hpp +#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED + +#ifdef __OBJC__ +#import "Foundation/Foundation.h" +#endif + +namespace Catch { + + class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry { + public: + ~ExceptionTranslatorRegistry() { + deleteAll( m_translators ); + } + + virtual void registerTranslator( const IExceptionTranslator* translator ) { + m_translators.push_back( translator ); + } + + virtual std::string translateActiveException() const { + try { +#ifdef __OBJC__ + // In Objective-C try objective-c exceptions first + @try { + return tryTranslators(); + } + @catch (NSException *exception) { + return Catch::toString( [exception description] ); + } +#else + return tryTranslators(); +#endif + } + catch( TestFailureException& ) { + throw; + } + catch( std::exception& ex ) { + return ex.what(); + } + catch( std::string& msg ) { + return msg; + } + catch( const char* msg ) { + return msg; + } + catch(...) { + return "Unknown exception"; + } + } + + std::string tryTranslators() const { + if( m_translators.empty() ) + throw; + else + return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() ); + } + + private: + std::vector m_translators; + }; +} + +namespace Catch { + + namespace { + + class RegistryHub : public IRegistryHub, public IMutableRegistryHub { + + RegistryHub( RegistryHub const& ); + void operator=( RegistryHub const& ); + + public: // IRegistryHub + RegistryHub() { + } + virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE { + return m_reporterRegistry; + } + virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE { + return m_testCaseRegistry; + } + virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE { + return m_exceptionTranslatorRegistry; + } + + public: // IMutableRegistryHub + virtual void registerReporter( std::string const& name, Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerReporter( name, factory ); + } + virtual void registerListener( Ptr const& factory ) CATCH_OVERRIDE { + m_reporterRegistry.registerListener( factory ); + } + virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE { + m_testCaseRegistry.registerTest( testInfo ); + } + virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE { + m_exceptionTranslatorRegistry.registerTranslator( translator ); + } + + private: + TestRegistry m_testCaseRegistry; + ReporterRegistry m_reporterRegistry; + ExceptionTranslatorRegistry m_exceptionTranslatorRegistry; + }; + + // Single, global, instance + inline RegistryHub*& getTheRegistryHub() { + static RegistryHub* theRegistryHub = CATCH_NULL; + if( !theRegistryHub ) + theRegistryHub = new RegistryHub(); + return theRegistryHub; + } + } + + IRegistryHub& getRegistryHub() { + return *getTheRegistryHub(); + } + IMutableRegistryHub& getMutableRegistryHub() { + return *getTheRegistryHub(); + } + void cleanUp() { + delete getTheRegistryHub(); + getTheRegistryHub() = CATCH_NULL; + cleanUpContext(); + } + std::string translateActiveException() { + return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException(); + } + +} // end namespace Catch + +// #included from: catch_notimplemented_exception.hpp +#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED + +#include + +namespace Catch { + + NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo ) + : m_lineInfo( lineInfo ) { + std::ostringstream oss; + oss << lineInfo << ": function "; + oss << "not implemented"; + m_what = oss.str(); + } + + const char* NotImplementedException::what() const CATCH_NOEXCEPT { + return m_what.c_str(); + } + +} // end namespace Catch + +// #included from: catch_context_impl.hpp +#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED + +// #included from: catch_stream.hpp +#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + template + class StreamBufImpl : public StreamBufBase { + char data[bufferSize]; + WriterF m_writer; + + public: + StreamBufImpl() { + setp( data, data + sizeof(data) ); + } + + ~StreamBufImpl() CATCH_NOEXCEPT { + sync(); + } + + private: + int overflow( int c ) { + sync(); + + if( c != EOF ) { + if( pbase() == epptr() ) + m_writer( std::string( 1, static_cast( c ) ) ); + else + sputc( static_cast( c ) ); + } + return 0; + } + + int sync() { + if( pbase() != pptr() ) { + m_writer( std::string( pbase(), static_cast( pptr() - pbase() ) ) ); + setp( pbase(), epptr() ); + } + return 0; + } + }; + + /////////////////////////////////////////////////////////////////////////// + + FileStream::FileStream( std::string const& filename ) { + m_ofs.open( filename.c_str() ); + if( m_ofs.fail() ) { + std::ostringstream oss; + oss << "Unable to open file: '" << filename << '\''; + throw std::domain_error( oss.str() ); + } + } + + std::ostream& FileStream::stream() const { + return m_ofs; + } + + struct OutputDebugWriter { + + void operator()( std::string const&str ) { + writeToDebugConsole( str ); + } + }; + + DebugOutStream::DebugOutStream() + : m_streamBuf( new StreamBufImpl() ), + m_os( m_streamBuf.get() ) + {} + + std::ostream& DebugOutStream::stream() const { + return m_os; + } + + // Store the streambuf from cout up-front because + // cout may get redirected when running tests + CoutStream::CoutStream() + : m_os( Catch::cout().rdbuf() ) + {} + + std::ostream& CoutStream::stream() const { + return m_os; + } + +#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions + std::ostream& cout() { + return std::cout; + } + std::ostream& cerr() { + return std::cerr; + } +#endif +} + +namespace Catch { + + class Context : public IMutableContext { + + Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {} + Context( Context const& ); + void operator=( Context const& ); + + public: + virtual ~Context() { + deleteAllValues( m_generatorsByTestName ); + } + + public: // IContext + virtual IResultCapture* getResultCapture() { + return m_resultCapture; + } + virtual IRunner* getRunner() { + return m_runner; + } + virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) { + return getGeneratorsForCurrentTest() + .getGeneratorInfo( fileInfo, totalSize ) + .getCurrentIndex(); + } + virtual bool advanceGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + return generators && generators->moveNext(); + } + + virtual Ptr getConfig() const { + return m_config; + } + + public: // IMutableContext + virtual void setResultCapture( IResultCapture* resultCapture ) { + m_resultCapture = resultCapture; + } + virtual void setRunner( IRunner* runner ) { + m_runner = runner; + } + virtual void setConfig( Ptr const& config ) { + m_config = config; + } + + friend IMutableContext& getCurrentMutableContext(); + + private: + IGeneratorsForTest* findGeneratorsForCurrentTest() { + std::string testName = getResultCapture()->getCurrentTestName(); + + std::map::const_iterator it = + m_generatorsByTestName.find( testName ); + return it != m_generatorsByTestName.end() + ? it->second + : CATCH_NULL; + } + + IGeneratorsForTest& getGeneratorsForCurrentTest() { + IGeneratorsForTest* generators = findGeneratorsForCurrentTest(); + if( !generators ) { + std::string testName = getResultCapture()->getCurrentTestName(); + generators = createGeneratorsForTest(); + m_generatorsByTestName.insert( std::make_pair( testName, generators ) ); + } + return *generators; + } + + private: + Ptr m_config; + IRunner* m_runner; + IResultCapture* m_resultCapture; + std::map m_generatorsByTestName; + }; + + namespace { + Context* currentContext = CATCH_NULL; + } + IMutableContext& getCurrentMutableContext() { + if( !currentContext ) + currentContext = new Context(); + return *currentContext; + } + IContext& getCurrentContext() { + return getCurrentMutableContext(); + } + + void cleanUpContext() { + delete currentContext; + currentContext = CATCH_NULL; + } +} + +// #included from: catch_console_colour_impl.hpp +#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED + +namespace Catch { + namespace { + + struct IColourImpl { + virtual ~IColourImpl() {} + virtual void use( Colour::Code _colourCode ) = 0; + }; + + struct NoColourImpl : IColourImpl { + void use( Colour::Code ) {} + + static IColourImpl* instance() { + static NoColourImpl s_instance; + return &s_instance; + } + }; + + } // anon namespace +} // namespace Catch + +#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI ) +# ifdef CATCH_PLATFORM_WINDOWS +# define CATCH_CONFIG_COLOUR_WINDOWS +# else +# define CATCH_CONFIG_COLOUR_ANSI +# endif +#endif + +#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) ///////////////////////////////////////// + +namespace Catch { +namespace { + + class Win32ColourImpl : public IColourImpl { + public: + Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) ) + { + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo ); + originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY ); + originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY ); + } + + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: return setTextAttribute( originalForegroundAttributes ); + case Colour::White: return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + case Colour::Red: return setTextAttribute( FOREGROUND_RED ); + case Colour::Green: return setTextAttribute( FOREGROUND_GREEN ); + case Colour::Blue: return setTextAttribute( FOREGROUND_BLUE ); + case Colour::Cyan: return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN ); + case Colour::Yellow: return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN ); + case Colour::Grey: return setTextAttribute( 0 ); + + case Colour::LightGrey: return setTextAttribute( FOREGROUND_INTENSITY ); + case Colour::BrightRed: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED ); + case Colour::BrightGreen: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN ); + case Colour::BrightWhite: return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + + private: + void setTextAttribute( WORD _textAttribute ) { + SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes ); + } + HANDLE stdoutHandle; + WORD originalForegroundAttributes; + WORD originalBackgroundAttributes; + }; + + IColourImpl* platformColourInstance() { + static Win32ColourImpl s_instance; + + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = !isDebuggerActive() + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? &s_instance + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#elif defined( CATCH_CONFIG_COLOUR_ANSI ) ////////////////////////////////////// + +#include + +namespace Catch { +namespace { + + // use POSIX/ ANSI console terminal codes + // Thanks to Adam Strzelecki for original contribution + // (http://github.com/nanoant) + // https://github.com/philsquared/Catch/pull/131 + class PosixColourImpl : public IColourImpl { + public: + virtual void use( Colour::Code _colourCode ) { + switch( _colourCode ) { + case Colour::None: + case Colour::White: return setColour( "[0m" ); + case Colour::Red: return setColour( "[0;31m" ); + case Colour::Green: return setColour( "[0;32m" ); + case Colour::Blue: return setColour( "[0;34m" ); + case Colour::Cyan: return setColour( "[0;36m" ); + case Colour::Yellow: return setColour( "[0;33m" ); + case Colour::Grey: return setColour( "[1;30m" ); + + case Colour::LightGrey: return setColour( "[0;37m" ); + case Colour::BrightRed: return setColour( "[1;31m" ); + case Colour::BrightGreen: return setColour( "[1;32m" ); + case Colour::BrightWhite: return setColour( "[1;37m" ); + + case Colour::Bright: throw std::logic_error( "not a colour" ); + } + } + static IColourImpl* instance() { + static PosixColourImpl s_instance; + return &s_instance; + } + + private: + void setColour( const char* _escapeCode ) { + Catch::cout() << '\033' << _escapeCode; + } + }; + + IColourImpl* platformColourInstance() { + Ptr config = getCurrentContext().getConfig(); + UseColour::YesOrNo colourMode = config + ? config->useColour() + : UseColour::Auto; + if( colourMode == UseColour::Auto ) + colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) ) + ? UseColour::Yes + : UseColour::No; + return colourMode == UseColour::Yes + ? PosixColourImpl::instance() + : NoColourImpl::instance(); + } + +} // end anon namespace +} // end namespace Catch + +#else // not Windows or ANSI /////////////////////////////////////////////// + +namespace Catch { + + static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); } + +} // end namespace Catch + +#endif // Windows/ ANSI/ None + +namespace Catch { + + Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); } + Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast( _other ).m_moved = true; } + Colour::~Colour(){ if( !m_moved ) use( None ); } + + void Colour::use( Code _colourCode ) { + static IColourImpl* impl = platformColourInstance(); + impl->use( _colourCode ); + } + +} // end namespace Catch + +// #included from: catch_generators_impl.hpp +#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED + +#include +#include +#include + +namespace Catch { + + struct GeneratorInfo : IGeneratorInfo { + + GeneratorInfo( std::size_t size ) + : m_size( size ), + m_currentIndex( 0 ) + {} + + bool moveNext() { + if( ++m_currentIndex == m_size ) { + m_currentIndex = 0; + return false; + } + return true; + } + + std::size_t getCurrentIndex() const { + return m_currentIndex; + } + + std::size_t m_size; + std::size_t m_currentIndex; + }; + + /////////////////////////////////////////////////////////////////////////// + + class GeneratorsForTest : public IGeneratorsForTest { + + public: + ~GeneratorsForTest() { + deleteAll( m_generatorsInOrder ); + } + + IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) { + std::map::const_iterator it = m_generatorsByName.find( fileInfo ); + if( it == m_generatorsByName.end() ) { + IGeneratorInfo* info = new GeneratorInfo( size ); + m_generatorsByName.insert( std::make_pair( fileInfo, info ) ); + m_generatorsInOrder.push_back( info ); + return *info; + } + return *it->second; + } + + bool moveNext() { + std::vector::const_iterator it = m_generatorsInOrder.begin(); + std::vector::const_iterator itEnd = m_generatorsInOrder.end(); + for(; it != itEnd; ++it ) { + if( (*it)->moveNext() ) + return true; + } + return false; + } + + private: + std::map m_generatorsByName; + std::vector m_generatorsInOrder; + }; + + IGeneratorsForTest* createGeneratorsForTest() + { + return new GeneratorsForTest(); + } + +} // end namespace Catch + +// #included from: catch_assertionresult.hpp +#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED + +namespace Catch { + + AssertionInfo::AssertionInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + std::string const& _capturedExpression, + ResultDisposition::Flags _resultDisposition ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + capturedExpression( _capturedExpression ), + resultDisposition( _resultDisposition ) + {} + + AssertionResult::AssertionResult() {} + + AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data ) + : m_info( info ), + m_resultData( data ) + {} + + AssertionResult::~AssertionResult() {} + + // Result was a success + bool AssertionResult::succeeded() const { + return Catch::isOk( m_resultData.resultType ); + } + + // Result was a success, or failure is suppressed + bool AssertionResult::isOk() const { + return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition ); + } + + ResultWas::OfType AssertionResult::getResultType() const { + return m_resultData.resultType; + } + + bool AssertionResult::hasExpression() const { + return !m_info.capturedExpression.empty(); + } + + bool AssertionResult::hasMessage() const { + return !m_resultData.message.empty(); + } + + std::string AssertionResult::getExpression() const { + if( isFalseTest( m_info.resultDisposition ) ) + return '!' + m_info.capturedExpression; + else + return m_info.capturedExpression; + } + std::string AssertionResult::getExpressionInMacro() const { + if( m_info.macroName.empty() ) + return m_info.capturedExpression; + else + return m_info.macroName + "( " + m_info.capturedExpression + " )"; + } + + bool AssertionResult::hasExpandedExpression() const { + return hasExpression() && getExpandedExpression() != getExpression(); + } + + std::string AssertionResult::getExpandedExpression() const { + return m_resultData.reconstructExpression(); + } + + std::string AssertionResult::getMessage() const { + return m_resultData.message; + } + SourceLineInfo AssertionResult::getSourceInfo() const { + return m_info.lineInfo; + } + + std::string AssertionResult::getTestMacroName() const { + return m_info.macroName; + } + + void AssertionResult::discardDecomposedExpression() const { + m_resultData.decomposedExpression = CATCH_NULL; + } + + void AssertionResult::expandDecomposedExpression() const { + m_resultData.reconstructExpression(); + } + +} // end namespace Catch + +// #included from: catch_test_case_info.hpp +#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED + +#include + +namespace Catch { + + inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) { + if( startsWith( tag, '.' ) || + tag == "hide" || + tag == "!hide" ) + return TestCaseInfo::IsHidden; + else if( tag == "!throws" ) + return TestCaseInfo::Throws; + else if( tag == "!shouldfail" ) + return TestCaseInfo::ShouldFail; + else if( tag == "!mayfail" ) + return TestCaseInfo::MayFail; + else if( tag == "!nonportable" ) + return TestCaseInfo::NonPortable; + else + return TestCaseInfo::None; + } + inline bool isReservedTag( std::string const& tag ) { + return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] ); + } + inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) { + if( isReservedTag( tag ) ) { + { + Colour colourGuard( Colour::Red ); + Catch::cerr() + << "Tag name [" << tag << "] not allowed.\n" + << "Tag names starting with non alpha-numeric characters are reserved\n"; + } + { + Colour colourGuard( Colour::FileName ); + Catch::cerr() << _lineInfo << std::endl; + } + exit(1); + } + } + + TestCase makeTestCase( ITestCase* _testCase, + std::string const& _className, + std::string const& _name, + std::string const& _descOrTags, + SourceLineInfo const& _lineInfo ) + { + bool isHidden( startsWith( _name, "./" ) ); // Legacy support + + // Parse out tags + std::set tags; + std::string desc, tag; + bool inTag = false; + for( std::size_t i = 0; i < _descOrTags.size(); ++i ) { + char c = _descOrTags[i]; + if( !inTag ) { + if( c == '[' ) + inTag = true; + else + desc += c; + } + else { + if( c == ']' ) { + TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag ); + if( prop == TestCaseInfo::IsHidden ) + isHidden = true; + else if( prop == TestCaseInfo::None ) + enforceNotReservedTag( tag, _lineInfo ); + + tags.insert( tag ); + tag.clear(); + inTag = false; + } + else + tag += c; + } + } + if( isHidden ) { + tags.insert( "hide" ); + tags.insert( "." ); + } + + TestCaseInfo info( _name, _className, desc, tags, _lineInfo ); + return TestCase( _testCase, info ); + } + + void setTags( TestCaseInfo& testCaseInfo, std::set const& tags ) + { + testCaseInfo.tags = tags; + testCaseInfo.lcaseTags.clear(); + + std::ostringstream oss; + for( std::set::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) { + oss << '[' << *it << ']'; + std::string lcaseTag = toLower( *it ); + testCaseInfo.properties = static_cast( testCaseInfo.properties | parseSpecialTag( lcaseTag ) ); + testCaseInfo.lcaseTags.insert( lcaseTag ); + } + testCaseInfo.tagsAsString = oss.str(); + } + + TestCaseInfo::TestCaseInfo( std::string const& _name, + std::string const& _className, + std::string const& _description, + std::set const& _tags, + SourceLineInfo const& _lineInfo ) + : name( _name ), + className( _className ), + description( _description ), + lineInfo( _lineInfo ), + properties( None ) + { + setTags( *this, _tags ); + } + + TestCaseInfo::TestCaseInfo( TestCaseInfo const& other ) + : name( other.name ), + className( other.className ), + description( other.description ), + tags( other.tags ), + lcaseTags( other.lcaseTags ), + tagsAsString( other.tagsAsString ), + lineInfo( other.lineInfo ), + properties( other.properties ) + {} + + bool TestCaseInfo::isHidden() const { + return ( properties & IsHidden ) != 0; + } + bool TestCaseInfo::throws() const { + return ( properties & Throws ) != 0; + } + bool TestCaseInfo::okToFail() const { + return ( properties & (ShouldFail | MayFail ) ) != 0; + } + bool TestCaseInfo::expectedToFail() const { + return ( properties & (ShouldFail ) ) != 0; + } + + TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {} + + TestCase::TestCase( TestCase const& other ) + : TestCaseInfo( other ), + test( other.test ) + {} + + TestCase TestCase::withName( std::string const& _newName ) const { + TestCase other( *this ); + other.name = _newName; + return other; + } + + void TestCase::swap( TestCase& other ) { + test.swap( other.test ); + name.swap( other.name ); + className.swap( other.className ); + description.swap( other.description ); + tags.swap( other.tags ); + lcaseTags.swap( other.lcaseTags ); + tagsAsString.swap( other.tagsAsString ); + std::swap( TestCaseInfo::properties, static_cast( other ).properties ); + std::swap( lineInfo, other.lineInfo ); + } + + void TestCase::invoke() const { + test->invoke(); + } + + bool TestCase::operator == ( TestCase const& other ) const { + return test.get() == other.test.get() && + name == other.name && + className == other.className; + } + + bool TestCase::operator < ( TestCase const& other ) const { + return name < other.name; + } + TestCase& TestCase::operator = ( TestCase const& other ) { + TestCase temp( other ); + swap( temp ); + return *this; + } + + TestCaseInfo const& TestCase::getTestCaseInfo() const + { + return *this; + } + +} // end namespace Catch + +// #included from: catch_version.hpp +#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED + +namespace Catch { + + Version::Version + ( unsigned int _majorVersion, + unsigned int _minorVersion, + unsigned int _patchNumber, + std::string const& _branchName, + unsigned int _buildNumber ) + : majorVersion( _majorVersion ), + minorVersion( _minorVersion ), + patchNumber( _patchNumber ), + branchName( _branchName ), + buildNumber( _buildNumber ) + {} + + std::ostream& operator << ( std::ostream& os, Version const& version ) { + os << version.majorVersion << '.' + << version.minorVersion << '.' + << version.patchNumber; + + if( !version.branchName.empty() ) { + os << '-' << version.branchName + << '.' << version.buildNumber; + } + return os; + } + + Version libraryVersion( 1, 7, 2, "", 0 ); + +} + +// #included from: catch_message.hpp +#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED + +namespace Catch { + + MessageInfo::MessageInfo( std::string const& _macroName, + SourceLineInfo const& _lineInfo, + ResultWas::OfType _type ) + : macroName( _macroName ), + lineInfo( _lineInfo ), + type( _type ), + sequence( ++globalCount ) + {} + + // This may need protecting if threading support is added + unsigned int MessageInfo::globalCount = 0; + + //////////////////////////////////////////////////////////////////////////// + + ScopedMessage::ScopedMessage( MessageBuilder const& builder ) + : m_info( builder.m_info ) + { + m_info.message = builder.m_stream.str(); + getResultCapture().pushScopedMessage( m_info ); + } + ScopedMessage::ScopedMessage( ScopedMessage const& other ) + : m_info( other.m_info ) + {} + + ScopedMessage::~ScopedMessage() { + getResultCapture().popScopedMessage( m_info ); + } + +} // end namespace Catch + +// #included from: catch_legacy_reporter_adapter.hpp +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED + +// #included from: catch_legacy_reporter_adapter.h +#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED + +namespace Catch +{ + // Deprecated + struct IReporter : IShared { + virtual ~IReporter(); + + virtual bool shouldRedirectStdout() const = 0; + + virtual void StartTesting() = 0; + virtual void EndTesting( Totals const& totals ) = 0; + virtual void StartGroup( std::string const& groupName ) = 0; + virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0; + virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0; + virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0; + virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0; + virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0; + virtual void NoAssertionsInSection( std::string const& sectionName ) = 0; + virtual void NoAssertionsInTestCase( std::string const& testName ) = 0; + virtual void Aborted() = 0; + virtual void Result( AssertionResult const& result ) = 0; + }; + + class LegacyReporterAdapter : public SharedImpl + { + public: + LegacyReporterAdapter( Ptr const& legacyReporter ); + virtual ~LegacyReporterAdapter(); + + virtual ReporterPreferences getPreferences() const; + virtual void noMatchingTestCases( std::string const& ); + virtual void testRunStarting( TestRunInfo const& ); + virtual void testGroupStarting( GroupInfo const& groupInfo ); + virtual void testCaseStarting( TestCaseInfo const& testInfo ); + virtual void sectionStarting( SectionInfo const& sectionInfo ); + virtual void assertionStarting( AssertionInfo const& ); + virtual bool assertionEnded( AssertionStats const& assertionStats ); + virtual void sectionEnded( SectionStats const& sectionStats ); + virtual void testCaseEnded( TestCaseStats const& testCaseStats ); + virtual void testGroupEnded( TestGroupStats const& testGroupStats ); + virtual void testRunEnded( TestRunStats const& testRunStats ); + virtual void skipTest( TestCaseInfo const& ); + + private: + Ptr m_legacyReporter; + }; +} + +namespace Catch +{ + LegacyReporterAdapter::LegacyReporterAdapter( Ptr const& legacyReporter ) + : m_legacyReporter( legacyReporter ) + {} + LegacyReporterAdapter::~LegacyReporterAdapter() {} + + ReporterPreferences LegacyReporterAdapter::getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout(); + return prefs; + } + + void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {} + void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) { + m_legacyReporter->StartTesting(); + } + void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) { + m_legacyReporter->StartGroup( groupInfo.name ); + } + void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) { + m_legacyReporter->StartTestCase( testInfo ); + } + void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) { + m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description ); + } + void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) { + // Not on legacy interface + } + + bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) { + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal ); + rb << it->message; + rb.setResultType( ResultWas::Info ); + AssertionResult result = rb.build(); + m_legacyReporter->Result( result ); + } + } + } + m_legacyReporter->Result( assertionStats.assertionResult ); + return true; + } + void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) { + if( sectionStats.missingAssertions ) + m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name ); + m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions ); + } + void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) { + m_legacyReporter->EndTestCase + ( testCaseStats.testInfo, + testCaseStats.totals, + testCaseStats.stdOut, + testCaseStats.stdErr ); + } + void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) { + if( testGroupStats.aborting ) + m_legacyReporter->Aborted(); + m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals ); + } + void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) { + m_legacyReporter->EndTesting( testRunStats.totals ); + } + void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) { + } +} + +// #included from: catch_timer.hpp + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++11-long-long" +#endif + +#ifdef CATCH_PLATFORM_WINDOWS +#else +#include +#endif + +namespace Catch { + + namespace { +#ifdef CATCH_PLATFORM_WINDOWS + uint64_t getCurrentTicks() { + static uint64_t hz=0, hzo=0; + if (!hz) { + QueryPerformanceFrequency( reinterpret_cast( &hz ) ); + QueryPerformanceCounter( reinterpret_cast( &hzo ) ); + } + uint64_t t; + QueryPerformanceCounter( reinterpret_cast( &t ) ); + return ((t-hzo)*1000000)/hz; + } +#else + uint64_t getCurrentTicks() { + timeval t; + gettimeofday(&t,CATCH_NULL); + return static_cast( t.tv_sec ) * 1000000ull + static_cast( t.tv_usec ); + } +#endif + } + + void Timer::start() { + m_ticks = getCurrentTicks(); + } + unsigned int Timer::getElapsedMicroseconds() const { + return static_cast(getCurrentTicks() - m_ticks); + } + unsigned int Timer::getElapsedMilliseconds() const { + return static_cast(getElapsedMicroseconds()/1000); + } + double Timer::getElapsedSeconds() const { + return getElapsedMicroseconds()/1000000.0; + } + +} // namespace Catch + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +// #included from: catch_common.hpp +#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED + +#include +#include + +namespace Catch { + + bool startsWith( std::string const& s, std::string const& prefix ) { + return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin()); + } + bool startsWith( std::string const& s, char prefix ) { + return !s.empty() && s[0] == prefix; + } + bool endsWith( std::string const& s, std::string const& suffix ) { + return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin()); + } + bool endsWith( std::string const& s, char suffix ) { + return !s.empty() && s[s.size()-1] == suffix; + } + bool contains( std::string const& s, std::string const& infix ) { + return s.find( infix ) != std::string::npos; + } + char toLowerCh(char c) { + return static_cast( std::tolower( c ) ); + } + void toLowerInPlace( std::string& s ) { + std::transform( s.begin(), s.end(), s.begin(), toLowerCh ); + } + std::string toLower( std::string const& s ) { + std::string lc = s; + toLowerInPlace( lc ); + return lc; + } + std::string trim( std::string const& str ) { + static char const* whitespaceChars = "\n\r\t "; + std::string::size_type start = str.find_first_not_of( whitespaceChars ); + std::string::size_type end = str.find_last_not_of( whitespaceChars ); + + return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string(); + } + + bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) { + bool replaced = false; + std::size_t i = str.find( replaceThis ); + while( i != std::string::npos ) { + replaced = true; + str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() ); + if( i < str.size()-withThis.size() ) + i = str.find( replaceThis, i+withThis.size() ); + else + i = std::string::npos; + } + return replaced; + } + + pluralise::pluralise( std::size_t count, std::string const& label ) + : m_count( count ), + m_label( label ) + {} + + std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) { + os << pluraliser.m_count << ' ' << pluraliser.m_label; + if( pluraliser.m_count != 1 ) + os << 's'; + return os; + } + + SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){} + SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line ) + : file( _file ), + line( _line ) + {} + bool SourceLineInfo::empty() const { + return file[0] == '\0'; + } + bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const { + return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0); + } + bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const { + return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0)); + } + + void seedRng( IConfig const& config ) { + if( config.rngSeed() != 0 ) + std::srand( config.rngSeed() ); + } + unsigned int rngSeed() { + return getCurrentContext().getConfig()->rngSeed(); + } + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) { +#ifndef __GNUG__ + os << info.file << '(' << info.line << ')'; +#else + os << info.file << ':' << info.line; +#endif + return os; + } + + void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) { + std::ostringstream oss; + oss << locationInfo << ": Internal Catch error: '" << message << '\''; + if( alwaysTrue() ) + throw std::logic_error( oss.str() ); + } +} + +// #included from: catch_section.hpp +#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED + +namespace Catch { + + SectionInfo::SectionInfo + ( SourceLineInfo const& _lineInfo, + std::string const& _name, + std::string const& _description ) + : name( _name ), + description( _description ), + lineInfo( _lineInfo ) + {} + + Section::Section( SectionInfo const& info ) + : m_info( info ), + m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) ) + { + m_timer.start(); + } + + Section::~Section() { + if( m_sectionIncluded ) { + SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() ); + if( std::uncaught_exception() ) + getResultCapture().sectionEndedEarly( endInfo ); + else + getResultCapture().sectionEnded( endInfo ); + } + } + + // This indicates whether the section should be executed or not + Section::operator bool() const { + return m_sectionIncluded; + } + +} // end namespace Catch + +// #included from: catch_debugger.hpp +#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED + +#ifdef CATCH_PLATFORM_MAC + + #include + #include + #include + #include + #include + + namespace Catch{ + + // The following function is taken directly from the following technical note: + // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html + + // Returns true if the current process is being debugged (either + // running under the debugger or has a debugger attached post facto). + bool isDebuggerActive(){ + + int mib[4]; + struct kinfo_proc info; + size_t size; + + // Initialize the flags so that, if sysctl fails for some bizarre + // reason, we get a predictable result. + + info.kp_proc.p_flag = 0; + + // Initialize mib, which tells sysctl the info we want, in this case + // we're looking for information about a specific process ID. + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + // Call sysctl. + + size = sizeof(info); + if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) { + Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl; + return false; + } + + // We're being debugged if the P_TRACED flag is set. + + return ( (info.kp_proc.p_flag & P_TRACED) != 0 ); + } + } // namespace Catch + +#elif defined(CATCH_PLATFORM_LINUX) + #include + #include + + namespace Catch{ + // The standard POSIX way of detecting a debugger is to attempt to + // ptrace() the process, but this needs to be done from a child and not + // this process itself to still allow attaching to this process later + // if wanted, so is rather heavy. Under Linux we have the PID of the + // "debugger" (which doesn't need to be gdb, of course, it could also + // be strace, for example) in /proc/$PID/status, so just get it from + // there instead. + bool isDebuggerActive(){ + std::ifstream in("/proc/self/status"); + for( std::string line; std::getline(in, line); ) { + static const int PREFIX_LEN = 11; + if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) { + // We're traced if the PID is not 0 and no other PID starts + // with 0 digit, so it's enough to check for just a single + // character. + return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0'; + } + } + + return false; + } + } // namespace Catch +#elif defined(_MSC_VER) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#elif defined(__MINGW32__) + extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); + namespace Catch { + bool isDebuggerActive() { + return IsDebuggerPresent() != 0; + } + } +#else + namespace Catch { + inline bool isDebuggerActive() { return false; } + } +#endif // Platform + +#ifdef CATCH_PLATFORM_WINDOWS + + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + ::OutputDebugStringA( text.c_str() ); + } + } +#else + namespace Catch { + void writeToDebugConsole( std::string const& text ) { + // !TBD: Need a version for Mac/ XCode and other IDEs + Catch::cout() << text; + } + } +#endif // Platform + +// #included from: catch_tostring.hpp +#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED + +namespace Catch { + +namespace Detail { + + const std::string unprintableString = "{?}"; + + namespace { + const int hexThreshold = 255; + + struct Endianness { + enum Arch { Big, Little }; + + static Arch which() { + union _{ + int asInt; + char asChar[sizeof (int)]; + } u; + + u.asInt = 1; + return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little; + } + }; + } + + std::string rawMemoryToString( const void *object, std::size_t size ) + { + // Reverse order for little endian architectures + int i = 0, end = static_cast( size ), inc = 1; + if( Endianness::which() == Endianness::Little ) { + i = end-1; + end = inc = -1; + } + + unsigned char const *bytes = static_cast(object); + std::ostringstream os; + os << "0x" << std::setfill('0') << std::hex; + for( ; i != end; i += inc ) + os << std::setw(2) << static_cast(bytes[i]); + return os.str(); + } +} + +std::string toString( std::string const& value ) { + std::string s = value; + if( getCurrentContext().getConfig()->showInvisibles() ) { + for(size_t i = 0; i < s.size(); ++i ) { + std::string subs; + switch( s[i] ) { + case '\n': subs = "\\n"; break; + case '\t': subs = "\\t"; break; + default: break; + } + if( !subs.empty() ) { + s = s.substr( 0, i ) + subs + s.substr( i+1 ); + ++i; + } + } + } + return '"' + s + '"'; +} +std::string toString( std::wstring const& value ) { + + std::string s; + s.reserve( value.size() ); + for(size_t i = 0; i < value.size(); ++i ) + s += value[i] <= 0xff ? static_cast( value[i] ) : '?'; + return Catch::toString( s ); +} + +std::string toString( const char* const value ) { + return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" ); +} + +std::string toString( char* const value ) { + return Catch::toString( static_cast( value ) ); +} + +std::string toString( const wchar_t* const value ) +{ + return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" ); +} + +std::string toString( wchar_t* const value ) +{ + return Catch::toString( static_cast( value ) ); +} + +std::string toString( int value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; + return oss.str(); +} + +std::string toString( unsigned long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; + return oss.str(); +} + +std::string toString( unsigned int value ) { + return Catch::toString( static_cast( value ) ); +} + +template +std::string fpToString( T value, int precision ) { + std::ostringstream oss; + oss << std::setprecision( precision ) + << std::fixed + << value; + std::string d = oss.str(); + std::size_t i = d.find_last_not_of( '0' ); + if( i != std::string::npos && i != d.size()-1 ) { + if( d[i] == '.' ) + i++; + d = d.substr( 0, i+1 ); + } + return d; +} + +std::string toString( const double value ) { + return fpToString( value, 10 ); +} +std::string toString( const float value ) { + return fpToString( value, 5 ) + 'f'; +} + +std::string toString( bool value ) { + return value ? "true" : "false"; +} + +std::string toString( char value ) { + if ( value == '\r' ) + return "'\\r'"; + if ( value == '\f' ) + return "'\\f'"; + if ( value == '\n' ) + return "'\\n'"; + if ( value == '\t' ) + return "'\\t'"; + if ( '\0' <= value && value < ' ' ) + return toString( static_cast( value ) ); + char chstr[] = "' '"; + chstr[1] = value; + return chstr; +} + +std::string toString( signed char value ) { + return toString( static_cast( value ) ); +} + +std::string toString( unsigned char value ) { + return toString( static_cast( value ) ); +} + +#ifdef CATCH_CONFIG_CPP11_LONG_LONG +std::string toString( long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; + return oss.str(); +} +std::string toString( unsigned long long value ) { + std::ostringstream oss; + oss << value; + if( value > Detail::hexThreshold ) + oss << " (0x" << std::hex << value << ')'; + return oss.str(); +} +#endif + +#ifdef CATCH_CONFIG_CPP11_NULLPTR +std::string toString( std::nullptr_t ) { + return "nullptr"; +} +#endif + +#ifdef __OBJC__ + std::string toString( NSString const * const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSString * CATCH_ARC_STRONG const& nsstring ) { + if( !nsstring ) + return "nil"; + return "@" + toString([nsstring UTF8String]); + } + std::string toString( NSObject* const& nsObject ) { + return toString( [nsObject description] ); + } +#endif + +} // end namespace Catch + +// #included from: catch_result_builder.hpp +#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED + +namespace Catch { + + std::string capturedExpressionWithSecondArgument( std::string const& capturedExpression, std::string const& secondArg ) { + return secondArg.empty() || secondArg == "\"\"" + ? capturedExpression + : capturedExpression + ", " + secondArg; + } + ResultBuilder::ResultBuilder( char const* macroName, + SourceLineInfo const& lineInfo, + char const* capturedExpression, + ResultDisposition::Flags resultDisposition, + char const* secondArg ) + : m_assertionInfo( macroName, lineInfo, capturedExpressionWithSecondArgument( capturedExpression, secondArg ), resultDisposition ), + m_shouldDebugBreak( false ), + m_shouldThrow( false ) + {} + + ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) { + m_data.resultType = result; + return *this; + } + ResultBuilder& ResultBuilder::setResultType( bool result ) { + m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed; + return *this; + } + + void ResultBuilder::endExpression( DecomposedExpression const& expr ) { + AssertionResult result = build( expr ); + handleResult( result ); + } + + void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) { + m_assertionInfo.resultDisposition = resultDisposition; + m_stream.oss << Catch::translateActiveException(); + captureResult( ResultWas::ThrewException ); + } + + void ResultBuilder::captureResult( ResultWas::OfType resultType ) { + setResultType( resultType ); + captureExpression(); + } + + void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) { + if( expectedMessage.empty() ) + captureExpectedException( Matchers::Impl::Generic::AllOf() ); + else + captureExpectedException( Matchers::Equals( expectedMessage ) ); + } + + void ResultBuilder::captureExpectedException( Matchers::Impl::Matcher const& matcher ) { + + assert( !isFalseTest( m_assertionInfo.resultDisposition ) ); + AssertionResultData data = m_data; + data.resultType = ResultWas::Ok; + data.reconstructedExpression = m_assertionInfo.capturedExpression; + + std::string actualMessage = Catch::translateActiveException(); + if( !matcher.match( actualMessage ) ) { + data.resultType = ResultWas::ExpressionFailed; + data.reconstructedExpression = actualMessage; + } + AssertionResult result( m_assertionInfo, data ); + handleResult( result ); + } + + void ResultBuilder::captureExpression() { + AssertionResult result = build(); + handleResult( result ); + } + + void ResultBuilder::handleResult( AssertionResult const& result ) + { + getResultCapture().assertionEnded( result ); + + if( !result.isOk() ) { + if( getCurrentContext().getConfig()->shouldDebugBreak() ) + m_shouldDebugBreak = true; + if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) ) + m_shouldThrow = true; + } + } + + void ResultBuilder::react() { + if( m_shouldThrow ) + throw Catch::TestFailureException(); + } + + bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; } + bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); } + + AssertionResult ResultBuilder::build() const + { + return build( *this ); + } + + // CAVEAT: The returned AssertionResult stores a pointer to the argument expr, + // a temporary DecomposedExpression, which in turn holds references to + // operands, possibly temporary as well. + // It should immediately be passed to handleResult; if the expression + // needs to be reported, its string expansion must be composed before + // the temporaries are destroyed. + AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const + { + assert( m_data.resultType != ResultWas::Unknown ); + AssertionResultData data = m_data; + + // Flip bool results if FalseTest flag is set + if( isFalseTest( m_assertionInfo.resultDisposition ) ) { + data.negate( expr.isBinaryExpression() ); + } + + data.message = m_stream.oss.str(); + data.decomposedExpression = &expr; // for lazy reconstruction + return AssertionResult( m_assertionInfo, data ); + } + + void ResultBuilder::reconstructExpression( std::string& dest ) const { + dest = m_assertionInfo.capturedExpression; + } + +} // end namespace Catch + +// #included from: catch_tag_alias_registry.hpp +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED + +// #included from: catch_tag_alias_registry.h +#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED + +#include + +namespace Catch { + + class TagAliasRegistry : public ITagAliasRegistry { + public: + virtual ~TagAliasRegistry(); + virtual Option find( std::string const& alias ) const; + virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const; + void add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + static TagAliasRegistry& get(); + + private: + std::map m_registry; + }; + +} // end namespace Catch + +namespace Catch { + + TagAliasRegistry::~TagAliasRegistry() {} + + Option TagAliasRegistry::find( std::string const& alias ) const { + std::map::const_iterator it = m_registry.find( alias ); + if( it != m_registry.end() ) + return it->second; + else + return Option(); + } + + std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const { + std::string expandedTestSpec = unexpandedTestSpec; + for( std::map::const_iterator it = m_registry.begin(), itEnd = m_registry.end(); + it != itEnd; + ++it ) { + std::size_t pos = expandedTestSpec.find( it->first ); + if( pos != std::string::npos ) { + expandedTestSpec = expandedTestSpec.substr( 0, pos ) + + it->second.tag + + expandedTestSpec.substr( pos + it->first.size() ); + } + } + return expandedTestSpec; + } + + void TagAliasRegistry::add( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + + if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n" << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) { + std::ostringstream oss; + oss << "error: tag alias, \"" << alias << "\" already registered.\n" + << "\tFirst seen at " << find(alias)->lineInfo << '\n' + << "\tRedefined at " << lineInfo; + throw std::domain_error( oss.str().c_str() ); + } + } + + TagAliasRegistry& TagAliasRegistry::get() { + static TagAliasRegistry instance; + return instance; + + } + + ITagAliasRegistry::~ITagAliasRegistry() {} + ITagAliasRegistry const& ITagAliasRegistry::get() { return TagAliasRegistry::get(); } + + RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) { + try { + TagAliasRegistry::get().add( alias, tag, lineInfo ); + } + catch( std::exception& ex ) { + Colour colourGuard( Colour::Red ); + Catch::cerr() << ex.what() << std::endl; + exit(1); + } + } + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_multi.hpp +#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED + +namespace Catch { + +class MultipleReporters : public SharedImpl { + typedef std::vector > Reporters; + Reporters m_reporters; + +public: + void add( Ptr const& reporter ) { + m_reporters.push_back( reporter ); + } + +public: // IStreamingReporter + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporters[0]->getPreferences(); + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->noMatchingTestCases( spec ); + } + + virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunStarting( testRunInfo ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupStarting( groupInfo ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseStarting( testInfo ); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionStarting( sectionInfo ); + } + + virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->assertionStarting( assertionInfo ); + } + + // The return value indicates if the messages buffer should be cleared: + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + bool clearBuffer = false; + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + clearBuffer |= (*it)->assertionEnded( assertionStats ); + return clearBuffer; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->sectionEnded( sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testGroupEnded( testGroupStats ); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->testRunEnded( testRunStats ); + } + + virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end(); + it != itEnd; + ++it ) + (*it)->skipTest( testInfo ); + } + + virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE { + return this; + } + +}; + +Ptr addReporter( Ptr const& existingReporter, Ptr const& additionalReporter ) { + Ptr resultingReporter; + + if( existingReporter ) { + MultipleReporters* multi = existingReporter->tryAsMulti(); + if( !multi ) { + multi = new MultipleReporters; + resultingReporter = Ptr( multi ); + if( existingReporter ) + multi->add( existingReporter ); + } + else + resultingReporter = existingReporter; + multi->add( additionalReporter ); + } + else + resultingReporter = additionalReporter; + + return resultingReporter; +} + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_xml.hpp +#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED + +// #included from: catch_reporter_bases.hpp +#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED + +#include + +namespace Catch { + + struct StreamingReporterBase : SharedImpl { + + StreamingReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual ~StreamingReporterBase() CATCH_OVERRIDE; + + virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE { + currentTestRunInfo = _testRunInfo; + } + virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE { + currentGroupInfo = _groupInfo; + } + + virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE { + currentTestCaseInfo = _testInfo; + } + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_sectionStack.push_back( _sectionInfo ); + } + + virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE { + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + } + virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE { + currentGroupInfo.reset(); + } + virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE { + currentTestCaseInfo.reset(); + currentGroupInfo.reset(); + currentTestRunInfo.reset(); + } + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE { + // Don't do anything with this by default. + // It can optionally be overridden in the derived class. + } + + Ptr m_config; + std::ostream& stream; + + LazyStat currentTestRunInfo; + LazyStat currentGroupInfo; + LazyStat currentTestCaseInfo; + + std::vector m_sectionStack; + ReporterPreferences m_reporterPrefs; + }; + + struct CumulativeReporterBase : SharedImpl { + template + struct Node : SharedImpl<> { + explicit Node( T const& _value ) : value( _value ) {} + virtual ~Node() {} + + typedef std::vector > ChildNodes; + T value; + ChildNodes children; + }; + struct SectionNode : SharedImpl<> { + explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {} + virtual ~SectionNode(); + + bool operator == ( SectionNode const& other ) const { + return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo; + } + bool operator == ( Ptr const& other ) const { + return operator==( *other ); + } + + SectionStats stats; + typedef std::vector > ChildSections; + typedef std::vector Assertions; + ChildSections childSections; + Assertions assertions; + std::string stdOut; + std::string stdErr; + }; + + struct BySectionInfo { + BySectionInfo( SectionInfo const& other ) : m_other( other ) {} + BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {} + bool operator() ( Ptr const& node ) const { + return node->stats.sectionInfo.lineInfo == m_other.lineInfo; + } + private: + void operator=( BySectionInfo const& ); + SectionInfo const& m_other; + }; + + typedef Node TestCaseNode; + typedef Node TestGroupNode; + typedef Node TestRunNode; + + CumulativeReporterBase( ReporterConfig const& _config ) + : m_config( _config.fullConfig() ), + stream( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = false; + } + ~CumulativeReporterBase(); + + virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE { + return m_reporterPrefs; + } + + virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {} + virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {} + + virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + SectionStats incompleteStats( sectionInfo, Counts(), 0, false ); + Ptr node; + if( m_sectionStack.empty() ) { + if( !m_rootSection ) + m_rootSection = new SectionNode( incompleteStats ); + node = m_rootSection; + } + else { + SectionNode& parentNode = *m_sectionStack.back(); + SectionNode::ChildSections::const_iterator it = + std::find_if( parentNode.childSections.begin(), + parentNode.childSections.end(), + BySectionInfo( sectionInfo ) ); + if( it == parentNode.childSections.end() ) { + node = new SectionNode( incompleteStats ); + parentNode.childSections.push_back( node ); + } + else + node = *it; + } + m_sectionStack.push_back( node ); + m_deepestSection = node; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& sectionNode = *m_sectionStack.back(); + sectionNode.assertions.push_back( assertionStats ); + // AssertionResult holds a pointer to a temporary DecomposedExpression, + // which getExpandedExpression() calls to build the expression string. + // Our section stack copy of the assertionResult will likely outlive the + // temporary, so it must be expanded or discarded now to avoid calling + // a destroyed object later. + prepareExpandedExpression( sectionNode.assertions.back().assertionResult ); + return true; + } + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + assert( !m_sectionStack.empty() ); + SectionNode& node = *m_sectionStack.back(); + node.stats = sectionStats; + m_sectionStack.pop_back(); + } + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + Ptr node = new TestCaseNode( testCaseStats ); + assert( m_sectionStack.size() == 0 ); + node->children.push_back( m_rootSection ); + m_testCases.push_back( node ); + m_rootSection.reset(); + + assert( m_deepestSection ); + m_deepestSection->stdOut = testCaseStats.stdOut; + m_deepestSection->stdErr = testCaseStats.stdErr; + } + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + Ptr node = new TestGroupNode( testGroupStats ); + node->children.swap( m_testCases ); + m_testGroups.push_back( node ); + } + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + Ptr node = new TestRunNode( testRunStats ); + node->children.swap( m_testGroups ); + m_testRuns.push_back( node ); + testRunEndedCumulative(); + } + virtual void testRunEndedCumulative() = 0; + + virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {} + + virtual void prepareExpandedExpression( AssertionResult& result ) const { + if( result.isOk() ) + result.discardDecomposedExpression(); + else + result.expandDecomposedExpression(); + } + + Ptr m_config; + std::ostream& stream; + std::vector m_assertions; + std::vector > > m_sections; + std::vector > m_testCases; + std::vector > m_testGroups; + + std::vector > m_testRuns; + + Ptr m_rootSection; + Ptr m_deepestSection; + std::vector > m_sectionStack; + ReporterPreferences m_reporterPrefs; + + }; + + template + char const* getLineOfChars() { + static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0}; + if( !*line ) { + std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 ); + line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0; + } + return line; + } + + struct TestEventListenerBase : StreamingReporterBase { + TestEventListenerBase( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {} + virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE { + return false; + } + }; + +} // end namespace Catch + +// #included from: ../internal/catch_reporter_registrars.hpp +#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED + +namespace Catch { + + template + class LegacyReporterRegistrar { + + class ReporterFactory : public IReporterFactory { + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new LegacyReporterAdapter( new T( config ) ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + LegacyReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ReporterRegistrar { + + class ReporterFactory : public SharedImpl { + + // *** Please Note ***: + // - If you end up here looking at a compiler error because it's trying to register + // your custom reporter class be aware that the native reporter interface has changed + // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via + // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter. + // However please consider updating to the new interface as the old one is now + // deprecated and will probably be removed quite soon! + // Please contact me via github if you have any questions at all about this. + // In fact, ideally, please contact me anyway to let me know you've hit this - as I have + // no idea who is actually using custom reporters at all (possibly no-one!). + // The new interface is designed to minimise exposure to interface changes in the future. + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + + virtual std::string getDescription() const { + return T::getDescription(); + } + }; + + public: + + ReporterRegistrar( std::string const& name ) { + getMutableRegistryHub().registerReporter( name, new ReporterFactory() ); + } + }; + + template + class ListenerRegistrar { + + class ListenerFactory : public SharedImpl { + + virtual IStreamingReporter* create( ReporterConfig const& config ) const { + return new T( config ); + } + virtual std::string getDescription() const { + return std::string(); + } + }; + + public: + + ListenerRegistrar() { + getMutableRegistryHub().registerListener( new ListenerFactory() ); + } + }; +} + +#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \ + namespace{ Catch::LegacyReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \ + namespace{ Catch::ReporterRegistrar catch_internal_RegistrarFor##reporterType( name ); } + +#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \ + namespace{ Catch::ListenerRegistrar catch_internal_RegistrarFor##listenerType; } + +// #included from: ../internal/catch_xmlwriter.hpp +#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED + +#include +#include +#include +#include + +namespace Catch { + + class XmlEncode { + public: + enum ForWhat { ForTextNodes, ForAttributes }; + + XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes ) + : m_str( str ), + m_forWhat( forWhat ) + {} + + void encodeTo( std::ostream& os ) const { + + // Apostrophe escaping not necessary if we always use " to write attributes + // (see: http://www.w3.org/TR/xml/#syntax) + + for( std::size_t i = 0; i < m_str.size(); ++ i ) { + char c = m_str[i]; + switch( c ) { + case '<': os << "<"; break; + case '&': os << "&"; break; + + case '>': + // See: http://www.w3.org/TR/xml/#syntax + if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' ) + os << ">"; + else + os << c; + break; + + case '\"': + if( m_forWhat == ForAttributes ) + os << """; + else + os << c; + break; + + default: + // Escape control chars - based on contribution by @espenalb in PR #465 and + // by @mrpi PR #588 + if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) { + // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0 + os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2) + << static_cast( c ); + } + else + os << c; + } + } + } + + friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) { + xmlEncode.encodeTo( os ); + return os; + } + + private: + std::string m_str; + ForWhat m_forWhat; + }; + + class XmlWriter { + public: + + class ScopedElement { + public: + ScopedElement( XmlWriter* writer ) + : m_writer( writer ) + {} + + ScopedElement( ScopedElement const& other ) + : m_writer( other.m_writer ){ + other.m_writer = CATCH_NULL; + } + + ~ScopedElement() { + if( m_writer ) + m_writer->endElement(); + } + + ScopedElement& writeText( std::string const& text, bool indent = true ) { + m_writer->writeText( text, indent ); + return *this; + } + + template + ScopedElement& writeAttribute( std::string const& name, T const& attribute ) { + m_writer->writeAttribute( name, attribute ); + return *this; + } + + private: + mutable XmlWriter* m_writer; + }; + + XmlWriter() + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( Catch::cout() ) + { + writeDeclaration(); + } + + XmlWriter( std::ostream& os ) + : m_tagIsOpen( false ), + m_needsNewline( false ), + m_os( os ) + { + writeDeclaration(); + } + + ~XmlWriter() { + while( !m_tags.empty() ) + endElement(); + } + + XmlWriter& startElement( std::string const& name ) { + ensureTagClosed(); + newlineIfNecessary(); + m_os << m_indent << '<' << name; + m_tags.push_back( name ); + m_indent += " "; + m_tagIsOpen = true; + return *this; + } + + ScopedElement scopedElement( std::string const& name ) { + ScopedElement scoped( this ); + startElement( name ); + return scoped; + } + + XmlWriter& endElement() { + newlineIfNecessary(); + m_indent = m_indent.substr( 0, m_indent.size()-2 ); + if( m_tagIsOpen ) { + m_os << "/>"; + m_tagIsOpen = false; + } + else { + m_os << m_indent << ""; + } + m_os << std::endl; + m_tags.pop_back(); + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) { + if( !name.empty() && !attribute.empty() ) + m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"'; + return *this; + } + + XmlWriter& writeAttribute( std::string const& name, bool attribute ) { + m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"'; + return *this; + } + + template + XmlWriter& writeAttribute( std::string const& name, T const& attribute ) { + std::ostringstream oss; + oss << attribute; + return writeAttribute( name, oss.str() ); + } + + XmlWriter& writeText( std::string const& text, bool indent = true ) { + if( !text.empty() ){ + bool tagWasOpen = m_tagIsOpen; + ensureTagClosed(); + if( tagWasOpen && indent ) + m_os << m_indent; + m_os << XmlEncode( text ); + m_needsNewline = true; + } + return *this; + } + + XmlWriter& writeComment( std::string const& text ) { + ensureTagClosed(); + m_os << m_indent << ""; + m_needsNewline = true; + return *this; + } + + void writeStylesheetRef( std::string const& url ) { + m_os << "\n"; + } + + XmlWriter& writeBlankLine() { + ensureTagClosed(); + m_os << '\n'; + return *this; + } + + void ensureTagClosed() { + if( m_tagIsOpen ) { + m_os << ">" << std::endl; + m_tagIsOpen = false; + } + } + + private: + XmlWriter( XmlWriter const& ); + void operator=( XmlWriter const& ); + + void writeDeclaration() { + m_os << "\n"; + } + + void newlineIfNecessary() { + if( m_needsNewline ) { + m_os << std::endl; + m_needsNewline = false; + } + } + + bool m_tagIsOpen; + bool m_needsNewline; + std::vector m_tags; + std::string m_indent; + std::ostream& m_os; + }; + +} +// #included from: catch_reenable_warnings.h + +#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(pop) +# else +# pragma clang diagnostic pop +# endif +#elif defined __GNUC__ +# pragma GCC diagnostic pop +#endif + + +namespace Catch { + class XmlReporter : public StreamingReporterBase { + public: + XmlReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_xml(_config.stream()), + m_sectionDepth( 0 ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~XmlReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results as an XML document"; + } + + virtual std::string getStylesheetRef() const { + return std::string(); + } + + public: // StreamingReporterBase + + virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE { + StreamingReporterBase::noMatchingTestCases( s ); + } + + virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testRunStarting( testInfo ); + std::string stylesheetRef = getStylesheetRef(); + if( !stylesheetRef.empty() ) + m_xml.writeStylesheetRef( stylesheetRef ); + m_xml.startElement( "Catch" ); + if( !m_config->name().empty() ) + m_xml.writeAttribute( "name", m_config->name() ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupStarting( groupInfo ); + m_xml.startElement( "Group" ) + .writeAttribute( "name", groupInfo.name ); + } + + virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseStarting(testInfo); + m_xml.startElement( "TestCase" ) + .writeAttribute( "name", trim( testInfo.name ) ) + .writeAttribute( "description", testInfo.description ) + .writeAttribute( "tags", testInfo.tagsAsString ); + + if ( m_config->showDurations() == ShowDurations::Always ) + m_testCaseTimer.start(); + m_xml.ensureTagClosed(); + } + + virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE { + StreamingReporterBase::sectionStarting( sectionInfo ); + if( m_sectionDepth++ > 0 ) { + m_xml.startElement( "Section" ) + .writeAttribute( "name", trim( sectionInfo.name ) ) + .writeAttribute( "description", sectionInfo.description ); + m_xml.ensureTagClosed(); + } + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + const AssertionResult& assertionResult = assertionStats.assertionResult; + + // Print any info messages in tags. + if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) { + for( std::vector::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end(); + it != itEnd; + ++it ) { + if( it->type == ResultWas::Info ) { + m_xml.scopedElement( "Info" ) + .writeText( it->message ); + } else if ( it->type == ResultWas::Warning ) { + m_xml.scopedElement( "Warning" ) + .writeText( it->message ); + } + } + } + + // Drop out if result was successful but we're not printing them. + if( !m_config->includeSuccessfulResults() && isOk(assertionResult.getResultType()) ) + return true; + + // Print the expression if there is one. + if( assertionResult.hasExpression() ) { + m_xml.startElement( "Expression" ) + .writeAttribute( "success", assertionResult.succeeded() ) + .writeAttribute( "type", assertionResult.getTestMacroName() ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ); + + m_xml.scopedElement( "Original" ) + .writeText( assertionResult.getExpression() ); + m_xml.scopedElement( "Expanded" ) + .writeText( assertionResult.getExpandedExpression() ); + } + + // And... Print a result applicable to each result type. + switch( assertionResult.getResultType() ) { + case ResultWas::ThrewException: + m_xml.scopedElement( "Exception" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::FatalErrorCondition: + m_xml.scopedElement( "FatalErrorCondition" ) + .writeAttribute( "filename", assertionResult.getSourceInfo().file ) + .writeAttribute( "line", assertionResult.getSourceInfo().line ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Info: + m_xml.scopedElement( "Info" ) + .writeText( assertionResult.getMessage() ); + break; + case ResultWas::Warning: + // Warning will already have been written + break; + case ResultWas::ExplicitFailure: + m_xml.scopedElement( "Failure" ) + .writeText( assertionResult.getMessage() ); + break; + default: + break; + } + + if( assertionResult.hasExpression() ) + m_xml.endElement(); + + return true; + } + + virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE { + StreamingReporterBase::sectionEnded( sectionStats ); + if( --m_sectionDepth > 0 ) { + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" ); + e.writeAttribute( "successes", sectionStats.assertions.passed ); + e.writeAttribute( "failures", sectionStats.assertions.failed ); + e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds ); + + m_xml.endElement(); + } + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( testCaseStats ); + XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" ); + e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() ); + + if ( m_config->showDurations() == ShowDurations::Always ) + e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() ); + + if( !testCaseStats.stdOut.empty() ) + m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false ); + if( !testCaseStats.stdErr.empty() ) + m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false ); + + m_xml.endElement(); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + StreamingReporterBase::testGroupEnded( testGroupStats ); + // TODO: Check testGroupStats.aborting and act accordingly. + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testGroupStats.totals.assertions.passed ) + .writeAttribute( "failures", testGroupStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE { + StreamingReporterBase::testRunEnded( testRunStats ); + m_xml.scopedElement( "OverallResults" ) + .writeAttribute( "successes", testRunStats.totals.assertions.passed ) + .writeAttribute( "failures", testRunStats.totals.assertions.failed ) + .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk ); + m_xml.endElement(); + } + + private: + Timer m_testCaseTimer; + XmlWriter m_xml; + int m_sectionDepth; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_junit.hpp +#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED + +#include + +namespace Catch { + + namespace { + std::string getCurrentTimestamp() { + // Beware, this is not reentrant because of backward compatibility issues + // Also, UTC only, again because of backward compatibility (%z is C++11) + time_t rawtime; + std::time(&rawtime); + const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z"); + +#ifdef _MSC_VER + std::tm timeInfo = {}; + gmtime_s(&timeInfo, &rawtime); +#else + std::tm* timeInfo; + timeInfo = std::gmtime(&rawtime); +#endif + + char timeStamp[timeStampSize]; + const char * const fmt = "%Y-%m-%dT%H:%M:%SZ"; + +#ifdef _MSC_VER + std::strftime(timeStamp, timeStampSize, fmt, &timeInfo); +#else + std::strftime(timeStamp, timeStampSize, fmt, timeInfo); +#endif + return std::string(timeStamp); + } + + } + + class JunitReporter : public CumulativeReporterBase { + public: + JunitReporter( ReporterConfig const& _config ) + : CumulativeReporterBase( _config ), + xml( _config.stream() ) + { + m_reporterPrefs.shouldRedirectStdOut = true; + } + + virtual ~JunitReporter() CATCH_OVERRIDE; + + static std::string getDescription() { + return "Reports test results in an XML format that looks like Ant's junitreport target"; + } + + virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {} + + virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE { + CumulativeReporterBase::testRunStarting( runInfo ); + xml.startElement( "testsuites" ); + } + + virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE { + suiteTimer.start(); + stdOutForSuite.str(""); + stdErrForSuite.str(""); + unexpectedExceptions = 0; + CumulativeReporterBase::testGroupStarting( groupInfo ); + } + + virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE { + if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException ) + unexpectedExceptions++; + return CumulativeReporterBase::assertionEnded( assertionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE { + stdOutForSuite << testCaseStats.stdOut; + stdErrForSuite << testCaseStats.stdErr; + CumulativeReporterBase::testCaseEnded( testCaseStats ); + } + + virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE { + double suiteTime = suiteTimer.getElapsedSeconds(); + CumulativeReporterBase::testGroupEnded( testGroupStats ); + writeGroup( *m_testGroups.back(), suiteTime ); + } + + virtual void testRunEndedCumulative() CATCH_OVERRIDE { + xml.endElement(); + } + + void writeGroup( TestGroupNode const& groupNode, double suiteTime ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" ); + TestGroupStats const& stats = groupNode.value; + xml.writeAttribute( "name", stats.groupInfo.name ); + xml.writeAttribute( "errors", unexpectedExceptions ); + xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions ); + xml.writeAttribute( "tests", stats.totals.assertions.total() ); + xml.writeAttribute( "hostname", "tbd" ); // !TBD + if( m_config->showDurations() == ShowDurations::Never ) + xml.writeAttribute( "time", "" ); + else + xml.writeAttribute( "time", suiteTime ); + xml.writeAttribute( "timestamp", getCurrentTimestamp() ); + + // Write test cases + for( TestGroupNode::ChildNodes::const_iterator + it = groupNode.children.begin(), itEnd = groupNode.children.end(); + it != itEnd; + ++it ) + writeTestCase( **it ); + + xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false ); + xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false ); + } + + void writeTestCase( TestCaseNode const& testCaseNode ) { + TestCaseStats const& stats = testCaseNode.value; + + // All test cases have exactly one section - which represents the + // test case itself. That section may have 0-n nested sections + assert( testCaseNode.children.size() == 1 ); + SectionNode const& rootSection = *testCaseNode.children.front(); + + std::string className = stats.testInfo.className; + + if( className.empty() ) { + if( rootSection.childSections.empty() ) + className = "global"; + } + writeSection( className, "", rootSection ); + } + + void writeSection( std::string const& className, + std::string const& rootName, + SectionNode const& sectionNode ) { + std::string name = trim( sectionNode.stats.sectionInfo.name ); + if( !rootName.empty() ) + name = rootName + '/' + name; + + if( !sectionNode.assertions.empty() || + !sectionNode.stdOut.empty() || + !sectionNode.stdErr.empty() ) { + XmlWriter::ScopedElement e = xml.scopedElement( "testcase" ); + if( className.empty() ) { + xml.writeAttribute( "classname", name ); + xml.writeAttribute( "name", "root" ); + } + else { + xml.writeAttribute( "classname", className ); + xml.writeAttribute( "name", name ); + } + xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) ); + + writeAssertions( sectionNode ); + + if( !sectionNode.stdOut.empty() ) + xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false ); + if( !sectionNode.stdErr.empty() ) + xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false ); + } + for( SectionNode::ChildSections::const_iterator + it = sectionNode.childSections.begin(), + itEnd = sectionNode.childSections.end(); + it != itEnd; + ++it ) + if( className.empty() ) + writeSection( name, "", **it ); + else + writeSection( className, name, **it ); + } + + void writeAssertions( SectionNode const& sectionNode ) { + for( SectionNode::Assertions::const_iterator + it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end(); + it != itEnd; + ++it ) + writeAssertion( *it ); + } + void writeAssertion( AssertionStats const& stats ) { + AssertionResult const& result = stats.assertionResult; + if( !result.isOk() ) { + std::string elementName; + switch( result.getResultType() ) { + case ResultWas::ThrewException: + case ResultWas::FatalErrorCondition: + elementName = "error"; + break; + case ResultWas::ExplicitFailure: + elementName = "failure"; + break; + case ResultWas::ExpressionFailed: + elementName = "failure"; + break; + case ResultWas::DidntThrowException: + elementName = "failure"; + break; + + // We should never see these here: + case ResultWas::Info: + case ResultWas::Warning: + case ResultWas::Ok: + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + elementName = "internalError"; + break; + } + + XmlWriter::ScopedElement e = xml.scopedElement( elementName ); + + xml.writeAttribute( "message", result.getExpandedExpression() ); + xml.writeAttribute( "type", result.getTestMacroName() ); + + std::ostringstream oss; + if( !result.getMessage().empty() ) + oss << result.getMessage() << '\n'; + for( std::vector::const_iterator + it = stats.infoMessages.begin(), + itEnd = stats.infoMessages.end(); + it != itEnd; + ++it ) + if( it->type == ResultWas::Info ) + oss << it->message << '\n'; + + oss << "at " << result.getSourceInfo(); + xml.writeText( oss.str(), false ); + } + } + + XmlWriter xml; + Timer suiteTimer; + std::ostringstream stdOutForSuite; + std::ostringstream stdErrForSuite; + unsigned int unexpectedExceptions; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_console.hpp +#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED + +namespace Catch { + + struct ConsoleReporter : StreamingReporterBase { + ConsoleReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ), + m_headerPrinted( false ) + {} + + virtual ~ConsoleReporter() CATCH_OVERRIDE; + static std::string getDescription() { + return "Reports test results as plain lines of text"; + } + + virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + lazyPrint(); + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + stream << std::endl; + return true; + } + + virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE { + m_headerPrinted = false; + StreamingReporterBase::sectionStarting( _sectionInfo ); + } + virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE { + if( _sectionStats.missingAssertions ) { + lazyPrint(); + Colour colour( Colour::ResultError ); + if( m_sectionStack.size() > 1 ) + stream << "\nNo assertions in section"; + else + stream << "\nNo assertions in test case"; + stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl; + } + if( m_headerPrinted ) { + if( m_config->showDurations() == ShowDurations::Always ) + stream << "Completed in " << _sectionStats.durationInSeconds << 's' << std::endl; + m_headerPrinted = false; + } + else { + if( m_config->showDurations() == ShowDurations::Always ) + stream << _sectionStats.sectionInfo.name << " completed in " << _sectionStats.durationInSeconds << 's' << std::endl; + } + StreamingReporterBase::sectionEnded( _sectionStats ); + } + + virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE { + StreamingReporterBase::testCaseEnded( _testCaseStats ); + m_headerPrinted = false; + } + virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE { + if( currentGroupInfo.used ) { + printSummaryDivider(); + stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n"; + printTotals( _testGroupStats.totals ); + stream << '\n' << std::endl; + } + StreamingReporterBase::testGroupEnded( _testGroupStats ); + } + virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE { + printTotalsDivider( _testRunStats.totals ); + printTotals( _testRunStats.totals ); + stream << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ), + stats( _stats ), + result( _stats.assertionResult ), + colour( Colour::None ), + message( result.getMessage() ), + messages( _stats.infoMessages ), + printInfoMessages( _printInfoMessages ) + { + switch( result.getResultType() ) { + case ResultWas::Ok: + colour = Colour::Success; + passOrFail = "PASSED"; + //if( result.hasMessage() ) + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) { + colour = Colour::Success; + passOrFail = "FAILED - but was ok"; + } + else { + colour = Colour::Error; + passOrFail = "FAILED"; + } + if( _stats.infoMessages.size() == 1 ) + messageLabel = "with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "with messages"; + break; + case ResultWas::ThrewException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to unexpected exception with message"; + break; + case ResultWas::FatalErrorCondition: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "due to a fatal error condition"; + break; + case ResultWas::DidntThrowException: + colour = Colour::Error; + passOrFail = "FAILED"; + messageLabel = "because no exception was thrown where one was expected"; + break; + case ResultWas::Info: + messageLabel = "info"; + break; + case ResultWas::Warning: + messageLabel = "warning"; + break; + case ResultWas::ExplicitFailure: + passOrFail = "FAILED"; + colour = Colour::Error; + if( _stats.infoMessages.size() == 1 ) + messageLabel = "explicitly with message"; + if( _stats.infoMessages.size() > 1 ) + messageLabel = "explicitly with messages"; + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + passOrFail = "** internal error **"; + colour = Colour::Error; + break; + } + } + + void print() const { + printSourceInfo(); + if( stats.totals.assertions.total() > 0 ) { + if( result.isOk() ) + stream << '\n'; + printResultType(); + printOriginalExpression(); + printReconstructedExpression(); + } + else { + stream << '\n'; + } + printMessage(); + } + + private: + void printResultType() const { + if( !passOrFail.empty() ) { + Colour colourGuard( colour ); + stream << passOrFail << ":\n"; + } + } + void printOriginalExpression() const { + if( result.hasExpression() ) { + Colour colourGuard( Colour::OriginalExpression ); + stream << " "; + stream << result.getExpressionInMacro(); + stream << '\n'; + } + } + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + stream << "with expansion:\n"; + Colour colourGuard( Colour::ReconstructedExpression ); + stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n'; + } + } + void printMessage() const { + if( !messageLabel.empty() ) + stream << messageLabel << ':' << '\n'; + for( std::vector::const_iterator it = messages.begin(), itEnd = messages.end(); + it != itEnd; + ++it ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || it->type != ResultWas::Info ) + stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n'; + } + } + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ": "; + } + + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + Colour::Code colour; + std::string passOrFail; + std::string messageLabel; + std::string message; + std::vector messages; + bool printInfoMessages; + }; + + void lazyPrint() { + + if( !currentTestRunInfo.used ) + lazyPrintRunInfo(); + if( !currentGroupInfo.used ) + lazyPrintGroupInfo(); + + if( !m_headerPrinted ) { + printTestCaseAndSectionHeader(); + m_headerPrinted = true; + } + } + void lazyPrintRunInfo() { + stream << '\n' << getLineOfChars<'~'>() << '\n'; + Colour colour( Colour::SecondaryText ); + stream << currentTestRunInfo->name + << " is a Catch v" << libraryVersion << " host application.\n" + << "Run with -? for options\n\n"; + + if( m_config->rngSeed() != 0 ) + stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n"; + + currentTestRunInfo.used = true; + } + void lazyPrintGroupInfo() { + if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) { + printClosedHeader( "Group: " + currentGroupInfo->name ); + currentGroupInfo.used = true; + } + } + void printTestCaseAndSectionHeader() { + assert( !m_sectionStack.empty() ); + printOpenHeader( currentTestCaseInfo->name ); + + if( m_sectionStack.size() > 1 ) { + Colour colourGuard( Colour::Headers ); + + std::vector::const_iterator + it = m_sectionStack.begin()+1, // Skip first section (test case) + itEnd = m_sectionStack.end(); + for( ; it != itEnd; ++it ) + printHeaderString( it->name, 2 ); + } + + SourceLineInfo lineInfo = m_sectionStack.back().lineInfo; + + if( !lineInfo.empty() ){ + stream << getLineOfChars<'-'>() << '\n'; + Colour colourGuard( Colour::FileName ); + stream << lineInfo << '\n'; + } + stream << getLineOfChars<'.'>() << '\n' << std::endl; + } + + void printClosedHeader( std::string const& _name ) { + printOpenHeader( _name ); + stream << getLineOfChars<'.'>() << '\n'; + } + void printOpenHeader( std::string const& _name ) { + stream << getLineOfChars<'-'>() << '\n'; + { + Colour colourGuard( Colour::Headers ); + printHeaderString( _name ); + } + } + + // if string has a : in first line will set indent to follow it on + // subsequent lines + void printHeaderString( std::string const& _string, std::size_t indent = 0 ) { + std::size_t i = _string.find( ": " ); + if( i != std::string::npos ) + i+=2; + else + i = 0; + stream << Text( _string, TextAttributes() + .setIndent( indent+i) + .setInitialIndent( indent ) ) << '\n'; + } + + struct SummaryColumn { + + SummaryColumn( std::string const& _label, Colour::Code _colour ) + : label( _label ), + colour( _colour ) + {} + SummaryColumn addRow( std::size_t count ) { + std::ostringstream oss; + oss << count; + std::string row = oss.str(); + for( std::vector::iterator it = rows.begin(); it != rows.end(); ++it ) { + while( it->size() < row.size() ) + *it = ' ' + *it; + while( it->size() > row.size() ) + row = ' ' + row; + } + rows.push_back( row ); + return *this; + } + + std::string label; + Colour::Code colour; + std::vector rows; + + }; + + void printTotals( Totals const& totals ) { + if( totals.testCases.total() == 0 ) { + stream << Colour( Colour::Warning ) << "No tests ran\n"; + } + else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) { + stream << Colour( Colour::ResultSuccess ) << "All tests passed"; + stream << " (" + << pluralise( totals.assertions.passed, "assertion" ) << " in " + << pluralise( totals.testCases.passed, "test case" ) << ')' + << '\n'; + } + else { + + std::vector columns; + columns.push_back( SummaryColumn( "", Colour::None ) + .addRow( totals.testCases.total() ) + .addRow( totals.assertions.total() ) ); + columns.push_back( SummaryColumn( "passed", Colour::Success ) + .addRow( totals.testCases.passed ) + .addRow( totals.assertions.passed ) ); + columns.push_back( SummaryColumn( "failed", Colour::ResultError ) + .addRow( totals.testCases.failed ) + .addRow( totals.assertions.failed ) ); + columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure ) + .addRow( totals.testCases.failedButOk ) + .addRow( totals.assertions.failedButOk ) ); + + printSummaryRow( "test cases", columns, 0 ); + printSummaryRow( "assertions", columns, 1 ); + } + } + void printSummaryRow( std::string const& label, std::vector const& cols, std::size_t row ) { + for( std::vector::const_iterator it = cols.begin(); it != cols.end(); ++it ) { + std::string value = it->rows[row]; + if( it->label.empty() ) { + stream << label << ": "; + if( value != "0" ) + stream << value; + else + stream << Colour( Colour::Warning ) << "- none -"; + } + else if( value != "0" ) { + stream << Colour( Colour::LightGrey ) << " | "; + stream << Colour( it->colour ) + << value << ' ' << it->label; + } + } + stream << '\n'; + } + + static std::size_t makeRatio( std::size_t number, std::size_t total ) { + std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0; + return ( ratio == 0 && number > 0 ) ? 1 : ratio; + } + static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) { + if( i > j && i > k ) + return i; + else if( j > k ) + return j; + else + return k; + } + + void printTotalsDivider( Totals const& totals ) { + if( totals.testCases.total() > 0 ) { + std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() ); + std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() ); + std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() ); + while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )++; + while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 ) + findMax( failedRatio, failedButOkRatio, passedRatio )--; + + stream << Colour( Colour::Error ) << std::string( failedRatio, '=' ); + stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' ); + if( totals.testCases.allPassed() ) + stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' ); + else + stream << Colour( Colour::Success ) << std::string( passedRatio, '=' ); + } + else { + stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' ); + } + stream << '\n'; + } + void printSummaryDivider() { + stream << getLineOfChars<'-'>() << '\n'; + } + + private: + bool m_headerPrinted; + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter ) + +} // end namespace Catch + +// #included from: ../reporters/catch_reporter_compact.hpp +#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED + +namespace Catch { + + struct CompactReporter : StreamingReporterBase { + + CompactReporter( ReporterConfig const& _config ) + : StreamingReporterBase( _config ) + {} + + virtual ~CompactReporter(); + + static std::string getDescription() { + return "Reports test results on a single line, suitable for IDEs"; + } + + virtual ReporterPreferences getPreferences() const { + ReporterPreferences prefs; + prefs.shouldRedirectStdOut = false; + return prefs; + } + + virtual void noMatchingTestCases( std::string const& spec ) { + stream << "No test cases matched '" << spec << '\'' << std::endl; + } + + virtual void assertionStarting( AssertionInfo const& ) { + } + + virtual bool assertionEnded( AssertionStats const& _assertionStats ) { + AssertionResult const& result = _assertionStats.assertionResult; + + bool printInfoMessages = true; + + // Drop out if result was successful and we're not printing those + if( !m_config->includeSuccessfulResults() && result.isOk() ) { + if( result.getResultType() != ResultWas::Warning ) + return false; + printInfoMessages = false; + } + + AssertionPrinter printer( stream, _assertionStats, printInfoMessages ); + printer.print(); + + stream << std::endl; + return true; + } + + virtual void testRunEnded( TestRunStats const& _testRunStats ) { + printTotals( _testRunStats.totals ); + stream << '\n' << std::endl; + StreamingReporterBase::testRunEnded( _testRunStats ); + } + + private: + class AssertionPrinter { + void operator= ( AssertionPrinter const& ); + public: + AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages ) + : stream( _stream ) + , stats( _stats ) + , result( _stats.assertionResult ) + , messages( _stats.infoMessages ) + , itMessage( _stats.infoMessages.begin() ) + , printInfoMessages( _printInfoMessages ) + {} + + void print() { + printSourceInfo(); + + itMessage = messages.begin(); + + switch( result.getResultType() ) { + case ResultWas::Ok: + printResultType( Colour::ResultSuccess, passedString() ); + printOriginalExpression(); + printReconstructedExpression(); + if ( ! result.hasExpression() ) + printRemainingMessages( Colour::None ); + else + printRemainingMessages(); + break; + case ResultWas::ExpressionFailed: + if( result.isOk() ) + printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) ); + else + printResultType( Colour::Error, failedString() ); + printOriginalExpression(); + printReconstructedExpression(); + printRemainingMessages(); + break; + case ResultWas::ThrewException: + printResultType( Colour::Error, failedString() ); + printIssue( "unexpected exception with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::FatalErrorCondition: + printResultType( Colour::Error, failedString() ); + printIssue( "fatal error condition with message:" ); + printMessage(); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::DidntThrowException: + printResultType( Colour::Error, failedString() ); + printIssue( "expected exception, got none" ); + printExpressionWas(); + printRemainingMessages(); + break; + case ResultWas::Info: + printResultType( Colour::None, "info" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::Warning: + printResultType( Colour::None, "warning" ); + printMessage(); + printRemainingMessages(); + break; + case ResultWas::ExplicitFailure: + printResultType( Colour::Error, failedString() ); + printIssue( "explicitly" ); + printRemainingMessages( Colour::None ); + break; + // These cases are here to prevent compiler warnings + case ResultWas::Unknown: + case ResultWas::FailureBit: + case ResultWas::Exception: + printResultType( Colour::Error, "** internal error **" ); + break; + } + } + + private: + // Colour::LightGrey + + static Colour::Code dimColour() { return Colour::FileName; } + +#ifdef CATCH_PLATFORM_MAC + static const char* failedString() { return "FAILED"; } + static const char* passedString() { return "PASSED"; } +#else + static const char* failedString() { return "failed"; } + static const char* passedString() { return "passed"; } +#endif + + void printSourceInfo() const { + Colour colourGuard( Colour::FileName ); + stream << result.getSourceInfo() << ':'; + } + + void printResultType( Colour::Code colour, std::string passOrFail ) const { + if( !passOrFail.empty() ) { + { + Colour colourGuard( colour ); + stream << ' ' << passOrFail; + } + stream << ':'; + } + } + + void printIssue( std::string issue ) const { + stream << ' ' << issue; + } + + void printExpressionWas() { + if( result.hasExpression() ) { + stream << ';'; + { + Colour colour( dimColour() ); + stream << " expression was:"; + } + printOriginalExpression(); + } + } + + void printOriginalExpression() const { + if( result.hasExpression() ) { + stream << ' ' << result.getExpression(); + } + } + + void printReconstructedExpression() const { + if( result.hasExpandedExpression() ) { + { + Colour colour( dimColour() ); + stream << " for: "; + } + stream << result.getExpandedExpression(); + } + } + + void printMessage() { + if ( itMessage != messages.end() ) { + stream << " '" << itMessage->message << '\''; + ++itMessage; + } + } + + void printRemainingMessages( Colour::Code colour = dimColour() ) { + if ( itMessage == messages.end() ) + return; + + // using messages.end() directly yields compilation error: + std::vector::const_iterator itEnd = messages.end(); + const std::size_t N = static_cast( std::distance( itMessage, itEnd ) ); + + { + Colour colourGuard( colour ); + stream << " with " << pluralise( N, "message" ) << ':'; + } + + for(; itMessage != itEnd; ) { + // If this assertion is a warning ignore any INFO messages + if( printInfoMessages || itMessage->type != ResultWas::Info ) { + stream << " '" << itMessage->message << '\''; + if ( ++itMessage != itEnd ) { + Colour colourGuard( dimColour() ); + stream << " and"; + } + } + } + } + + private: + std::ostream& stream; + AssertionStats const& stats; + AssertionResult const& result; + std::vector messages; + std::vector::const_iterator itMessage; + bool printInfoMessages; + }; + + // Colour, message variants: + // - white: No tests ran. + // - red: Failed [both/all] N test cases, failed [both/all] M assertions. + // - white: Passed [both/all] N test cases (no assertions). + // - red: Failed N tests cases, failed M assertions. + // - green: Passed [both/all] N tests cases with M assertions. + + std::string bothOrAll( std::size_t count ) const { + return count == 1 ? std::string() : count == 2 ? "both " : "all " ; + } + + void printTotals( const Totals& totals ) const { + if( totals.testCases.total() == 0 ) { + stream << "No tests ran."; + } + else if( totals.testCases.failed == totals.testCases.total() ) { + Colour colour( Colour::ResultError ); + const std::string qualify_assertions_failed = + totals.assertions.failed == totals.assertions.total() ? + bothOrAll( totals.assertions.failed ) : std::string(); + stream << + "Failed " << bothOrAll( totals.testCases.failed ) + << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << qualify_assertions_failed << + pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else if( totals.assertions.total() == 0 ) { + stream << + "Passed " << bothOrAll( totals.testCases.total() ) + << pluralise( totals.testCases.total(), "test case" ) + << " (no assertions)."; + } + else if( totals.assertions.failed ) { + Colour colour( Colour::ResultError ); + stream << + "Failed " << pluralise( totals.testCases.failed, "test case" ) << ", " + "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.'; + } + else { + Colour colour( Colour::ResultSuccess ); + stream << + "Passed " << bothOrAll( totals.testCases.passed ) + << pluralise( totals.testCases.passed, "test case" ) << + " with " << pluralise( totals.assertions.passed, "assertion" ) << '.'; + } + } + }; + + INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter ) + +} // end namespace Catch + +namespace Catch { + // These are all here to avoid warnings about not having any out of line + // virtual methods + NonCopyable::~NonCopyable() {} + IShared::~IShared() {} + IStream::~IStream() CATCH_NOEXCEPT {} + FileStream::~FileStream() CATCH_NOEXCEPT {} + CoutStream::~CoutStream() CATCH_NOEXCEPT {} + DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {} + StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {} + IContext::~IContext() {} + IResultCapture::~IResultCapture() {} + ITestCase::~ITestCase() {} + ITestCaseRegistry::~ITestCaseRegistry() {} + IRegistryHub::~IRegistryHub() {} + IMutableRegistryHub::~IMutableRegistryHub() {} + IExceptionTranslator::~IExceptionTranslator() {} + IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {} + IReporter::~IReporter() {} + IReporterFactory::~IReporterFactory() {} + IReporterRegistry::~IReporterRegistry() {} + IStreamingReporter::~IStreamingReporter() {} + AssertionStats::~AssertionStats() {} + SectionStats::~SectionStats() {} + TestCaseStats::~TestCaseStats() {} + TestGroupStats::~TestGroupStats() {} + TestRunStats::~TestRunStats() {} + CumulativeReporterBase::SectionNode::~SectionNode() {} + CumulativeReporterBase::~CumulativeReporterBase() {} + + StreamingReporterBase::~StreamingReporterBase() {} + ConsoleReporter::~ConsoleReporter() {} + CompactReporter::~CompactReporter() {} + IRunner::~IRunner() {} + IMutableContext::~IMutableContext() {} + IConfig::~IConfig() {} + XmlReporter::~XmlReporter() {} + JunitReporter::~JunitReporter() {} + TestRegistry::~TestRegistry() {} + FreeFunctionTestCase::~FreeFunctionTestCase() {} + IGeneratorInfo::~IGeneratorInfo() {} + IGeneratorsForTest::~IGeneratorsForTest() {} + WildcardPattern::~WildcardPattern() {} + TestSpec::Pattern::~Pattern() {} + TestSpec::NamePattern::~NamePattern() {} + TestSpec::TagPattern::~TagPattern() {} + TestSpec::ExcludedPattern::~ExcludedPattern() {} + + Matchers::Impl::StdString::Equals::~Equals() {} + Matchers::Impl::StdString::Contains::~Contains() {} + Matchers::Impl::StdString::StartsWith::~StartsWith() {} + Matchers::Impl::StdString::EndsWith::~EndsWith() {} + + void Config::dummy() {} + + namespace TestCaseTracking { + ITracker::~ITracker() {} + TrackerBase::~TrackerBase() {} + SectionTracker::~SectionTracker() {} + IndexTracker::~IndexTracker() {} + } +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif + +#ifdef CATCH_CONFIG_MAIN +// #included from: internal/catch_default_main.hpp +#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED + +#ifndef __OBJC__ + +// Standard C/C++ main entry point +int main (int argc, char * argv[]) { + int result = Catch::Session().run( argc, argv ); + return ( result < 0xff ? result : 0xff ); +} + +#else // __OBJC__ + +// Objective-C entry point +int main (int argc, char * const argv[]) { +#if !CATCH_ARC_ENABLED + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; +#endif + + Catch::registerTestMethods(); + int result = Catch::Session().run( argc, (char* const*)argv ); + +#if !CATCH_ARC_ENABLED + [pool drain]; +#endif + + return ( result < 0xff ? result : 0xff ); +} + +#endif // __OBJC__ + +#endif + +#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED +# undef CLARA_CONFIG_MAIN +#endif + +////// + +// If this config identifier is defined then all CATCH macros are prefixed with CATCH_ +#ifdef CATCH_CONFIG_PREFIX_ALL + +#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE" ) +#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "CATCH_REQUIRE_FALSE" ) + +#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "CATCH_REQUIRE_THROWS" ) +#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THROWS_AS" ) +#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "CATCH_REQUIRE_THROWS_WITH" ) +#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_NOTHROW" ) + +#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK" ) +#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CATCH_CHECK_FALSE" ) +#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_IF" ) +#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECKED_ELSE" ) +#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CATCH_CHECK_NOFAIL" ) + +#define CATCH_CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CATCH_CHECK_THROWS" ) +#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THROWS_AS" ) +#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CATCH_CHECK_THROWS_WITH" ) +#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_NOTHROW" ) + +#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CATCH_CHECK_THAT" ) +#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "CATCH_REQUIRE_THAT" ) + +#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "CATCH_WARN", msg ) +#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "CATCH_INFO" ) +#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) +#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CATCH_CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", __VA_ARGS__ ) + #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", __VA_ARGS__ ) +#else + #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description ) + #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "CATCH_FAIL", msg ) + #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "CATCH_SUCCEED", msg ) +#endif +#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags ) +#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define CATCH_GIVEN( desc ) CATCH_SECTION( std::string( "Given: ") + desc, "" ) +#define CATCH_WHEN( desc ) CATCH_SECTION( std::string( " When: ") + desc, "" ) +#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) +#define CATCH_THEN( desc ) CATCH_SECTION( std::string( " Then: ") + desc, "" ) +#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( " And: ") + desc, "" ) + +// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required +#else + +#define REQUIRE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal, "REQUIRE" ) +#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, "REQUIRE_FALSE" ) + +#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, "", "REQUIRE_THROWS" ) +#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::Normal, "REQUIRE_THROWS_AS" ) +#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::Normal, matcher, "REQUIRE_THROWS_WITH" ) +#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::Normal, "REQUIRE_NOTHROW" ) + +#define CHECK( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK" ) +#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, "CHECK_FALSE" ) +#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_IF" ) +#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECKED_ELSE" ) +#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( expr, Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, "CHECK_NOFAIL" ) + +#define CHECK_THROWS( expr ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, "", "CHECK_THROWS" ) +#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( expr, exceptionType, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THROWS_AS" ) +#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( expr, Catch::ResultDisposition::ContinueOnFailure, matcher, "CHECK_THROWS_WITH" ) +#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( expr, Catch::ResultDisposition::ContinueOnFailure, "CHECK_NOTHROW" ) + +#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::ContinueOnFailure, "CHECK_THAT" ) +#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( arg, matcher, Catch::ResultDisposition::Normal, "REQUIRE_THAT" ) + +#define INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define WARN( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, "WARN", msg ) +#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( msg, "INFO" ) +#define CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) +#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( #msg " := " << msg, "CAPTURE" ) + +#ifdef CATCH_CONFIG_VARIADIC_MACROS + #define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ ) + #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ ) + #define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ ) + #define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ ) + #define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ ) + #define FAIL( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", __VA_ARGS__ ) + #define SUCCEED( ... ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", __VA_ARGS__ ) +#else + #define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description ) + #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description ) + #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description ) + #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description ) + #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description ) + #define FAIL( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, "FAIL", msg ) + #define SUCCEED( msg ) INTERNAL_CATCH_MSG( Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, "SUCCEED", msg ) +#endif +#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" ) + +#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) +#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) + +#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr ) + +#endif + +#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) + +// "BDD-style" convenience wrappers +#ifdef CATCH_CONFIG_VARIADIC_MACROS +#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ ) +#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ ) +#else +#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags ) +#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags ) +#endif +#define GIVEN( desc ) SECTION( std::string(" Given: ") + desc, "" ) +#define WHEN( desc ) SECTION( std::string(" When: ") + desc, "" ) +#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" ) +#define THEN( desc ) SECTION( std::string(" Then: ") + desc, "" ) +#define AND_THEN( desc ) SECTION( std::string(" And: ") + desc, "" ) + +using Catch::Detail::Approx; + +#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED + diff --git a/components/ospcommon/utility/Any.h b/components/ospcommon/utility/Any.h new file mode 100644 index 0000000000..27bc6309fb --- /dev/null +++ b/components/ospcommon/utility/Any.h @@ -0,0 +1,269 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include + +#include "../common.h" +#include "../TypeTraits.h" + +namespace ospcommon { + namespace utility { + + /* 'Any' implements a single item container which erases its type (can hold + * any value which is copyable). The value can be extracted successfuly + * only if the correct type is queried for the held value, where an + * exception is thrown otherwise. Similar (but perhaps not identical to) + * 'boost::any' or C++17's 'std::any'. + * + * Example: + * + * Any myAny = 1; // myAny is an 'int' w/ value of '1' + * int value = myAny.get(); // get value of '1' out of myAny + * char bad = myAny.get(); // throws exception + */ + struct Any + { + Any() = default; + Any(const Any ©); + + template + Any(T value); + + ~Any() = default; + + Any& operator=(const Any &rhs); + + template + Any& operator=(T rhs); + + template + T& get(); + + template + const T& get() const; + + template + bool is() const; + + bool valid() const; + + std::string toString() const; + + private: + + // Friends // + + friend bool operator==(const Any &lhs, const Any &rhs); + + // Helper types // + + struct handle_base + { + virtual ~handle_base() = default; + virtual handle_base* clone() const = 0; + virtual const std::type_info& valueTypeID() const = 0; + virtual bool isSame(handle_base *other) const = 0; + virtual void *data() = 0; + }; + + template + struct handle : public handle_base + { + handle(T value); + handle_base* clone() const override; + const std::type_info& valueTypeID() const override; + bool isSame(handle_base *other) const override; + void *data() override; + T value; + + // NOTE(jda) - Use custom type trait to select a real implementation of + // isSame(), or one that always returns 'false' if the + // template type 'T' does not implement operator==() with + // itself. + template + inline traits::HasOperatorEquals//<-- substitues to 'bool' + isSameImpl(handle_base *other) const; + + template + inline traits::NoOperatorEquals//<-- substitutes to 'bool' + isSameImpl(handle_base *other) const; + }; + + // Data members // + + std::unique_ptr currentValue; + }; + + // Inlined Any definitions //////////////////////////////////////////////// + + template + inline Any::Any(T value) : + currentValue(new handle::type>( + std::forward(value) + )) + { + static_assert(std::is_copy_constructible::value + && std::is_copy_assignable::value, + "Any can only be constructed with copyable values!"); + } + + inline Any::Any(const Any ©) : + currentValue(copy.valid() ? copy.currentValue->clone() : nullptr) + { + } + + inline Any &Any::operator=(const Any &rhs) + { + Any temp(rhs); + currentValue = std::move(temp.currentValue); + return *this; + } + + template + inline Any &Any::operator=(T rhs) + { + static_assert(std::is_copy_constructible::value + && std::is_copy_assignable::value, + "Any can only be assigned values which are copyable!"); + + currentValue = std::unique_ptr( + new handle::type>( + std::forward(rhs) + ) + ); + + return *this; + } + + template + inline T &Any::get() + { + if (!valid()) + throw std::runtime_error("Can't query value from an empty Any!"); + + if (is()) + return *(static_cast(currentValue->data())); + else { + std::stringstream msg; + msg << "Incorrect type queried for Any!" << '\n'; + msg << " queried type == " << typeid(T).name() << '\n'; + msg << " current type == " << currentValue->valueTypeID().name() + << '\n'; + throw std::runtime_error(msg.str()); + } + } + + template + inline const T &Any::get() const + { + if (!valid()) + throw std::runtime_error("Can't query value from an empty Any!"); + + if (is()) + return *(static_cast(currentValue->data())); + else { + std::stringstream msg; + msg << "Incorrect type queried for Any!" << '\n'; + msg << " queried type == " << typeid(T).name() << '\n'; + msg << " current type == " << currentValue->valueTypeID().name() + << '\n'; + throw std::runtime_error(msg.str()); + } + } + + template + inline bool Any::is() const + { + return valid() && + (typeid(T).hash_code() == currentValue->valueTypeID().hash_code()); + } + + inline bool Any::valid() const + { + return currentValue.get() != nullptr; + } + + inline std::string Any::toString() const + { + std::stringstream retval; + retval << "Any : (currently holds value of type) --> " + << currentValue->valueTypeID().name(); + return retval.str(); + } + + template + inline Any::handle::handle(T v) : value(std::move(v)) + { + } + + template + inline Any::handle_base *Any::handle::clone() const + { + return new handle(value); + } + + template + inline const std::type_info &Any::handle::valueTypeID() const + { + return typeid(T); + } + + template + inline bool Any::handle::isSame(Any::handle_base *other) const + { + return isSameImpl(other); + } + + template + template + inline traits::HasOperatorEquals + Any::handle::isSameImpl(Any::handle_base *other) const + { + handle* otherHandle = dynamic_cast*>(other); + return (otherHandle != nullptr) && (otherHandle->value == this->value); + } + + template + template + inline traits::NoOperatorEquals + Any::handle::isSameImpl(Any::handle_base *other) const + { + return false; + } + + template + inline void *Any::handle::data() + { + return &value; + } + + // Comparison functions /////////////////////////////////////////////////// + + inline bool operator==(const Any &lhs, const Any &rhs) + { + return lhs.currentValue->isSame(rhs.currentValue.get()); + } + + inline bool operator!=(const Any &lhs, const Any &rhs) + { + return !(lhs == rhs); + } + + } // ::ospcommon::utility +} // ::ospcommon diff --git a/components/ospcommon/utility/CodeTimer.h b/components/ospcommon/utility/CodeTimer.h new file mode 100644 index 0000000000..20c3eb356c --- /dev/null +++ b/components/ospcommon/utility/CodeTimer.h @@ -0,0 +1,91 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "../common.h" + +namespace ospcommon { + namespace utility { + + /*! Helper class that assists with timing a region of code. */ + struct CodeTimer + { + void start(); + void stop(); + + double seconds() const; + double milliseconds() const; + double perSecond() const; + + double secondsSmoothed() const; + double millisecondsSmoothed() const; + double perSecondSmoothed() const; + + private: + + double lastSeconds {0.0}; + double smooth_nom {0.0}; + double smooth_den {0.0}; + double frameStartTime {0.0}; + }; + + // Inlined CodeTimer definitions ////////////////////////////////////////// + + inline void CodeTimer::start() + { + frameStartTime = ospcommon::getSysTime(); + } + + inline void CodeTimer::stop() + { + lastSeconds = ospcommon::getSysTime() - frameStartTime; + smooth_nom = smooth_nom * 0.8f + lastSeconds; + smooth_den = smooth_den * 0.8f + 1.f; + } + + inline double CodeTimer::seconds() const + { + return lastSeconds; + } + + inline double CodeTimer::milliseconds() const + { + return seconds() * 1000.0; + } + + inline double CodeTimer::perSecond() const + { + return 1.0 / seconds(); + } + + inline double CodeTimer::secondsSmoothed() const + { + return 1.0 / perSecondSmoothed(); + } + + inline double CodeTimer::millisecondsSmoothed() const + { + return secondsSmoothed() * 1000.0; + } + + inline double CodeTimer::perSecondSmoothed() const + { + return smooth_den / smooth_nom; + } + + } // ::ospcommon::utility +} // ::ospcommon diff --git a/apps/qtViewer/FPSCounter.cpp b/components/ospcommon/utility/OnScopeExit.h similarity index 60% rename from apps/qtViewer/FPSCounter.cpp rename to components/ospcommon/utility/OnScopeExit.h index 6a7c6fd478..2d8790df1d 100644 --- a/apps/qtViewer/FPSCounter.cpp +++ b/components/ospcommon/utility/OnScopeExit.h @@ -14,36 +14,42 @@ // limitations under the License. // // ======================================================================== // -#include "FPSCounter.h" +#pragma once -namespace ospray { - namespace viewer { +#include "../TypeTraits.h" +#include - FPSCounter::FPSCounter() +namespace ospcommon { + namespace utility { + + /* Execute a given function when a scope exits */ + struct OnScopeExit { - smooth_nom = 0.; - smooth_den = 0.; - frameStartTime = 0.; - } - - void FPSCounter::startRender() - { - frameStartTime = ospcommon::getSysTime(); - } + template + OnScopeExit(FCN_T&& _fcn); - void FPSCounter::doneRender() - { - double seconds = ospcommon::getSysTime() - frameStartTime; - fps = 1./seconds; - smooth_nom = smooth_nom * 0.8 + seconds; - smooth_den = smooth_den * 0.8 + 1.; - } + ~OnScopeExit(); + + private: + + std::function fcn; + }; - double FPSCounter::getSmoothFPS() const - { return smooth_den / smooth_nom; } + // Inlined OnScopeExit definitions //////////////////////////////////////// - double FPSCounter::getFPS() const - { return fps; } + template + inline OnScopeExit::OnScopeExit(FCN_T&& _fcn) + { + static_assert(traits::has_operator_method::value, + "FCN_T must implement operator() with no arguments!"); + + fcn = std::forward(_fcn); + } + + inline OnScopeExit::~OnScopeExit() + { + fcn(); + } - } // ::ospray::viewer -} // ::ospray + } // ::ospcommon::utility +} // ::ospcommon diff --git a/components/ospcommon/utility/PseudoURL.cpp b/components/ospcommon/utility/PseudoURL.cpp new file mode 100644 index 0000000000..c5b6e10089 --- /dev/null +++ b/components/ospcommon/utility/PseudoURL.cpp @@ -0,0 +1,130 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +/*! \file PseudoURL: splits a 'pseudo-url' of the form + '://[:name=value]*' into its components of 'type' + (e.g, 'points', 'lines', etc), filename, and 'name=value' + argument pairs (e.g., 'format=xyzrgb') */ + +#include "PseudoURL.h" + +namespace ospcommon { + namespace utility { + + void tokenize(const std::string &str, const char delim, + std::vector &tokens) + { + size_t prev = 0; + size_t fnd = str.find(delim); + for (; fnd != std::string::npos; prev = fnd + 1, fnd = str.find(delim, prev)) { + // Discard repeated tokens in the string, e.g. tokeninzing a::c::b on ':' should + // just return a, c, b + if (fnd - prev > 1) { + tokens.push_back(str.substr(prev, fnd - prev)); + } + } + // Grab the last token in the string, if the string didn't terminate with a delimeter + if (str.size() - prev > 1) { + tokens.push_back(str.substr(prev)); + } + } + + /*! constructor - parse the given string into its components */ + PseudoURL::PseudoURL(const std::string &inputString) + { + std::string tmp = inputString; + const size_t separator = tmp.find("://"); + if (separator != std::string::npos) { + // separator specified: cut off 'type' before that separator, + // and reset 'tmp' to everything behind it + type = tmp.substr(0, separator); + tmp = tmp.substr(separator + 3); + } else { + // no separator -> empty type specifier string, tmp returns + // un-modified + type = ""; + } + + /* now, split remainder into its colon-separated components (the + first of those is the filename, all other ones are params */ + std::vector colonSeparatedComponents; + tokenize(tmp, ':', colonSeparatedComponents); + + if (colonSeparatedComponents.empty()) + // degenerate case of "type://" - return empty filename and empty params + return; + + fileName = colonSeparatedComponents[0]; + for (size_t arg_it = 1; arg_it < colonSeparatedComponents.size(); arg_it++) { + std::string arg = colonSeparatedComponents[arg_it]; + const size_t equalSign = arg.find('='); + if (equalSign != std::string::npos) { + params.push_back(std::make_pair(arg.substr(0, equalSign), + arg.substr(equalSign+1))); + } else { + params.push_back(std::make_pair(arg, std::string(""))); + } + } + } + + /*! return the parsed type. may we empty string if none was specified */ + std::string PseudoURL::getType() const + { + return type; + } + + /*! return the parsed file name specifier. cannot be empty + string */ + std::string PseudoURL::getFileName() const + { + return fileName; + } + + /*! return value for given parameters name, or throw an exception + if not specified */ + std::string PseudoURL::getValue(const std::string &name) const + { + /* note(iw) - we do _not_ do a immediate 'return' upon the first + param with mathcin gname we find so as to ensure that we use + the _last_ time any parameter was written. it's more intuitive + to have the last value override earlier ones, but i didn't want + the parser (ie, constructor) to mess with the input data (maybe + in some cases a class using this _wants_ to have multiple + instances of the same parameter!?), so let's fix that here */ + int found = -1; + for (size_t i = 0; i < params.size(); i++) { + if (params[i].first == name) + found = i; + } + + if (found < 0) { + throw std::runtime_error("PseudoURL::getValue queried value of " + "not-specified parameter"); + } + + return params[found].second; + } + + /*! check if the given parameter was specified */ + bool PseudoURL::hasParam(const std::string &name) + { + for (auto param : params) + if (param.first == name) return true; + return false; + } + + } // ::ospcommon::utility +} // ::ospcommon diff --git a/components/ospcommon/utility/PseudoURL.h b/components/ospcommon/utility/PseudoURL.h new file mode 100644 index 0000000000..dee479d9d2 --- /dev/null +++ b/components/ospcommon/utility/PseudoURL.h @@ -0,0 +1,72 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +/*! \file PseudoURL: splits a 'pseudo-url' of the form + '://[:name=value]*' into its components of 'type' + (e.g, 'points', 'slines', etc), filename, and 'name=value' + argument pairs (e.g., 'format=xyzrgb') */ + +#include "../common.h" +#include +#include + +namespace ospcommon { + namespace utility { + + //! \brief Tokenize the string passed on the desired delimeter + void tokenize(const std::string &str, const char delim, + std::vector &tokens); + + /* a pseudo-url is of the form '://[:name=value]*' + into its components of 'type' (e.g, 'points', 'lines', etc), + filename, and 'name=value' argument pairs (e.g., + 'format=xyzrgb'). This class takes a string and splits it into these + components */ + struct PseudoURL { + + /*! constructor - parse the given string into its components */ + PseudoURL(const std::string &inputString); + + /*! return the parsed type. may we empty string if none was specified */ + std::string getType() const; + + /*! return the parsed file name specifier. cannot be empty + string */ + std::string getFileName() const; + + /*! return value for given parameters name, or throw an exception + if not specified */ + std::string getValue(const std::string &name) const; + + /*! check if the given parameter was specified */ + bool hasParam(const std::string &name); + + private: + /*! the type of the psueod-url, eg, for 'points://file.raw' this + would be 'points'. If no "://" is specified, this gets set to "" */ + std::string type; + /*! the filename - the thing after the ://, and before the + ":" that starts parameters */ + std::string fileName; + + /*! the name-value pairs specified as parameters */ + std::vector> params; + }; + + } // ::ospcommon::utility +} // ::ospcommon diff --git a/components/ospcommon/utility/TransactionalValue.h b/components/ospcommon/utility/TransactionalValue.h new file mode 100644 index 0000000000..55f828302e --- /dev/null +++ b/components/ospcommon/utility/TransactionalValue.h @@ -0,0 +1,117 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include + +namespace ospcommon { + namespace utility { + + /* This implements a 1-to-1 value fence. One thread can set (or "queue") a + * value for another thread to later get. This is conceptually similar to + * "doublebuffering" a single value. Note that all values from the producer + * thread overwrite the "queued" value, where the consumer thread will + * always get the last value set by the produer thread. + */ + template + class TransactionalValue + { + public: + + TransactionalValue() = default; + ~TransactionalValue() = default; + + template + TransactionalValue(const OtherType &ot); + + template + TransactionalValue& operator=(const OtherType &ot); + + TransactionalValue& operator=(const TransactionalValue& fp); + + T &ref(); + T get(); + + bool update(); + + private: + + bool newValue {false}; + T queuedValue; + T currentValue; + + std::mutex mutex; + }; + + // Inlined TransactionalValue Members ///////////////////////////////////// + + template + template + inline TransactionalValue::TransactionalValue(const OtherType &ot) + { + currentValue = ot; + } + + template + template + inline TransactionalValue & + TransactionalValue::operator=(const OtherType &ot) + { + std::lock_guard lock{mutex}; + queuedValue = ot; + newValue = true; + return *this; + } + + template + inline TransactionalValue & + TransactionalValue::operator=(const TransactionalValue &fp) + { + std::lock_guard lock{mutex}; + queuedValue = fp.ref(); + newValue = true; + return *this; + } + + template + inline T &TransactionalValue::ref() + { + return currentValue; + } + + template + inline T TransactionalValue::get() + { + return currentValue; + } + + template + inline bool TransactionalValue::update() + { + bool didUpdate = false; + if (newValue) { + std::lock_guard lock{mutex}; + currentValue = std::move(queuedValue); + newValue = false; + didUpdate = true; + } + + return didUpdate; + } + + } // ::ospcommon::utility +} // ::ospcommon diff --git a/components/ospcommon/utility/test_Any.cpp b/components/ospcommon/utility/test_Any.cpp new file mode 100644 index 0000000000..8df73343d9 --- /dev/null +++ b/components/ospcommon/utility/test_Any.cpp @@ -0,0 +1,127 @@ +#define CATCH_CONFIG_MAIN +#include "../testing/catch.hpp" + +#include "../box.h" +#include "../vec.h" +#include "Any.h" + +using ospcommon::utility::Any; + +// Helper types /////////////////////////////////////////////////////////////// + +struct OSPObject_T {}; +using OSPObject = OSPObject_T*; + +// Helper functions /////////////////////////////////////////////////////////// + +template +inline void verify_value(const Any &v, const T &correctValue) +{ + REQUIRE(v.valid()); + REQUIRE(v.is()); + REQUIRE(v.get() == correctValue); +} + +template +inline void test_interface(T testValue, T testValue2) +{ + Any v; + REQUIRE(!v.valid()); + + SECTION("Can make valid by construction") + { + Any v2(testValue); + verify_value(v2, testValue); + } + + SECTION("Can make valid by calling operator=()") + { + v = testValue; + verify_value(v, testValue); + } + + SECTION("Can make valid by copy construction") + { + v = testValue; + Any v2(v); + verify_value(v2, testValue); + } + + SECTION("Two objects with same value are equal if constructed the same") + { + v = testValue; + Any v2 = testValue; + REQUIRE(v == v2); + } + + SECTION("Two objects with same value are equal if assigned from another") + { + v = testValue; + Any v2 = testValue2; + v = v2; + REQUIRE(v == v2); + } + + SECTION("Two objects with different values are not equal") + { + v = testValue; + Any v2 = testValue2; + REQUIRE(v != v2); + } +} + +// Tests ////////////////////////////////////////////////////////////////////// + +TEST_CASE("Any 'int' type behavior", "[types]") +{ + test_interface(5, 7); +} + +TEST_CASE("Any 'float' type behavior", "[types]") +{ + test_interface(1.f, 2.f); +} + +TEST_CASE("Any 'bool' type behavior", "[types]") +{ + test_interface(true, false); +} + +TEST_CASE("Any 'vec3f' type behavior", "[types]") +{ + test_interface({1.f, 1.f, 1.f}, {2.f, 3.f, 4.f}); +} + +TEST_CASE("Any 'vec2f' type behavior", "[types]") +{ + test_interface({1.f, 1.f}, {3.f, 4.f}); +} + +TEST_CASE("Any 'vec2i' type behavior", "[types]") +{ + test_interface({1, 1}, {3, 4}); +} + +TEST_CASE("Any 'box3f' type behavior", "[types]") +{ + test_interface({{1.f, 1.f, 1.f}, {2.f, 2.f, 2.f}}, + {{3.f, 4.f, 5.f}, {6.f, 7.f, 8.f}}); +} + +TEST_CASE("Any 'string' type behavior", "[types]") +{ + test_interface("Hello", "World"); +} + +TEST_CASE("Any 'OSPObject' type behavior", "[types]") +{ + // NOTE(jda) - we just need some phony pointer addresses to test Any, + // no need to hand it "real" OSPRay objects... + void *val1 = nullptr; + void *val2 = nullptr; + val1 = &val1; + val2 = &val2; + + test_interface(static_cast(val1), + static_cast(val2)); +} diff --git a/components/ospcommon/utility/test_OnScopeExit.cpp b/components/ospcommon/utility/test_OnScopeExit.cpp new file mode 100644 index 0000000000..c1a78f853c --- /dev/null +++ b/components/ospcommon/utility/test_OnScopeExit.cpp @@ -0,0 +1,19 @@ +#define CATCH_CONFIG_MAIN +#include "../testing/catch.hpp" + +#include "OnScopeExit.h" + +using ospcommon::utility::OnScopeExit; + +TEST_CASE("OnScopeExit correctness", "[]") +{ + int testValue = 0; + + { + OnScopeExit guard([&](){ testValue++; }); + + REQUIRE(testValue == 0);// hasn't executed yet + } + + REQUIRE(testValue == 1);// executed exactly once? +} diff --git a/modules/mpi/CMakeLists.txt b/modules/mpi/CMakeLists.txt index 49bb673b57..c953967595 100644 --- a/modules/mpi/CMakeLists.txt +++ b/modules/mpi/CMakeLists.txt @@ -20,36 +20,32 @@ IF (OSPRAY_MODULE_MPI) SET(OSPRAY_DEFAULT_COMPONENT mpi) - OSPRAY_BUILD_COMPONENT(mpiCommon) - - OPTION(OSPRAY_PIN_ASYNC "Pin async mpi comm threads?" OFF) - MARK_AS_ADVANCED(OSPRAY_PIN_ASYNC) - - # if mpi mode is enabled, we have to configure the right mpi - # compiler etc. OSPRAY_CONFIGURE_MPI() - CONFIGURE_FILE(OSPMPIConfig.h.in ${CMAKE_BINARY_DIR}/OSPMPIConfig.h) - INSTALL(FILES ${CMAKE_BINARY_DIR}/OSPMPIConfig.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ospray - COMPONENT devel - ) + OSPRAY_BUILD_COMPONENT(mpiCommon) + OSPRAY_BUILD_COMPONENT(mpiMessageLayer) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/..) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) INCLUDE_DIRECTORIES_ISPC(${CMAKE_CURRENT_BINARY_DIR}) OSPRAY_CREATE_LIBRARY(ospray_module_mpi - MPIDevice.cpp - worker.cpp + MPIDistributedDevice.cpp + MPIOffloadDevice.cpp + MPIOffloadWorker.cpp common/OSPWork.cpp + common/Messaging.cpp + common/DistributedModel.cpp + common/DistributedModel.ispc fb/DistributedFrameBuffer.cpp fb/DistributedFrameBuffer.ispc fb/DistributedFrameBuffer_TileTypes.cpp render/MPILoadBalancer.cpp + render/distributed/DistributedRaycast.cpp + render/distributed/DistributedRaycast.ispc render/volume/RaycastVolumeMaterial.cpp render/volume/RaycastVolumeMaterial.ih render/volume/RaycastVolumeMaterial.ispc @@ -64,6 +60,7 @@ IF (OSPRAY_MODULE_MPI) ospray ospray_mpi_common ospray_common + ospray_mpi_maml ${MPI_CXX_LIBRARIES} ) @@ -72,9 +69,15 @@ IF (OSPRAY_MODULE_MPI) ############################################################## OSPRAY_CREATE_APPLICATION(ospray_mpi_worker - MPIWorker.cpp + mpi_offload_worker_main.cpp LINK - ospray_module_mpi + ospray ) + ############################################################## + # Test apps + ############################################################## + + ADD_SUBDIRECTORY(apps) + ENDIF (OSPRAY_MODULE_MPI) diff --git a/modules/mpi/MPIDevice.cpp b/modules/mpi/MPIDevice.cpp deleted file mode 100644 index 047664150f..0000000000 --- a/modules/mpi/MPIDevice.cpp +++ /dev/null @@ -1,983 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -#undef NDEBUG // do all assertions in this file - -#include "mpiCommon/MPICommon.h" -#include "mpiCommon/async/CommLayer.h" -#include "mpi/MPIDevice.h" -#include "common/Model.h" -#include "common/Data.h" -#include "common/Library.h" -#include "common/Util.h" -#include "ospcommon/sysinfo.h" -#include "ospcommon/FileName.h" -#include "geometry/TriangleMesh.h" -#include "render/Renderer.h" -#include "camera/Camera.h" -#include "volume/Volume.h" -#include "mpi/render/MPILoadBalancer.h" -#include "fb/LocalFB.h" -#include "mpi/fb/DistributedFrameBuffer.h" -#include "common/OSPWork.h" -// std -#ifndef _WIN32 -# include // for fork() -# include -#endif - -namespace ospray { - using std::cout; - using std::endl; - - namespace mpi { - - //! this runs an ospray worker process. - /*! it's up to the proper init routine to decide which processes - call this function and which ones don't. This function will not - return. */ - OSPRAY_MPI_INTERFACE void runWorker(); - - /*! in this mode ("ospray on ranks" mode, or "ranks" mode), the - user has launched the app across all ranks using mpirun " "; no new processes need to get launched. - - Based on the 'startworkers' flag, this function can set up ospray in - one of two modes: - - in "workers" mode (startworkes=true) all ranks > 0 become - workers, and will NOT return to the application; rank 0 is the - master that controls those workers but doesn't do any - rendeirng (we may at some point allow the master to join in - working as well, but currently this is not implemented). to - reach that mode we call this function with - 'startworkers=true', which will make sure that, even though - all ranks _called_ mpiinit, only rank 0 will ever return to - the app, while all other ranks will automatically go to - running worker code, and never ever return from this function. - - b) in "distribtued" mode the app itself is distributed, and - will use the ospray distributed api to control ospray in a - data-distributed mode. in this way, we'll call this function - with startWorkers=false, which will let all ranks return from - this function to do further work in the app. - - For this function, we assume: - - - all *all* MPI_COMM_WORLD processes are going into this function - - - this fct is called from ospInit (with ranksBecomeWorkers=true) or - from ospdMpiInit (w/ ranksBecomeWorkers = false) - */ - void createMPI_runOnExistingRanks(int *ac, const char **av, - bool ranksBecomeWorkers) - { - MPI_Status status; - mpi::init(ac,av); - printf("#o: initMPI::OSPonRanks: %i/%i\n",world.rank,world.size); - MPI_Barrier(MPI_COMM_WORLD); - if (world.size <= 1) { - throw std::runtime_error("No MPI workers found.\n#osp:mpi: Fatal Error " - "- OSPRay told to run in MPI mode, but there " - "seems to be no MPI peers!?\n#osp:mpi: (Did " - "you forget an 'mpirun' in front of your " - "application?)"); - } - - if (world.rank == 0) { - // we're the root - MPI_Comm_split(mpi::world.comm,1,mpi::world.rank,&app.comm); - app.makeIntraComm(); - // app.makeIntercomm(); - printf("#w: app process %i/%i (global %i/%i)\n", - app.rank,app.size,world.rank,world.size); - - MPI_Intercomm_create(app.comm, 0, world.comm, 1, 1, &worker.comm); - // worker.makeIntracomm(); - worker.makeInterComm(); - - printf("#m: ping-ponging a test message to every worker...\n"); - for (int i=0;i 1) { - if (app.rank == 1) { - cout << "ospray:WARNING: you're trying to run an mpi-parallel app " - << "with ospray\n" - << "(only the root node is allowed to issue ospray api " - << "calls right now)\n"; - cout << "=======================================================" - << endl; - } - - MPI_Barrier(app.comm); - } - - MPI_Barrier(app.comm); - } - - MPIDevice::MPIDevice() : bufferedComm(std::make_shared()){} - - MPIDevice::~MPIDevice() - { - // NOTE(jda) - Seems that there are no MPI Devices on worker ranks...this - // will likely change for data-distributed API settings? - if (mpi::world.rank == 0) { - std::cout << "shutting down mpi device" << std::endl; - work::CommandFinalize work; - processWork(&work); - } - } - - void MPIDevice::commit() - { - if (initialized)// NOTE (jda) - doesn't make sense to commit again? - return; - - Device::commit(); - - initialized = true; - - int _ac = 2; - const char *_av[] = {"ospray_mpi_worker", "--osp:mpi"}; - - std::string mode = getParamString("mpiMode", "mpi"); - - if (mode == "mpi") { - createMPI_RanksBecomeWorkers(&_ac,_av); - } - else if(mode == "mpi-launch") { - std::string launchCommand = getParamString("launchCommand", ""); - - if (launchCommand.empty()) { - throw std::runtime_error("You must provide the launchCommand " - "parameter in mpi-launch mode!"); - } - - createMPI_LaunchWorkerGroup(&_ac,_av,launchCommand.c_str()); - } - else if (mode == "mpi-listen") { - std::string fileNameToStorePortIn = - getParamString("fileNameToStorePortIn", ""); - - if (fileNameToStorePortIn.empty()) { - throw std::runtime_error("You must provide the fileNameToStorePortIn " - "parameter in mpi-listen mode!"); - } - - createMPI_ListenForWorkers(&_ac,_av,fileNameToStorePortIn.c_str()); - } - else { - throw std::runtime_error("Invalid MPI mode!"); - } - - TiledLoadBalancer::instance = make_unique(); - - if (mpi::world.size != 1) { - if (mpi::world.rank < 0) { - PRINT(mpi::world.rank); - PRINT(mpi::world.size); - throw std::runtime_error("OSPRay MPI startup error. Use \"mpirun " - "-n 1 \" when calling an " - "application that tries to spawn to start " - "the application you were trying to " - "start."); - } - } - } - - OSPFrameBuffer - MPIDevice::frameBufferCreate(const vec2i &size, - const OSPFrameBufferFormat mode, - const uint32 channels) - { - ObjectHandle handle = allocateHandle(); - work::CreateFrameBuffer work(handle, size, mode, channels); - processWork(&work); - return (OSPFrameBuffer)(int64)handle; - } - - - /*! map frame buffer */ - const void *MPIDevice::frameBufferMap(OSPFrameBuffer _fb, - OSPFrameBufferChannel channel) - { - ObjectHandle handle = (const ObjectHandle &)_fb; - FrameBuffer *fb = (FrameBuffer *)handle.lookup(); - - switch (channel) { - case OSP_FB_COLOR: return fb->mapColorBuffer(); - case OSP_FB_DEPTH: return fb->mapDepthBuffer(); - default: return nullptr; - } - } - - /*! unmap previously mapped frame buffer */ - void MPIDevice::frameBufferUnmap(const void *mapped, - OSPFrameBuffer _fb) - { - ObjectHandle handle = (const ObjectHandle &)_fb; - FrameBuffer *fb = (FrameBuffer *)handle.lookup(); - fb->unmap(mapped); - } - - /*! create a new model */ - OSPModel MPIDevice::newModel() - { - ObjectHandle handle = allocateHandle(); - work::NewObject work("", handle); - processWork(&work); - return (OSPModel)(int64)handle; - } - - /*! finalize a newly specified model */ - void MPIDevice::commit(OSPObject _object) - { - Assert(_object); - const ObjectHandle handle = (const ObjectHandle&)_object; - work::CommitObject work(handle); - processWork(&work); - } - - /*! add a new geometry to a model */ - void MPIDevice::addGeometry(OSPModel _model, OSPGeometry _geometry) - { - Assert(_model); - Assert(_geometry); - work::AddObject work(_model, _geometry); - processWork(&work); - } - - /*! add a new volume to a model */ - void MPIDevice::addVolume(OSPModel _model, OSPVolume _volume) - { - Assert(_model); - Assert(_volume); - work::AddObject work(_model, _volume); - processWork(&work); - } - - /*! create a new data buffer */ - OSPData MPIDevice::newData(size_t nitems, OSPDataType format, - void *init, int flags) - { - ObjectHandle handle = allocateHandle(); - // If we're in mastered mode you can't share data with remote nodes - if (currentApiMode == OSPD_MODE_MASTERED) { - flags = flags & ~OSP_DATA_SHARED_BUFFER; - } - work::NewData work(handle, nitems, format, init, flags); - processWork(&work); - return (OSPData)(int64)handle; - } - - /*! assign (named) string parameter to an object */ - void MPIDevice::setVoidPtr(OSPObject _object, const char *bufName, void *v) - { - UNUSED(_object, bufName, v); - throw std::runtime_error("setting a void pointer as parameter to an " - "object is not allowed in MPI mode"); - } - - void MPIDevice::removeParam(OSPObject object, const char *name) - { - Assert(object != nullptr && "invalid object handle"); - Assert(name != nullptr && "invalid identifier for object parameter"); - work::RemoveParam work((ObjectHandle&)object, name); - processWork(&work); - } - - /*! Copy data into the given object. */ - int MPIDevice::setRegion(OSPVolume _volume, const void *source, - const vec3i &index, const vec3i &count) - { - Assert(_volume); - Assert(source); - - char *typeString = nullptr; - getString(_volume, "voxelType", &typeString); - OSPDataType type = typeForString(typeString); - delete[] typeString; - - Assert(type != OSP_UNKNOWN && "unknown volume voxel type"); - // TODO: should we be counting and reporting failures of setRegion - // like before? - work::SetRegion work(_volume, index, count, source, type); - processWork(&work); - return true; - } - - /*! assign (named) string parameter to an object */ - void MPIDevice::setString(OSPObject _object, - const char *bufName, - const char *s) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, s); - processWork(&work); - } - - /*! load module */ - int MPIDevice::loadModule(const char *name) - { - work::LoadModule work(name); - processWork(&work); - // FIXME: actually we should return an error code here... - // TODO: If some fail? Can we assume if the master succeeds in loading - // that all have succeeded in loading? I don't think so. - return 0; - } - - /*! assign (named) float parameter to an object */ - void MPIDevice::setFloat(OSPObject _object, - const char *bufName, - const float f) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, f); - processWork(&work); - } - - /*! assign (named) int parameter to an object */ - void MPIDevice::setInt(OSPObject _object, const char *bufName, const int i) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, i); - processWork(&work); - } - - /*! assign (named) vec2f parameter to an object */ - void MPIDevice::setVec2f(OSPObject _object, - const char *bufName, - const vec2f &v) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, v); - processWork(&work); - } - - /*! assign (named) vec3f parameter to an object */ - void MPIDevice::setVec3f(OSPObject _object, - const char *bufName, - const vec3f &v) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, v); - processWork(&work); - } - - /*! assign (named) vec4f parameter to an object */ - void MPIDevice::setVec4f(OSPObject _object, - const char *bufName, - const vec4f &v) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, v); - processWork(&work); - } - - /*! assign (named) vec2i parameter to an object */ - void MPIDevice::setVec2i(OSPObject _object, - const char *bufName, - const vec2i &v) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, v); - processWork(&work); - } - - /*! assign (named) vec3i parameter to an object */ - void MPIDevice::setVec3i(OSPObject _object, - const char *bufName, - const vec3i &v) - { - Assert(_object); - Assert(bufName); - work::SetParam work((ObjectHandle&)_object, bufName, v); - processWork(&work); - } - - /*! assign (named) data item as a parameter to an object */ - void MPIDevice::setObject(OSPObject _target, - const char *bufName, - OSPObject _value) - { - Assert(_target != nullptr); - Assert(bufName != nullptr); - work::SetParam work((ObjectHandle&)_target, bufName, _value); - processWork(&work); - } - - /*! create a new pixelOp object (out of list of registered pixelOps) */ - OSPPixelOp MPIDevice::newPixelOp(const char *type) - { - Assert(type != nullptr); - - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPPixelOp)(int64)handle; - } - - /*! set a frame buffer's pixel op object */ - void MPIDevice::setPixelOp(OSPFrameBuffer _fb, OSPPixelOp _op) - { - Assert(_fb != nullptr); - Assert(_op != nullptr); - work::SetPixelOp work(_fb, _op); - processWork(&work); - } - - /*! create a new renderer object (out of list of registered renderers) */ - OSPRenderer MPIDevice::newRenderer(const char *type) - { - Assert(type != nullptr); - - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPRenderer)(int64)handle; - } - - /*! create a new camera object (out of list of registered cameras) */ - OSPCamera MPIDevice::newCamera(const char *type) - { - Assert(type != nullptr); - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPCamera)(int64)handle; - } - - /*! create a new volume object (out of list of registered volumes) */ - OSPVolume MPIDevice::newVolume(const char *type) - { - Assert(type != nullptr); - - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPVolume)(int64)handle; - } - - /*! create a new geometry object (out of list of registered geometries) */ - OSPGeometry MPIDevice::newGeometry(const char *type) - { - Assert(type != nullptr); - - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPGeometry)(int64)handle; - } - - /*! have given renderer create a new material */ - OSPMaterial MPIDevice::newMaterial(OSPRenderer _renderer, const char *type) - { - if (type == nullptr) - throw std::runtime_error("#osp:mpi:newMaterial: NULL material type"); - - if (_renderer == nullptr) - throw std::runtime_error("#osp:mpi:newMaterial: NULL renderer handle"); - - ObjectHandle handle = allocateHandle(); - work::NewRendererObject work(type, _renderer, handle); - processWork(&work); - // TODO: Should we be tracking number of failures? Shouldn't they - // all fail or not fail? - return (OSPMaterial)(int64)handle; - } - - /*! create a new transfer function object (out of list of - registered transfer function types) */ - OSPTransferFunction MPIDevice::newTransferFunction(const char *type) - { - Assert(type != nullptr); - - ObjectHandle handle = allocateHandle(); - work::NewObject work(type, handle); - processWork(&work); - return (OSPTransferFunction)(int64)handle; - } - - - /*! have given renderer create a new Light */ - OSPLight MPIDevice::newLight(OSPRenderer _renderer, const char *type) - { - if (type == nullptr) - throw std::runtime_error("#osp:mpi:newLight: NULL light type"); - - ObjectHandle handle = allocateHandle(); - work::NewRendererObject work(type, _renderer, handle); - processWork(&work); - // TODO: Should we be tracking number of failures? Shouldn't they - // all fail or not fail? - return (OSPLight)(int64)handle; - } - - /*! clear the specified channel(s) of the frame buffer specified in - 'whichChannels' - - if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to - '0,0,0,0'. - - if whichChannel&OSP_FB_DEPTH!=0, clear the depth buffer to - +inf. - - if whichChannel&OSP_FB_ACCUM!=0, clear the accum buffer to 0,0,0,0, - and reset accumID. - */ - void MPIDevice::frameBufferClear(OSPFrameBuffer _fb, - const uint32 fbChannelFlags) - { - work::ClearFrameBuffer work(_fb, fbChannelFlags); - processWork(&work); - } - - /*! remove an existing geometry from a model */ - void MPIDevice::removeGeometry(OSPModel _model, OSPGeometry _geometry) - { - work::RemoveObject work(_model, _geometry); - processWork(&work); - } - - /*! remove an existing volume from a model */ - void MPIDevice::removeVolume(OSPModel _model, OSPVolume _volume) - { - work::RemoveObject work(_model, _volume); - processWork(&work); - } - - - /*! call a renderer to render a frame buffer */ - float MPIDevice::renderFrame(OSPFrameBuffer _fb, - OSPRenderer _renderer, - const uint32 fbChannelFlags) - { - // Note: render frame is flushing so the work error result will be set, - // since the master participates in rendering - work::RenderFrame work(_fb, _renderer, fbChannelFlags); - processWork(&work); - return work.varianceResult; - } - - //! release (i.e., reduce refcount of) given object - /*! note that all objects in ospray are refcounted, so one cannot - explicitly "delete" any object. instead, each object is created - with a refcount of 1, and this refcount will be - increased/decreased every time another object refers to this - object resp releases its hold on it; if the refcount is 0 the - object will automatically get deleted. For example, you can - create a new material, assign it to a geometry, and immediately - after this assignation release its refcount; the material will - stay 'alive' as long as the given geometry requires it. */ - void MPIDevice::release(OSPObject _obj) - { - if (!_obj) return; - work::CommandRelease work((const ObjectHandle&)_obj); - processWork(&work); - } - - //! assign given material to given geometry - void MPIDevice::setMaterial(OSPGeometry _geometry, OSPMaterial _material) - { - work::SetParam work((ObjectHandle&)_geometry, _material); - processWork(&work); - } - - /*! create a new Texture2D object */ - OSPTexture2D MPIDevice::newTexture2D(const vec2i &sz, - const OSPTextureFormat type, void *data, const uint32 flags) - { - ObjectHandle handle = allocateHandle(); - work::NewTexture2d work(handle, sz, type, data, flags); - processWork(&work); - return (OSPTexture2D)(int64)handle; - } - - /*! return a string represenging the given API Mode */ - const char *apiModeName(int mode) - { - switch (mode) { - case OSPD_MODE_INDEPENDENT: - return "OSPD_MODE_INDEPENDENT"; - case OSPD_MODE_MASTERED: - return "OSPD_MODE_MASTERED"; - case OSPD_MODE_COLLABORATIVE: - return "OSPD_MODE_COLLABORATIVE"; - default: - PRINT(mode); - NOTIMPLEMENTED; - }; - } - - /*! switch API mode for distriubted API extensions */ - void MPIDevice::apiMode(OSPDApiMode newMode) - { - printf("rank %i asked to go from %s mode to %s mode\n", - mpi::world.rank,apiModeName(currentApiMode),apiModeName(newMode)); - switch (currentApiMode) { - // ================================================================== - // ================================================================== - case OSPD_MODE_INDEPENDENT: { - switch (newMode) { - case OSPD_MODE_COLLABORATIVE: - case OSPD_MODE_INDEPENDENT: - currentApiMode = newMode; - // It's probably worth making this an explicit sync point - // between app/worker ranks. TODO: What comm to barrier on? - // MPI_Barrier(MPI_COMM_WORLD); - mpi::barrier(mpi::world); - break; - case OSPD_MODE_MASTERED: - NOTIMPLEMENTED; - } - } break; - // ================================================================== - // currently in default (mastered) mode where master tells workers what - // to do - // ================================================================== - case OSPD_MODE_MASTERED: { - // first: tell workers to switch to new mode: they're in - // mastered mode and thus waiting for *us* to tell them what - // to do, so let's do it. - switch (newMode) { - case OSPD_MODE_MASTERED: { - // nothing to do, actually, the workers are already in this - // mode, no use sending this request again - printf("rank %i remaining in mastered mode\n",mpi::world.rank); - } break; - case OSPD_MODE_INDEPENDENT: - case OSPD_MODE_COLLABORATIVE: { - printf("rank %i telling clients to switch to %s mode.\n", - mpi::world.rank,apiModeName(newMode)); - // cmd.newCommand(CMD_API_MODE); - // cmd.send((int32)newMode); - // cmd.flush(); - currentApiMode = newMode; - // and just to be sure, do a barrier here -- not acutally needed - // AFAICT. - mpi::barrier(mpi::world); - // MPI_Barrier(MPI_COMM_WORLD); - } break; - default: - NOTIMPLEMENTED; - }; - } break; - // ================================================================== - // ================================================================== - case OSPD_MODE_COLLABORATIVE: { - switch (newMode) { - case OSPD_MODE_COLLABORATIVE: - case OSPD_MODE_INDEPENDENT: - currentApiMode = newMode; - // It's probably worth making this an explicit sync point - // between app/worker ranks. TODO: What comm to barrier on? - mpi::barrier(mpi::world); - // MPI_Barrier(MPI_COMM_WORLD); - break; - case OSPD_MODE_MASTERED: - NOTIMPLEMENTED; - } - } break; - - // ================================================================== - // this mode should not exit - implementation error - // ================================================================== - default: - NOTIMPLEMENTED; - }; - } - - void MPIDevice::sampleVolume(float **results, - OSPVolume volume, - const vec3f *worldCoordinates, - const size_t &count) - { - Assert2(volume, "invalid volume handle"); - Assert2(worldCoordinates, "invalid worldCoordinates"); - - Assert2(0, "not implemented"); - } - - int MPIDevice::getString(OSPObject _object, const char *name, char **value) - { - Assert(_object); - Assert(name); - - ManagedObject *object = ((ObjectHandle&)_object).lookup(); - ManagedObject::Param *param = object->findParam(name); - bool foundParameter = (param != NULL && param->type == OSP_STRING); - if (foundParameter) { - *value = new char[2048]; - strncpy(*value, param->s->c_str(), 2048); - return true; - } - return false; - } - - void MPIDevice::processWork(work::Work* work) - { - if (currentApiMode == OSPD_MODE_MASTERED) { - bufferedComm->send(mpi::Address(&mpi::worker,(int32)mpi::SEND_ALL), work); - // TODO: Maybe instead of this we can have a concept of "flushing" work units - if (work->flushing()) { - bufferedComm->flush(); - } - // Run the master side variant of the work unit - work->runOnMaster(); - } else { - work->run(); - } - } - - ObjectHandle MPIDevice::allocateHandle() const { - ObjectHandle handle = nullHandle; - switch (currentApiMode) { - case OSPD_MODE_MASTERED: - handle = ObjectHandle::alloc(); - break; - default: - throw std::runtime_error("MPIDevice::processWork: Unimplemented mode!"); - } - return handle; - } - - OSP_REGISTER_DEVICE(MPIDevice, mpi_device); - OSP_REGISTER_DEVICE(MPIDevice, mpi); - - } // ::ospray::mpi -} // ::ospray - -extern "C" OSPRAY_DLLEXPORT void ospray_init_module_mpi() -{ - ospray::mpi::work::initWorkMap(); - std::cout << "#mpi: initializing ospray MPI plugin" << std::endl; -} - diff --git a/modules/mpi/MPIDistributedDevice.cpp b/modules/mpi/MPIDistributedDevice.cpp new file mode 100644 index 0000000000..124f1c0b4c --- /dev/null +++ b/modules/mpi/MPIDistributedDevice.cpp @@ -0,0 +1,459 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#undef NDEBUG // do all assertions in this file + +//ospray +#include "ospray/camera/Camera.h" +#include "ospray/common/Data.h" +#include "ospray/lights/Light.h" +#include "ospray/transferFunction/TransferFunction.h" +//mpiCommon +#include "mpiCommon/MPICommon.h" +//ospray_mpi +#include "mpi/MPIDistributedDevice.h" +#include "mpi/fb/DistributedFrameBuffer.h" +#include "mpi/render/MPILoadBalancer.h" + +//distributed objects +#include "render/distributed/DistributedRaycast.h" +#include "common/DistributedModel.h" + +#ifdef OPEN_MPI +# include +//# define _GNU_SOURCE +# include +#endif + +namespace ospray { + namespace mpi { + + // Helper functions /////////////////////////////////////////////////////// + + template + inline API_TYPE createOSPRayObjectWithHandle(const char *type) + { + auto *instance = OSPRAY_TYPE::createInstance(type); + instance->refInc(); + + ObjectHandle handle; + handle.assign(instance); + + return (API_TYPE)(int64)handle; + } + + template + inline OSPRAY_TYPE& objectFromAPIHandle(OSPObject obj) + { + auto &handle = reinterpret_cast(obj); + auto *object = (OSPRAY_TYPE*)handle.lookup(); + + if (!object) + throw std::runtime_error("#dmpi: ObjectHandle doesn't exist!"); + + return *object; + } + + static void embreeErrorFunc(const RTCError code, const char* str) + { + postStatusMsg() << "#osp: embree internal error " << code << " : " << str; + throw std::runtime_error("embree internal error '" +std::string(str)+"'"); + } + + // MPIDistributedDevice definitions /////////////////////////////////////// + + MPIDistributedDevice::~MPIDistributedDevice() + { + try { + MPI_CALL(Finalize()); + } catch (...) { + //TODO: anything to do here? try-catch added to silence a warning... + } + } + + void MPIDistributedDevice::commit() + { + if (!initialized) { + int _ac = 1; + const char *_av[] = {"ospray_mpi_worker"}; + + mpicommon::init(&_ac, _av); + + embreeDevice = rtcNewDevice(generateEmbreeDeviceCfg(*this).c_str()); + + rtcDeviceSetErrorFunction(embreeDevice, embreeErrorFunc); + + RTCError erc = rtcDeviceGetError(embreeDevice); + if (erc != RTC_NO_ERROR) { + // why did the error function not get called !? + postStatusMsg() << "#osp:init: embree internal error number " << erc; + assert(erc == RTC_NO_ERROR); + } + + initialized = true; + } + + Device::commit(); + + masterRank = getParam1i("masterRank", 0); + + std::string mode = getParamString("mode", "distributed"); + + if (mode == "distributed") { + postStatusMsg() << "#dmpi: device commit() setting mode to " << mode; + } else { + throw std::runtime_error("#dmpi: bad device mode ['" + mode + "]"); + } + + TiledLoadBalancer::instance = + make_unique(); + } + + OSPFrameBuffer + MPIDistributedDevice::frameBufferCreate(const vec2i &size, + const OSPFrameBufferFormat mode, + const uint32 channels) + { + const bool hasDepthBuffer = channels & OSP_FB_DEPTH; + const bool hasAccumBuffer = channels & OSP_FB_ACCUM; + const bool hasVarianceBuffer = channels & OSP_FB_VARIANCE; + + ObjectHandle handle; + + auto *instance = new DistributedFrameBuffer(size, handle, mode, + hasDepthBuffer, + hasAccumBuffer, + hasVarianceBuffer, + true); + instance->refInc(); + + handle.assign(instance); + + return (OSPFrameBuffer)(int64)handle; + } + + + const void* + MPIDistributedDevice::frameBufferMap(OSPFrameBuffer _fb, + OSPFrameBufferChannel channel) + { + if (!mpicommon::IamTheMaster()) + throw std::runtime_error("Can only map framebuffer on the master!"); + + auto &fb = objectFromAPIHandle(_fb); + + switch (channel) { + case OSP_FB_COLOR: return fb.mapColorBuffer(); + case OSP_FB_DEPTH: return fb.mapDepthBuffer(); + default: return nullptr; + } + } + + void MPIDistributedDevice::frameBufferUnmap(const void *mapped, + OSPFrameBuffer _fb) + { + auto &fb = objectFromAPIHandle(_fb); + fb.unmap(mapped); + } + + void MPIDistributedDevice::frameBufferClear(OSPFrameBuffer _fb, + const uint32 fbChannelFlags) + { + auto &fb = objectFromAPIHandle(_fb); + fb.clear(fbChannelFlags); + } + + OSPModel MPIDistributedDevice::newModel() + { + auto *instance = new DistributedModel; + instance->refInc(); + + ObjectHandle handle; + handle.assign(instance); + + return (OSPModel)(int64)handle; + } + + void MPIDistributedDevice::commit(OSPObject _object) + { + auto &object = objectFromAPIHandle(_object); + object.commit(); + } + + void MPIDistributedDevice::addGeometry(OSPModel _model, + OSPGeometry _geometry) + { + auto &model = objectFromAPIHandle(_model); + auto &geom = objectFromAPIHandle(_geometry); + + model.geometry.push_back(&geom); + } + + void MPIDistributedDevice::addVolume(OSPModel _model, OSPVolume _volume) + { + auto &model = objectFromAPIHandle(_model); + auto &volume = objectFromAPIHandle(_volume); + + model.volume.push_back(&volume); + } + + OSPData MPIDistributedDevice::newData(size_t nitems, OSPDataType format, + void *init, int flags) + { + ObjectHandle handle; + + auto *instance = new Data(nitems, format, init, flags); + instance->refInc(); + + handle.assign(instance); + + return (OSPData)(int64)handle; + } + + void MPIDistributedDevice::setVoidPtr(OSPObject _object, + const char *bufName, + void *v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::removeParam(OSPObject _object, const char *name) + { + auto &object = objectFromAPIHandle(_object); + object.removeParam(name); + } + + int MPIDistributedDevice::setRegion(OSPVolume _volume, const void *source, + const vec3i &index, const vec3i &count) + { + auto &volume = objectFromAPIHandle(_volume); + return volume.setRegion(source, index, count); + } + + void MPIDistributedDevice::setString(OSPObject _object, + const char *bufName, + const char *s) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(s); + } + + int MPIDistributedDevice::loadModule(const char *name) + { + return loadLocalModule(name); + } + + void MPIDistributedDevice::setFloat(OSPObject _object, + const char *bufName, + const float f) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(f); + } + + void MPIDistributedDevice::setInt(OSPObject _object, + const char *bufName, + const int i) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(i); + } + + void MPIDistributedDevice::setVec2f(OSPObject _object, + const char *bufName, + const vec2f &v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::setVec3f(OSPObject _object, + const char *bufName, + const vec3f &v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::setVec4f(OSPObject _object, + const char *bufName, + const vec4f &v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::setVec2i(OSPObject _object, + const char *bufName, + const vec2i &v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::setVec3i(OSPObject _object, + const char *bufName, + const vec3i &v) + { + auto &object = objectFromAPIHandle(_object); + object.findParam(bufName, true)->set(v); + } + + void MPIDistributedDevice::setObject(OSPObject _object, + const char *bufName, + OSPObject _value) + { + auto &object = objectFromAPIHandle(_object); + auto &value = objectFromAPIHandle(_value); + object.set(bufName, &value); + } + + OSPPixelOp MPIDistributedDevice::newPixelOp(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + void MPIDistributedDevice::setPixelOp(OSPFrameBuffer _fb, OSPPixelOp _op) + { + auto &fb = objectFromAPIHandle(_fb); + auto &op = objectFromAPIHandle(_op); + fb.pixelOp = op.createInstance(&fb, fb.pixelOp.ptr); + } + + OSPRenderer MPIDistributedDevice::newRenderer(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + OSPCamera MPIDistributedDevice::newCamera(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + OSPVolume MPIDistributedDevice::newVolume(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + OSPGeometry MPIDistributedDevice::newGeometry(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + OSPMaterial MPIDistributedDevice::newMaterial(OSPRenderer _renderer, + const char *type) + { + NOT_IMPLEMENTED; + } + + OSPTransferFunction + MPIDistributedDevice::newTransferFunction(const char *type) + { + return createOSPRayObjectWithHandle(type); + } + + OSPLight MPIDistributedDevice::newLight(OSPRenderer _renderer, + const char *type) + { + auto &renderer = objectFromAPIHandle(_renderer); + auto *light = renderer.createLight(type); + + if (light == nullptr) + light = Light::createLight(type); + + if (light) { + ObjectHandle handle; + + handle.assign(light); + + return (OSPLight)(int64)handle; + } else { + return nullptr; + } + } + + void MPIDistributedDevice::removeGeometry(OSPModel _model, + OSPGeometry _geometry) + { + auto &model = objectFromAPIHandle(_model); + auto &geom = objectFromAPIHandle(_geometry); + + //TODO: confirm this works? + model.geometry.erase(std::remove(model.geometry.begin(), + model.geometry.end(), + Ref(&geom))); + } + + void MPIDistributedDevice::removeVolume(OSPModel _model, OSPVolume _volume) + { + auto &model = objectFromAPIHandle(_model); + auto &volume = objectFromAPIHandle(_volume); + + //TODO: confirm this works? + model.volume.erase(std::remove(model.volume.begin(), + model.volume.end(), + Ref(&volume))); + } + + float MPIDistributedDevice::renderFrame(OSPFrameBuffer _fb, + OSPRenderer _renderer, + const uint32 fbChannelFlags) + { + auto &fb = objectFromAPIHandle(_fb); + auto &renderer = objectFromAPIHandle(_renderer); + auto result = renderer.renderFrame(&fb, fbChannelFlags); + mpicommon::world.barrier(); + return result; + } + + void MPIDistributedDevice::release(OSPObject _obj) + { + if (!_obj) return; + auto &obj = objectFromAPIHandle(_obj); + obj.refDec(); + } + + void MPIDistributedDevice::setMaterial(OSPGeometry _geometry, + OSPMaterial _material) + { + NOT_IMPLEMENTED; + } + + OSPTexture2D MPIDistributedDevice::newTexture2D( + const vec2i &sz, + const OSPTextureFormat type, + void *data, + const uint32 flags + ) + { + NOT_IMPLEMENTED; + } + + void MPIDistributedDevice::sampleVolume(float **results, + OSPVolume volume, + const vec3f *worldCoordinates, + const size_t &count) + { + NOT_IMPLEMENTED; + } + + OSP_REGISTER_DEVICE(MPIDistributedDevice, mpi_distributed_device); + OSP_REGISTER_DEVICE(MPIDistributedDevice, mpi_distributed); + + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/MPIDistributedDevice.h b/modules/mpi/MPIDistributedDevice.h new file mode 100644 index 0000000000..7bfcb70aaa --- /dev/null +++ b/modules/mpi/MPIDistributedDevice.h @@ -0,0 +1,214 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +// mpicommon +#include "mpiCommon/MPICommon.h" +// ospray +#include "api/Device.h" +#include "common/Managed.h" +// ospray::mpi + +namespace ospray { + namespace mpi { + + struct MPIDistributedDevice : public api::Device + { + MPIDistributedDevice() = default; + ~MPIDistributedDevice(); + + // ManagedObject Implementation ///////////////////////////////////////// + + void commit() override; + + // Device Implementation //////////////////////////////////////////////// + + /*! create a new frame buffer */ + OSPFrameBuffer + frameBufferCreate(const vec2i &size, + const OSPFrameBufferFormat mode, + const uint32 channels) override; + + /*! create a new transfer function object (out of list of + registered transfer function types) */ + OSPTransferFunction newTransferFunction(const char *type) override; + + /*! have given renderer create a new Light */ + OSPLight newLight(OSPRenderer _renderer, const char *type) override; + + /*! map frame buffer */ + const void *frameBufferMap(OSPFrameBuffer fb, + OSPFrameBufferChannel channel) override; + + /*! unmap previously mapped frame buffer */ + void frameBufferUnmap(const void *mapped, OSPFrameBuffer fb) override; + + + /*! set a frame buffer's pixel op object */ + void setPixelOp(OSPFrameBuffer _fb, OSPPixelOp _op) override; + + /*! create a new pixelOp object (out of list of registered pixelOps) */ + OSPPixelOp newPixelOp(const char *type) override; + + /*! clear the specified channel(s) of the frame buffer specified in + 'whichChannels' + + if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to + '0,0,0,0'. + + if whichChannel&OSP_FB_DEPTH!=0, clear the depth buffer to + +inf. + + if whichChannel&OSP_FB_ACCUM!=0, clear the accum buffer to 0,0,0,0, + and reset accumID. + */ + void frameBufferClear(OSPFrameBuffer _fb, + const uint32 fbChannelFlags) override; + + /*! create a new model */ + OSPModel newModel() override; + + /*! commit the given object's outstanding changes */ + void commit(OSPObject object) override; + + /*! add a new geometry to a model */ + void addGeometry(OSPModel _model, OSPGeometry _geometry) override; + + /*! remove an existing geometry from a model */ + void removeGeometry(OSPModel _model, OSPGeometry _geometry) override; + + /*! add a new volume to a model */ + void addVolume(OSPModel _model, OSPVolume _volume) override; + + /*! remove an existing volume from a model */ + void removeVolume(OSPModel _model, OSPVolume _volume) override; + + /*! create a new data buffer */ + OSPData newData(size_t nitems, OSPDataType format, + void *init, int flags) override; + + /*! Copy data into the given volume. */ + int setRegion(OSPVolume object, const void *source, + const vec3i &index, const vec3i &count) override; + + /*! assign (named) string parameter to an object */ + void setString(OSPObject object, + const char *bufName, + const char *s) override; + + /*! assign (named) data item as a parameter to an object */ + void setObject(OSPObject target, + const char *bufName, + OSPObject value) override; + + /*! assign (named) float parameter to an object */ + void setFloat(OSPObject object, + const char *bufName, + const float f) override; + + /*! assign (named) vec2f parameter to an object */ + void setVec2f(OSPObject object, + const char *bufName, + const vec2f &v) override; + + /*! assign (named) vec3f parameter to an object */ + void setVec3f(OSPObject object, + const char *bufName, + const vec3f &v) override; + + /*! assign (named) vec4f parameter to an object */ + void setVec4f(OSPObject object, + const char *bufName, + const vec4f &v) override; + + /*! assign (named) int parameter to an object */ + void setInt(OSPObject object, const char *bufName, const int f) override; + + /*! assign (named) vec2i parameter to an object */ + void setVec2i(OSPObject object, + const char *bufName, + const vec2i &v) override; + + /*! assign (named) vec3i parameter to an object */ + void setVec3i(OSPObject object, + const char *bufName, + const vec3i &v) override; + + /*! add untyped void pointer to object - this will *ONLY* work in local + rendering! */ + void setVoidPtr(OSPObject object, const char *bufName, void *v) override; + + void removeParam(OSPObject object, const char *name) override; + + /*! create a new renderer object (out of list of registered renderers) */ + OSPRenderer newRenderer(const char *type) override; + + /*! create a new geometry object (out of list of registered geometrys) */ + OSPGeometry newGeometry(const char *type) override; + + /*! have given renderer create a new material */ + OSPMaterial newMaterial(OSPRenderer _renderer, const char *type) override; + + /*! create a new camera object (out of list of registered cameras) */ + OSPCamera newCamera(const char *type) override; + + /*! create a new volume object (out of list of registered volumes) */ + OSPVolume newVolume(const char *type) override; + + /*! call a renderer to render a frame buffer */ + float renderFrame(OSPFrameBuffer _sc, + OSPRenderer _renderer, + const uint32 fbChannelFlags) override; + + /*! load module */ + int loadModule(const char *name) override; + + //! release (i.e., reduce refcount of) given object + /*! note that all objects in ospray are refcounted, so one cannot + explicitly "delete" any object. instead, each object is created + with a refcount of 1, and this refcount will be + increased/decreased every time another object refers to this + object resp releases its hold on it override; if the refcount is 0 the + object will automatically get deleted. For example, you can + create a new material, assign it to a geometry, and immediately + after this assignation release its refcount override; the material will + stay 'alive' as long as the given geometry requires it. */ + void release(OSPObject _obj) override; + + //! assign given material to given geometry + void setMaterial(OSPGeometry _geom, OSPMaterial _mat) override; + + /*! create a new Texture2D object */ + OSPTexture2D newTexture2D(const vec2i &size, const OSPTextureFormat, + void *data, const uint32 flags) override; + + /*! sample a volume */ + void sampleVolume(float **results, + OSPVolume volume, + const vec3f *worldCoordinates, + const size_t &count) override; + + private: + + bool initialized {false}; + int masterRank {0}; + }; + + } // ::ospray::mpi +} // ::ospray + + diff --git a/modules/mpi/MPIOffloadDevice.cpp b/modules/mpi/MPIOffloadDevice.cpp new file mode 100644 index 0000000000..25afb7b955 --- /dev/null +++ b/modules/mpi/MPIOffloadDevice.cpp @@ -0,0 +1,866 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#undef NDEBUG // do all assertions in this file + +#include "mpiCommon/MPICommon.h" +#include "mpiCommon/MPIBcastFabric.h" +#include "mpi/MPIOffloadDevice.h" +#include "common/Model.h" +#include "common/Data.h" +#include "common/Library.h" +#include "common/Util.h" +#include "ospcommon/sysinfo.h" +#include "ospcommon/FileName.h" +#include "geometry/TriangleMesh.h" +#include "render/Renderer.h" +#include "camera/Camera.h" +#include "volume/Volume.h" +#include "mpi/render/MPILoadBalancer.h" +#include "fb/LocalFB.h" +#include "mpi/fb/DistributedFrameBuffer.h" +#include "ospcommon/networking/BufferedDataStreaming.h" +#include "ospcommon/networking/Socket.h" + +// std +#ifndef _WIN32 +# include // for fork() +# include +#endif + +#ifdef OPEN_MPI +# include +//# define _GNU_SOURCE +# include +#endif + +namespace ospray { + namespace mpi { + + using namespace mpicommon; + + // Forward declarations /////////////////////////////////////////////////// + + //! this runs an ospray worker process. + /*! it's up to the proper init routine to decide which processes + call this function and which ones don't. This function will not + return. */ + void runWorker(); + + // Misc helper functions ////////////////////////////////////////////////// + + static inline void throwIfNotMpiParallel() + { + if (world.size <= 1) { + throw std::runtime_error("No MPI workers found.\n#osp:mpi: Fatal Error " + "- OSPRay told to run in MPI mode, but there " + "seems to be no MPI peers!?\n#osp:mpi: (Did " + "you forget an 'mpirun' in front of your " + "application?)"); + } + } + + static inline void setupMaster() + { + MPI_CALL(Comm_split(mpi::world.comm,1,mpi::world.rank,&app.comm)); + + app.makeIntraComm(); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#w: app process " << app.rank << '/' << app.size + << " (global " << world.rank << '/' << world.size; + + MPI_CALL(Intercomm_create(app.comm, 0, world.comm, 1, 1, &worker.comm)); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "master: Made 'worker' intercomm (through intercomm_create): " + << std::hex << std::showbase << worker.comm + << std::noshowbase << std::dec; + + worker.makeInterComm(); + + // ------------------------------------------------------- + // at this point, all processes should be set up and synced. in + // particular: + // - app has intracommunicator to all workers (and vica versa) + // - app process(es) are in one intercomm ("app"); workers all in + // another ("worker") + // - all processes (incl app) have barrier'ed, and thus now in sync. + } + + static inline void setupWorker() + { + MPI_CALL(Comm_split(mpi::world.comm,0,mpi::world.rank,&worker.comm)); + + worker.makeIntraComm(); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "master: Made 'worker' intercomm (through split): " + << std::hex << std::showbase << worker.comm + << std::noshowbase << std::dec; + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#w: app process " << app.rank << '/' << app.size + << " (global " << world.rank << '/' << world.size; + + MPI_CALL(Intercomm_create(worker.comm, 0, world.comm, 0, 1, &app.comm)); + + app.makeInterComm(); + + // ------------------------------------------------------- + // at this point, all processes should be set up and synced. in + // particular: + // - app has intracommunicator to all workers (and vica versa) + // - app process(es) are in one intercomm ("app"); workers all in + // another ("worker") + // - all processes (incl app) have barrier'ed, and thus now in sync. + } + + // MPI initialization helper functions //////////////////////////////////// + + /*! in this mode ("ospray on ranks" mode, or "ranks" mode), the + user has launched the app across all ranks using mpirun " "; no new processes need to get launched. + + Based on the 'startworkers' flag, this function can set up ospray in + one of two modes: + + in "workers" mode (startworkes=true) all ranks > 0 become + workers, and will NOT return to the application; rank 0 is the + master that controls those workers but doesn't do any + rendeirng (we may at some point allow the master to join in + working as well, but currently this is not implemented). to + reach that mode we call this function with + 'startworkers=true', which will make sure that, even though + all ranks _called_ mpiinit, only rank 0 will ever return to + the app, while all other ranks will automatically go to + running worker code, and never ever return from this function. + + b) in "distribtued" mode the app itself is distributed, and + will use the ospray distributed api to control ospray in a + data-distributed mode. in this way, we'll call this function + with startWorkers=false, which will let all ranks return from + this function to do further work in the app. + + For this function, we assume: + + - all *all* MPI_COMM_WORLD processes are going into this function + + - this fct is called from ospInit (with ranksBecomeWorkers=true) or + from ospdMpiInit (w/ ranksBecomeWorkers = false) + */ + void createMPI_RanksBecomeWorkers(int *ac, const char **av) + { + mpi::init(ac,av); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#o: initMPI::OSPonRanks: " << world.rank << '/' << world.size; + + MPI_CALL(Barrier(MPI_COMM_WORLD)); + + throwIfNotMpiParallel(); + + if (IamTheMaster()) + setupMaster(); + else { + setupWorker(); + + // now, all workers will enter their worker loop (ie, they will *not* + // return) + mpi::runWorker(); + throw std::runtime_error("should never reach here!"); + /* no return here - 'runWorker' will never return */ + } + } + + /*! in this mode ("separate worker group" mode) + - the user may or may not have launched MPI explicitly for his app + - the app may or may not be running distributed + - the ospray frontend (the part linked to the app) will wait for a remote + MPI gruop of + workers to connect to this app + - the ospray frontend will store the port its waiting on connections for + in the + filename passed to this function; or will print it to stdout if this is + nullptr + */ + void createMPI_ListenForWorkers(int *ac, const char **av) + { + mpi::init(ac,av); + + if (world.rank < 1) { + postStatusMsg("====================================================\n" + "initializing OSPRay MPI in 'listen for workers' mode\n" + "====================================================", + OSPRAY_MPI_VERBOSE_LEVEL); + } + + app.comm = world.comm; + app.makeIntraComm(); + + char appPortName[MPI_MAX_PORT_NAME]; + + if (!IamTheMaster()) { + throw std::runtime_error("--osp:mpi-listen only makes sense with " + "a single rank..."); + } + + MPI_CALL(Open_port(MPI_INFO_NULL, appPortName)); + + socket_t mpiPortSocket = ospcommon::bind(3141); + socket_t clientSocket = ospcommon::listen(mpiPortSocket); + size_t len = strlen(appPortName); + ospcommon::write(clientSocket,&len,sizeof(len)); + ospcommon::write(clientSocket,appPortName,len); + flush(clientSocket); + + MPI_CALL(Comm_accept(appPortName,MPI_INFO_NULL,0,app.comm,&worker.comm)); + ospcommon::close(clientSocket); + ospcommon::close(mpiPortSocket); + + worker.makeInterComm(); + + mpi::Group mergedComm; + MPI_CALL(Intercomm_merge(worker.comm,0,&mergedComm.comm)); + mergedComm.makeIntraComm(); + mpi::world.comm = mergedComm.comm; + mpi::world.makeIntraComm(); + + if (app.rank == 0) { + postStatusMsg("=====================================================\n" + "OSPRAY Worker ring connected\n" + "=====================================================", + OSPRAY_MPI_VERBOSE_LEVEL); + } + + mpi::world.barrier(); + } + + void createMPI_connectToListener(int *ac, const char **av, + const std::string &host) + { + mpi::init(ac,av); + + if (world.rank < 1) { + postStatusMsg("=====================================================\n" + "initializing OSPRay MPI in 'connect to master' mode \n" + "=====================================================", + OSPRAY_MPI_VERBOSE_LEVEL); + } + + worker.comm = world.comm; + worker.makeIntraComm(); + + char appPortName[MPI_MAX_PORT_NAME]; + if (worker.rank == 0) { + auto masterSocket = ospcommon::connect(host.c_str(),3141); + + size_t len; + ospcommon::read(masterSocket,&len,sizeof(len)); + ospcommon::read(masterSocket,appPortName,len); + ospcommon::close(masterSocket); + } + + MPI_CALL(Comm_connect(appPortName,MPI_INFO_NULL,0,worker.comm,&app.comm)); + app.makeInterComm(); + + + mpi::Group mergedComm; + MPI_CALL(Intercomm_merge(app.comm,1,&mergedComm.comm)); + mergedComm.makeIntraComm(); + mpi::world.comm = mergedComm.comm; + mpi::world.makeIntraComm(); + + mpi::world.barrier(); + + postStatusMsg("starting worker...", OSPRAY_MPI_VERBOSE_LEVEL); + mpi::runWorker(); + } + + /*! in this mode ("separate worker group" mode) + - the user may or may not have launched MPI explicitly for his app + - the app may or may not be running distributed + - the ospray frontend (the part linked to the app) will use the specified + 'launchCmd' to launch a _new_ MPI group of worker processes. + - the ospray frontend will assume the launched process to output + 'OSPRAY_SERVICE_PORT' stdout, will parse that output for this string, + and create an mpi connection to this port to establish the service + */ + void createMPI_LaunchWorkerGroup(int *ac, const char **av, + const char *launchCommand) + { + mpi::init(ac,av); + + Assert(launchCommand); + + MPI_CALL(Comm_dup(world.comm,&app.comm)); + app.makeIntraComm(); + + char appPortName[MPI_MAX_PORT_NAME]; + if (app.rank == 0 || app.size == -1) { + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "=======================================================\n" + << "initializing OSPRay MPI in 'launching new workers' mode" + << "=======================================================\n" + << "using launch script '" << launchCommand << "'"; + + MPI_CALL(Open_port(MPI_INFO_NULL, appPortName)); + + // fix port name: replace all '$'s by '%'s to allow using it on the + // cmdline... + char *fixedPortName = strdup(appPortName); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "with port " << fixedPortName + << "\n======================================================="; + + for (char *s = fixedPortName; *s; ++s) + if (*s == '$') *s = '%'; + + char systemCommand[10000]; + sprintf(systemCommand,"%s %s",launchCommand,fixedPortName); +#ifdef _WIN32 + throw std::runtime_error("OSPRay MPI: no fork() yet on Windows"); +#else + if (fork()) { + auto result = system(systemCommand); + (void)result; + + postStatusMsg("OSPRay worker process has died - killing application"); + exit(0); + } +#endif + } + + MPI_CALL(Comm_accept(appPortName,MPI_INFO_NULL,0,app.comm,&worker.comm)); + worker.makeInterComm(); + + if (app.rank == 0 || app.size == -1) { + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "OSPRay MPI Worker ring successfully connected.\n" + << "found " << worker.size << " workers." + << "\n======================================================="; + } + + if (app.size > 1) { + if (app.rank == 1) { + postStatusMsg() + << "ospray:WARNING: you're trying to run an mpi-parallel app " + << "with ospray\n" + << "(only the root node is allowed to issue ospray api " + << "calls right now)\n" + << "======================================================="; + } + + MPI_CALL(Barrier(app.comm)); + } + + MPI_CALL(Barrier(app.comm)); + } + + // MPIDevice definitions ////////////////////////////////////////////////// + + MPIOffloadDevice::~MPIOffloadDevice() + { + if (IamTheMaster()) { + postStatusMsg("shutting down mpi device", OSPRAY_MPI_VERBOSE_LEVEL); + work::CommandFinalize work; + processWork(work, true); + } + } + + void MPIOffloadDevice::commit() + { + if (initialized) + return; + + Device::commit(); + + initialized = true; + + work::registerOSPWorkItems(workRegistry); + + int _ac = 2; + const char *_av[] = {"ospray_mpi_worker", "--osp:mpi"}; + + std::string mode = getParamString("mpiMode", "mpi"); + + if (mode == "mpi") { + createMPI_RanksBecomeWorkers(&_ac,_av); + } else if(mode == "mpi-launch") { + std::string launchCommand = getParamString("launchCommand", ""); + + if (launchCommand.empty()) { + throw std::runtime_error("You must provide the launchCommand " + "parameter in mpi-launch mode!"); + } + + createMPI_LaunchWorkerGroup(&_ac,_av,launchCommand.c_str()); + } else if (mode == "mpi-listen") { + createMPI_ListenForWorkers(&_ac,_av); + } else if (mode == "mpi-connect") { + std::string portName = + getParamString("portName", ""); + + if (portName.empty()) { + throw std::runtime_error("You must provide the port name string " + "where the master is listening at!"); + } + + createMPI_connectToListener(&_ac,_av,portName); + } else { + throw std::runtime_error("Invalid MPI mode!"); + } + + if (mpi::world.size != 1) { + if (mpi::world.rank < 0) { + throw std::runtime_error("OSPRay MPI startup error. Use \"mpirun " + "-n 1 \" when calling an " + "application that tries to spawn to start " + "the application you were trying to " + "start."); + } + } + + /* set up fabric and stuff - by now all the communicators should + be properly set up */ + mpiFabric = make_unique(mpi::worker, MPI_ROOT, 0); + readStream = make_unique(*mpiFabric); + writeStream = make_unique(*mpiFabric); + + TiledLoadBalancer::instance = make_unique(); + } + + OSPFrameBuffer + MPIOffloadDevice::frameBufferCreate(const vec2i &size, + const OSPFrameBufferFormat mode, + const uint32 channels) + { + ObjectHandle handle = allocateHandle(); + work::CreateFrameBuffer work(handle, size, mode, channels); + processWork(work); + return (OSPFrameBuffer)(int64)handle; + } + + + /*! map frame buffer */ + const void *MPIOffloadDevice::frameBufferMap(OSPFrameBuffer _fb, + OSPFrameBufferChannel channel) + { + ObjectHandle handle = (const ObjectHandle &)_fb; + FrameBuffer *fb = (FrameBuffer *)handle.lookup(); + + switch (channel) { + case OSP_FB_COLOR: return fb->mapColorBuffer(); + case OSP_FB_DEPTH: return fb->mapDepthBuffer(); + default: return nullptr; + } + } + + /*! unmap previously mapped frame buffer */ + void MPIOffloadDevice::frameBufferUnmap(const void *mapped, + OSPFrameBuffer _fb) + { + ObjectHandle handle = (const ObjectHandle &)_fb; + FrameBuffer *fb = (FrameBuffer *)handle.lookup(); + + fb->unmap(mapped); + } + + /*! create a new model */ + OSPModel MPIOffloadDevice::newModel() + { + ObjectHandle handle = allocateHandle(); + work::NewModel work("", handle); + processWork(work); + return (OSPModel)(int64)handle; + } + + /*! finalize a newly specified model */ + void MPIOffloadDevice::commit(OSPObject _object) + { + const ObjectHandle handle = (const ObjectHandle&)_object; + work::CommitObject work(handle); + processWork(work, true); + } + + /*! add a new geometry to a model */ + void MPIOffloadDevice::addGeometry(OSPModel _model, OSPGeometry _geometry) + { + work::AddGeometry work(_model, _geometry); + processWork(work); + } + + /*! add a new volume to a model */ + void MPIOffloadDevice::addVolume(OSPModel _model, OSPVolume _volume) + { + work::AddVolume work(_model, _volume); + processWork(work); + } + + /*! create a new data buffer */ + OSPData MPIOffloadDevice::newData(size_t nitems, OSPDataType format, + void *init, int flags) + { + ObjectHandle handle = allocateHandle(); + + flags = flags & ~OSP_DATA_SHARED_BUFFER; + + work::NewData work(handle, nitems, format, init, flags); + processWork(work); + + return (OSPData)(int64)handle; + } + + /*! assign (named) string parameter to an object */ + void MPIOffloadDevice::setVoidPtr(OSPObject _object, + const char *bufName, + void *v) + { + UNUSED(_object, bufName, v); + throw std::runtime_error("setting a void pointer as parameter to an " + "object is not allowed in MPI mode"); + } + + void MPIOffloadDevice::removeParam(OSPObject object, const char *name) + { + work::RemoveParam work((ObjectHandle&)object, name); + processWork(work); + } + + /*! Copy data into the given object. */ + int MPIOffloadDevice::setRegion(OSPVolume _volume, const void *source, + const vec3i &index, const vec3i &count) + { + char *typeString = nullptr; + getString(_volume, "voxelType", &typeString); + OSPDataType type = typeForString(typeString); + delete [] typeString; + + Assert(type != OSP_UNKNOWN && "unknown volume voxel type"); + work::SetRegion work(_volume, index, count, source, type); + processWork(work); + return true; + } + + /*! assign (named) string parameter to an object */ + void MPIOffloadDevice::setString(OSPObject _object, + const char *bufName, + const char *s) + { + work::SetParam work((ObjectHandle&)_object, bufName, s); + processWork(work); + } + + /*! load module */ + int MPIOffloadDevice::loadModule(const char *name) + { + work::LoadModule work(name); + processWork(work, true); + return work.errorCode; + } + + /*! assign (named) float parameter to an object */ + void MPIOffloadDevice::setFloat(OSPObject _object, + const char *bufName, + const float f) + { + work::SetParam work((ObjectHandle&)_object, bufName, f); + processWork(work); + } + + /*! assign (named) int parameter to an object */ + void MPIOffloadDevice::setInt(OSPObject _object, + const char *bufName, + const int i) + { + work::SetParam work((ObjectHandle&)_object, bufName, i); + processWork(work); + } + + /*! assign (named) vec2f parameter to an object */ + void MPIOffloadDevice::setVec2f(OSPObject _object, + const char *bufName, + const vec2f &v) + { + work::SetParam work((ObjectHandle&)_object, bufName, v); + processWork(work); + } + + /*! assign (named) vec3f parameter to an object */ + void MPIOffloadDevice::setVec3f(OSPObject _object, + const char *bufName, + const vec3f &v) + { + work::SetParam work((ObjectHandle&)_object, bufName, v); + processWork(work); + } + + /*! assign (named) vec4f parameter to an object */ + void MPIOffloadDevice::setVec4f(OSPObject _object, + const char *bufName, + const vec4f &v) + { + work::SetParam work((ObjectHandle&)_object, bufName, v); + processWork(work); + } + + /*! assign (named) vec2i parameter to an object */ + void MPIOffloadDevice::setVec2i(OSPObject _object, + const char *bufName, + const vec2i &v) + { + work::SetParam work((ObjectHandle&)_object, bufName, v); + processWork(work); + } + + /*! assign (named) vec3i parameter to an object */ + void MPIOffloadDevice::setVec3i(OSPObject _object, + const char *bufName, + const vec3i &v) + { + work::SetParam work((ObjectHandle&)_object, bufName, v); + processWork(work); + } + + /*! assign (named) data item as a parameter to an object */ + void MPIOffloadDevice::setObject(OSPObject _target, + const char *bufName, + OSPObject _value) + { + work::SetParam work((ObjectHandle&)_target, bufName, _value); + processWork(work); + } + + /*! create a new pixelOp object (out of list of registered pixelOps) */ + OSPPixelOp MPIOffloadDevice::newPixelOp(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewPixelOp work(type, handle); + processWork(work); + return (OSPPixelOp)(int64)handle; + } + + /*! set a frame buffer's pixel op object */ + void MPIOffloadDevice::setPixelOp(OSPFrameBuffer _fb, OSPPixelOp _op) + { + work::SetPixelOp work(_fb, _op); + processWork(work); + } + + /*! create a new renderer object (out of list of registered renderers) */ + OSPRenderer MPIOffloadDevice::newRenderer(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewRenderer work(type, handle); + processWork(work); + return (OSPRenderer)(int64)handle; + } + + /*! create a new camera object (out of list of registered cameras) */ + OSPCamera MPIOffloadDevice::newCamera(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewCamera work(type, handle); + processWork(work); + return (OSPCamera)(int64)handle; + } + + /*! create a new volume object (out of list of registered volumes) */ + OSPVolume MPIOffloadDevice::newVolume(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewVolume work(type, handle); + processWork(work); + return (OSPVolume)(int64)handle; + } + + /*! create a new geometry object (out of list of registered geometries) */ + OSPGeometry MPIOffloadDevice::newGeometry(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewGeometry work(type, handle); + processWork(work); + return (OSPGeometry)(int64)handle; + } + + /*! have given renderer create a new material */ + OSPMaterial MPIOffloadDevice::newMaterial(OSPRenderer _renderer, + const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewMaterial work(type, _renderer, handle); + processWork(work); + return (OSPMaterial)(int64)handle; + } + + /*! create a new transfer function object (out of list of + registered transfer function types) */ + OSPTransferFunction MPIOffloadDevice::newTransferFunction(const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewTransferFunction work(type, handle); + processWork(work); + return (OSPTransferFunction)(int64)handle; + } + + /*! have given renderer create a new Light */ + OSPLight MPIOffloadDevice::newLight(OSPRenderer _renderer, const char *type) + { + ObjectHandle handle = allocateHandle(); + work::NewLight work(type, _renderer, handle); + processWork(work); + return (OSPLight)(int64)handle; + } + + /*! clear the specified channel(s) of the frame buffer specified in + 'whichChannels' + + if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to + '0,0,0,0'. + + if whichChannel&OSP_FB_DEPTH!=0, clear the depth buffer to + +inf. + + if whichChannel&OSP_FB_ACCUM!=0, clear the accum buffer to 0,0,0,0, + and reset accumID. + */ + void MPIOffloadDevice::frameBufferClear(OSPFrameBuffer _fb, + const uint32 fbChannelFlags) + { + work::ClearFrameBuffer work(_fb, fbChannelFlags); + processWork(work); + } + + /*! remove an existing geometry from a model */ + void MPIOffloadDevice::removeGeometry(OSPModel _model, + OSPGeometry _geometry) + { + work::RemoveGeometry work(_model, _geometry); + processWork(work); + } + + /*! remove an existing volume from a model */ + void MPIOffloadDevice::removeVolume(OSPModel _model, OSPVolume _volume) + { + work::RemoveVolume work(_model, _volume); + processWork(work); + } + + /*! call a renderer to render a frame buffer */ + float MPIOffloadDevice::renderFrame(OSPFrameBuffer _fb, + OSPRenderer _renderer, + const uint32 fbChannelFlags) + { + work::RenderFrame work(_fb, _renderer, fbChannelFlags); + processWork(work, true); + return work.varianceResult; + } + + //! release (i.e., reduce refcount of) given object + /*! note that all objects in ospray are refcounted, so one cannot + explicitly "delete" any object. instead, each object is created + with a refcount of 1, and this refcount will be + increased/decreased every time another object refers to this + object resp releases its hold on it; if the refcount is 0 the + object will automatically get deleted. For example, you can + create a new material, assign it to a geometry, and immediately + after this assignation release its refcount; the material will + stay 'alive' as long as the given geometry requires it. */ + void MPIOffloadDevice::release(OSPObject _obj) + { + work::CommandRelease work((const ObjectHandle&)_obj); + processWork(work); + } + + //! assign given material to given geometry + void MPIOffloadDevice::setMaterial(OSPGeometry _geometry, + OSPMaterial _material) + { + work::SetMaterial work((ObjectHandle&)_geometry, _material); + processWork(work); + } + + /*! create a new Texture2D object */ + OSPTexture2D MPIOffloadDevice::newTexture2D(const vec2i &sz, + const OSPTextureFormat type, void *data, const uint32 flags) + { + ObjectHandle handle = allocateHandle(); + work::NewTexture2d work(handle, sz, type, data, flags); + processWork(work); + return (OSPTexture2D)(int64)handle; + } + + void MPIOffloadDevice::sampleVolume(float **results, + OSPVolume volume, + const vec3f *worldCoordinates, + const size_t &count) + { + UNUSED(results, volume, worldCoordinates, count); + NOT_IMPLEMENTED; + } + + int MPIOffloadDevice::getString(OSPObject _object, + const char *name, + char **value) + { + ManagedObject *object = ((ObjectHandle&)_object).lookup(); + ManagedObject::Param *param = object->findParam(name); + bool foundParameter = (param != nullptr && param->type == OSP_STRING); + if (foundParameter) { + *value = new char[2048]; + strncpy(*value, param->s->c_str(), 2048); + return true; + } + return false; + } + + void MPIOffloadDevice::processWork(work::Work &work, bool flushWriteStream) + { + static size_t numWorkSent = 0; + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp.mpi.master: processing/sending work item " + << numWorkSent++; + + auto tag = typeIdOf(work); + writeStream->write(&tag, sizeof(tag)); + work.serialize(*writeStream); + + if (flushWriteStream) + writeStream->flush(); + + // Run the master side variant of the work unit + work.runOnMaster(); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp.mpi.master: done work item, tag " << tag << ": " + << typeString(work); + } + + ObjectHandle MPIOffloadDevice::allocateHandle() const + { + return ObjectHandle(); + } + + OSP_REGISTER_DEVICE(MPIOffloadDevice, mpi_offload); + OSP_REGISTER_DEVICE(MPIOffloadDevice, mpi); + + } // ::ospray::mpi +} // ::ospray + +extern "C" OSPRAY_DLLEXPORT void ospray_init_module_mpi() +{ +} diff --git a/modules/mpi/MPIDevice.h b/modules/mpi/MPIOffloadDevice.h similarity index 87% rename from modules/mpi/MPIDevice.h rename to modules/mpi/MPIOffloadDevice.h index 3793be7476..527c0d2ed6 100644 --- a/modules/mpi/MPIDevice.h +++ b/modules/mpi/MPIOffloadDevice.h @@ -16,23 +16,25 @@ #pragma once +// ospcommon +#include "ospcommon/networking/BufferedDataStreaming.h" // mpicommon #include "mpiCommon/MPICommon.h" -#include "mpiCommon/command.h" -#include "mpiCommon/BufferedMPIComm.h" // ospray #include "api/Device.h" #include "common/Managed.h" // ospray::mpi -#include "mpi/common/OSPWork.h" +#include "common/OSPWork.h" /*! \file MPIDevice.h Implements the "mpi" device for mpi rendering */ namespace ospray { namespace mpi { - struct MPIDevice : public api::Device { - MPIDevice(); - ~MPIDevice(); + + struct MPIOffloadDevice : public api::Device + { + MPIOffloadDevice() = default; + ~MPIOffloadDevice(); // ManagedObject Implementation ///////////////////////////////////////// @@ -67,7 +69,8 @@ namespace ospray { /*! create a new pixelOp object (out of list of registered pixelOps) */ OSPPixelOp newPixelOp(const char *type) override; - /*! clear the specified channel(s) of the frame buffer specified in 'whichChannels' + /*! clear the specified channel(s) of the frame buffer specified in + 'whichChannels' if whichChannel&OSP_FB_COLOR!=0, clear the color buffer to '0,0,0,0'. @@ -79,14 +82,11 @@ namespace ospray { and reset accumID. */ void frameBufferClear(OSPFrameBuffer _fb, - const uint32 fbChannelFlags) override; + const uint32 fbChannelFlags) override; /*! create a new model */ OSPModel newModel() override; - // /*! finalize a newly specified model */ - // void finalizeModel(OSPModel _model) override; - /*! commit the given object's outstanding changes */ void commit(OSPObject object) override; @@ -108,7 +108,7 @@ namespace ospray { /*! Copy data into the given volume. */ int setRegion(OSPVolume object, const void *source, - const vec3i &index, const vec3i &count) override; + const vec3i &index, const vec3i &count) override; /*! assign (named) string parameter to an object */ void setString(OSPObject object, @@ -153,7 +153,8 @@ namespace ospray { const char *bufName, const vec3i &v) override; - /*! add untyped void pointer to object - this will *ONLY* work in local rendering! */ + /*! add untyped void pointer to object - this will *ONLY* work in local + rendering! */ void setVoidPtr(OSPObject object, const char *bufName, void *v) override; void removeParam(OSPObject object, const char *name) override; @@ -175,8 +176,8 @@ namespace ospray { /*! call a renderer to render a frame buffer */ float renderFrame(OSPFrameBuffer _sc, - OSPRenderer _renderer, - const uint32 fbChannelFlags) override; + OSPRenderer _renderer, + const uint32 fbChannelFlags) override; /*! load module */ int loadModule(const char *name) override; @@ -200,38 +201,32 @@ namespace ospray { OSPTexture2D newTexture2D(const vec2i &size, const OSPTextureFormat, void *data, const uint32 flags) override; - /*! switch API mode for distriubted API extensions */ - void apiMode(OSPDApiMode mode) override; - - OSPDApiMode currentApiMode {OSPD_MODE_MASTERED}; - /*! sample a volume */ void sampleVolume(float **results, OSPVolume volume, const vec3f *worldCoordinates, const size_t &count) override; - void processWork(work::Work* work); - private: + void processWork(work::Work &work, bool flushWriteStream = false); + /*! This only exists to support getting the voxel type for setRegion */ int getString(OSPObject object, const char *name, char **value); ObjectHandle allocateHandle() const; - std::shared_ptr bufferedComm; + /*! @{ read and write stream for the work commands */ + std::unique_ptr mpiFabric; + std::unique_ptr readStream; + std::unique_ptr writeStream; + /*! @} */ + + work::WorkTypeRegistry workRegistry; bool initialized {false}; }; - // ================================================================== - // Helper functions - // ================================================================== - - /*! return a string represenging the given API Mode */ - const char *apiModeName(int mode); - } // ::ospray::mpi } // ::ospray diff --git a/modules/mpi/worker.cpp b/modules/mpi/MPIOffloadWorker.cpp similarity index 55% rename from modules/mpi/worker.cpp rename to modules/mpi/MPIOffloadWorker.cpp index 72629ef876..82dc1a3fb2 100644 --- a/modules/mpi/worker.cpp +++ b/modules/mpi/MPIOffloadWorker.cpp @@ -15,8 +15,8 @@ // ======================================================================== // #include "mpiCommon/MPICommon.h" -#include "mpiCommon/async/CommLayer.h" -#include "mpi/MPIDevice.h" +#include "mpiCommon/MPIBcastFabric.h" +#include "mpi/MPIOffloadDevice.h" #include "common/Model.h" #include "common/Data.h" #include "common/Library.h" @@ -31,17 +31,19 @@ #include "mpi/fb/DistributedFrameBuffer.h" #include "mpi/render/MPILoadBalancer.h" #include "transferFunction/TransferFunction.h" - +#include "common/OSPWork.h" // std #include +#ifdef OPEN_MPI +# include +//# define _GNU_SOURCE +# include +#endif + #ifdef _WIN32 # include // for Sleep and gethostname # include // for getpid -void sleep(unsigned int seconds) -{ - Sleep(seconds * 1000); -} #else # include // for gethostname #endif @@ -53,20 +55,44 @@ void sleep(unsigned int seconds) #endif namespace ospray { - namespace mpi { - using std::cout; - using std::endl; - OSPRAY_MPI_INTERFACE void runWorker(); + using namespace mpicommon; void embreeErrorFunc(const RTCError code, const char* str) { - std::cerr << "#osp: embree internal error " << code << " : " - << str << std::endl; - throw std::runtime_error("embree internal error '"+std::string(str)+"'"); + std::stringstream msg; + msg << "#osp: Embree internal error " << code << " : " << str; + postStatusMsg(msg); + throw std::runtime_error(msg.str()); } + std::unique_ptr readWork(work::WorkTypeRegistry ®istry, + networking::ReadStream &readStream) + { + work::Work::tag_t tag; + readStream >> tag; + + static size_t numWorkReceived = 0; + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp.mpi.worker: got work #" << numWorkReceived++ + << ", tag " << tag; + + auto make_work = registry.find(tag); + if (make_work == registry.end()) { + std::stringstream msg; + msg << "Invalid work type received - tag #: " << tag << "\n"; + postStatusMsg(msg); + throw std::runtime_error(msg.str()); + } + + auto work = make_work->second(); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) << ": " << typeString(work); + + work->deserialize(readStream); + return work; + } /*! it's up to the proper init routine to decide which processes call this function and which @@ -78,25 +104,16 @@ namespace ospray { { auto &device = ospray::api::Device::current; - auto numThreads = device ? device->numThreads : -1; - - // initialize embree. (we need to do this here rather than in - // ospray::init() because in mpi-mode the latter is also called - // in the host-stubs, where it shouldn't. - std::stringstream embreeConfig; - if (device && device->debugMode) - embreeConfig << " threads=1,verbose=2"; - else if(numThreads > 0) - embreeConfig << " threads=" << numThreads; - // NOTE(jda) - This guard guarentees that the embree device gets cleaned // up no matter how the scope of runWorker() is left - struct EmbreeDeviceScopeGuard { + struct EmbreeDeviceScopeGuard + { RTCDevice embreeDevice; ~EmbreeDeviceScopeGuard() { rtcDeleteDevice(embreeDevice); } }; - RTCDevice embreeDevice = rtcNewDevice(embreeConfig.str().c_str()); + auto embreeDevice = + rtcNewDevice(generateEmbreeDeviceCfg(*device).c_str()); device->embreeDevice = embreeDevice; EmbreeDeviceScopeGuard guard; guard.embreeDevice = embreeDevice; @@ -105,27 +122,39 @@ namespace ospray { if (rtcDeviceGetError(embreeDevice) != RTC_NO_ERROR) { // why did the error function not get called !? - std::cerr << "#osp:init: embree internal error number " - << (int)rtcDeviceGetError(embreeDevice) << std::endl; + postStatusMsg() << "#osp:init: embree internal error number " + << (int)rtcDeviceGetError(embreeDevice); } char hostname[HOST_NAME_MAX]; gethostname(hostname,HOST_NAME_MAX); - printf("#w: running MPI worker process %i/%i on pid %i@%s\n", - worker.rank,worker.size,getpid(),hostname); + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#w: running MPI worker process " << worker.rank + << "/" << worker.size << " on pid " << getpid() << "@" << hostname; TiledLoadBalancer::instance = make_unique(); - auto bufferedComm = mpi::BufferedMPIComm::get(); + // ------------------------------------------------------- + // setting up read/write streams + // ------------------------------------------------------- + auto mpiFabric = make_unique(mpi::app, MPI_ROOT, 0); + auto readStream = make_unique(*mpiFabric); + + // create registry of work item types + std::map workTypeRegistry; + work::registerOSPWorkItems(workTypeRegistry); + while (1) { - std::vector workCommands; - bufferedComm->recv(mpi::Address(&mpi::app, (int32)mpi::RECV_ALL), - workCommands); - for (work::Work *&w : workCommands) { - w->run(); - delete w; - w = nullptr; - } + auto work = readWork(workTypeRegistry, *readStream); + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp.mpi.worker: processing work " << typeIdOf(work) + << ": " << typeString(work); + + work->run(); + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp.mpi.worker: done w/ work " << typeIdOf(work) + << ": " << typeString(work); } } diff --git a/modules/mpi/MPIWorker.cpp b/modules/mpi/MPIWorker.cpp deleted file mode 100644 index c6bc9fd6d2..0000000000 --- a/modules/mpi/MPIWorker.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// ======================================================================== // -// Copyright 2009-2017 Intel Corporation // -// // -// Licensed under the Apache License, Version 2.0 (the "License"); // -// you may not use this file except in compliance with the License. // -// You may obtain a copy of the License at // -// // -// http://www.apache.org/licenses/LICENSE-2.0 // -// // -// Unless required by applicable law or agreed to in writing, software // -// distributed under the License is distributed on an "AS IS" BASIS, // -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // -// See the License for the specific language governing permissions and // -// limitations under the License. // -// ======================================================================== // - -// ospray components -#include "components/mpiCommon/MPICommon.h" -// ospray API -#include "ospray/ospray.h" - -namespace ospray { - namespace mpi { - - using std::cout; - using std::endl; - - OSPRAY_MPI_INTERFACE void runWorker(); - - void workerMain(int ac, const char **av) - { - if (ac < 2) { - int argc = 2; - const char *argv[] = {"ospray_mpi_worker", "--osp:mpi"}; - ospInit(&argc, argv); - } else { - ospInit(&ac, av); - } - - int rc; - (void)rc; - mpi::init(&ac,av); - worker.comm = world.comm; - worker.makeIntraComm(); - - if (ac == 3 && !strcmp(av[1],"--osp:connect")) { - char *appPortName = strdup(av[2]); - // de-fix port name - 'real' port name has '$'s, not '%'s - for (char *s = appPortName; *s; ++s) - if (*s == '%') *s = '$'; - cout << "#w: trying to connect to port " << appPortName << endl; - rc = MPI_Comm_connect(appPortName,MPI_INFO_NULL,0, - worker.comm,&app.comm); - Assert(rc == MPI_SUCCESS); - cout << "#w: ospray started with " << worker.size << " workers, " - << "#w: and connected to app at " << appPortName << endl; - app.makeInterComm(); - } else { - char servicePortName[MPI_MAX_PORT_NAME]; - if (world.rank == 0) { - rc = MPI_Open_port(MPI_INFO_NULL, servicePortName); - cout << "#osp:ospray_mpi_worker opened port: " << servicePortName - << endl; - for (char *s = servicePortName; *s ; s++) - if (*s == '$') *s = '%'; - - Assert(rc == MPI_SUCCESS); - cout << "------------------------------------------------------" - << endl; - cout << "ospray service started with " << worker.size - << " workers" << endl; - cout << "OSPRAY_SERVICE_PORT:" << servicePortName << endl; - } - cout << "#osp:ospray_mpi_worker trying to accept from '" - << servicePortName << "'" << endl; - rc = MPI_Comm_accept(servicePortName,MPI_INFO_NULL, - 0,MPI_COMM_WORLD,&app.comm); - Assert(rc == MPI_SUCCESS); - app.makeInterComm(); - } - MPI_Barrier(world.comm); - runWorker(); // this fct will not return - } - - } // ::ospray::api -} // ::ospray - -int main(int ac, const char **av) -{ - ospray::mpi::workerMain(ac,av); -} diff --git a/modules/mpi/apps/CMakeLists.txt b/modules/mpi/apps/CMakeLists.txt new file mode 100644 index 0000000000..66f5be8b37 --- /dev/null +++ b/modules/mpi/apps/CMakeLists.txt @@ -0,0 +1,36 @@ +## ======================================================================== ## +## Copyright 2009-2017 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. ## +## ======================================================================== ## + +# Test app which generates data-parallel groups of spheres #################### + +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/apps/exampleViewer) + +IF (NOT WIN32)# Internal Compiler Error on MSVC12... + OSPRAY_CREATE_TEST(ospRandSphereTest + ospRandSphereTest.cpp + LINK + ospray + ospray_mpi_common + ospray_commandline + ) + OSPRAY_CREATE_TEST(ospRandSciVisTest + ospRandSciVisTest.cpp + LINK + ospray + ospray_mpi_common + ospray_commandline + ) +ENDIF() diff --git a/modules/mpi/apps/ospRandSciVisTest.cpp b/modules/mpi/apps/ospRandSciVisTest.cpp new file mode 100644 index 0000000000..e78edc3d2a --- /dev/null +++ b/modules/mpi/apps/ospRandSciVisTest.cpp @@ -0,0 +1,424 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// mpiCommon +#include "mpiCommon/MPICommon.h" +// public-ospray +#include "ospray/ospray_cpp/Camera.h" +#include "ospray/ospray_cpp/Data.h" +#include "ospray/ospray_cpp/Device.h" +#include "ospray/ospray_cpp/FrameBuffer.h" +#include "ospray/ospray_cpp/Renderer.h" +#include "ospray/ospray_cpp/TransferFunction.h" +#include "ospray/ospray_cpp/Volume.h" +// ospray apps +#include "common/commandline/CameraParser.h" +#include "widgets/imguiViewer.h" +// stl +#include + +#define RUN_LOCAL 0 + +/* This app demonstrates how to write a distributed scivis style + * renderer using the distributed MPI device. Note that because + * OSPRay uses sort-last compositing it is up to the user to ensure + * that the data distribution across the nodes is suitable. Specifically, + * each nodes' data must be convex and disjoint. This renderer only + * supports multiple volumes and geometries per-node, to ensure they're + * composited correctly you specify a list of bounding regions to the + * model, within these regions can be arbitrary volumes/geometries + * and each rank can have as many regions as needed. As long as the + * regions are disjoint/convex the data will be rendered correctly. + * In this example we set two regions on certain ranks just to produce + * a gap in the ranks volume to demonstrate how they work. + * + * In the case that you have geometry crossing the boundary of nodes + * and are replicating it on both nodes to render (ghost zones, etc.) + * the region will be used by the renderer to clip rays against allowing + * to split the object between the two nodes, with each rendering half. + * This will keep the regions rendered by each rank disjoint and thus + * avoid any artifacts. For example, if a sphere center is on the border + * between two nodes, each would render half the sphere and the halves + * would be composited to produce the final complete sphere in the image. + */ + +namespace ospRandSciVisTest { + + using namespace ospcommon; + + int numSpheresPerNode = 100; + float sphereRadius = 0.01f; + vec2i fbSize = vec2i(1024, 768); + int numFrames = 32; + bool runDistributed = true; + int logLevel = 0; + + //TODO: factor this into a reusable piece inside of ospcommon!!!!!! + // helper function to write the rendered image as PPM file + void writePPM(const std::string &fileName, + const int sizeX, const int sizeY, + const uint32_t *pixel) + { + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + auto *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); + } + + // Compute an X x Y x Z grid to have num bricks, + // only gives a nice grid for numbers with even factors since + // we don't search for factors of the number, we just try dividing by two + vec3i computeGrid(int num) + { + vec3i grid(1); + int axis = 0; + while (num % 2 == 0) { + grid[axis] *= 2; + num /= 2; + axis = (axis + 1) % 3; + } + if (num != 1) { + grid[axis] = num; + } + return grid; + } + + /* This function generates the rank's local geometry within its + * volume's bounding box. The bbox represents say its simulation + * or owned data region. + */ + ospray::cpp::Geometry makeSpheres(const box3f &bbox) + { + struct Sphere + { + vec3f org; + int colorID {0}; + }; + + auto numRanks = static_cast(mpicommon::numGlobalRanks()); + auto myRank = mpicommon::globalRank(); + + std::vector spheres(numSpheresPerNode); + + std::mt19937 rng; + rng.seed(std::random_device()()); + + // Generate spheres within this nodes volume, to keep the data disjoint. + // We also leave some buffer space on the boundaries to avoid clipping + // artifacts or needing duplication across nodes in the case a sphere + // crosses a boundary. Note: Since we don't communicated ghost regions + // among the nodes, we make sure not to generate any spheres which would + // be clipped. + std::uniform_real_distribution dist_x(bbox.lower.x + sphereRadius, + bbox.upper.x - sphereRadius); + std::uniform_real_distribution dist_y(bbox.lower.y + sphereRadius, + bbox.upper.y - sphereRadius); + std::uniform_real_distribution dist_z(bbox.lower.z + sphereRadius, + bbox.upper.z - sphereRadius); + + for (auto &s : spheres) { + s.org.x = dist_x(rng); + s.org.y = dist_y(rng); + s.org.z = dist_z(rng); + } + + ospray::cpp::Data sphere_data(numSpheresPerNode * sizeof(Sphere), + OSP_UCHAR, spheres.data()); + + + const float r = (numRanks - myRank) / numRanks; + const float b = myRank / numRanks; + const float g = myRank > numRanks / 2 ? 2 * r : 2 * b; + vec4f color(r, g, b, 1.f); + ospray::cpp::Data color_data(1, OSP_FLOAT4, &color); + + ospray::cpp::Geometry geom("spheres"); + geom.set("spheres", sphere_data); + geom.set("color", color_data); + geom.set("offset_colorID", int(sizeof(vec3f))); + geom.set("radius", sphereRadius); + geom.commit(); + + return geom; + } + + /* Generate this rank's volume data. The volumes are placed in + * cells of the grid computed in 'computeGrid' based on the number + * of ranks with each rank owning a specific cell in the gridding. + * The coloring is based on color-mapping the ranks id. + * The region occupied by the volume is then used to be the rank's + * overall region bounds and will be the bounding box for the + * generated geometry as well. + */ + std::pair makeVolume() + { + auto numRanks = static_cast(mpicommon::numGlobalRanks()); + auto myRank = mpicommon::globalRank(); + + ospray::cpp::TransferFunction transferFcn("piecewise_linear"); + const std::vector colors = { + vec3f(0, 0, 0.56), + vec3f(0, 0, 1), + vec3f(0, 1, 1), + vec3f(0.5, 1, 0.5), + vec3f(1, 1, 0), + vec3f(1, 0, 0), + vec3f(0.5, 0, 0) + }; + const std::vector opacities = {0.015, 0.015}; + ospray::cpp::Data colorsData(colors.size(), OSP_FLOAT3, colors.data()); + ospray::cpp::Data opacityData(opacities.size(), OSP_FLOAT, opacities.data()); + colorsData.commit(); + opacityData.commit(); + + const vec2f valueRange(static_cast(0), static_cast(numRanks)); + transferFcn.set("colors", colorsData); + transferFcn.set("opacities", opacityData); + transferFcn.set("valueRange", valueRange); + transferFcn.commit(); + + const vec3i volumeDims(128); + const vec3i grid = computeGrid(numRanks); + ospray::cpp::Volume volume("block_bricked_volume"); + volume.set("voxelType", "uchar"); + volume.set("dimensions", volumeDims); + volume.set("transferFunction", transferFcn); + + const vec3f gridSpacing = vec3f(1.f) / (vec3f(grid) * vec3f(volumeDims)); + volume.set("gridSpacing", gridSpacing); + + const vec3i brickId(myRank % grid.x, (myRank / grid.x) % grid.y, myRank / (grid.x * grid.y)); + const vec3f gridOrigin = vec3f(brickId) * gridSpacing * vec3f(volumeDims); + volume.set("gridOrigin", gridOrigin); + + std::vector volumeData(volumeDims.x * volumeDims.y * volumeDims.z, 0); + for (size_t i = 0; i < volumeData.size(); ++i) { + volumeData[i] = myRank; + } + volume.setRegion(volumeData.data(), vec3i(0), volumeDims); + volume.commit(); + + auto bbox = box3f(gridOrigin, gridOrigin + vec3f(1.f) / vec3f(grid)); + return std::make_pair(volume, bbox); + } + + void setupCamera(ospray::cpp::Camera &camera, box3f worldBounds) + { + vec3f center = ospcommon::center(worldBounds); + vec3f diag = worldBounds.size(); + diag = max(diag,vec3f(0.3f*length(diag))); + vec3f from = center - .85f*vec3f(-.6*diag.x,-1.2f*diag.y,.8f*diag.z); + vec3f dir = center - from; + + camera.set("pos", from); + camera.set("dir", dir); + camera.set("aspect", static_cast(fbSize.x)/fbSize.y); + + camera.commit(); + } + + void parseCommandLine(int ac, const char **av) + { + for (int i = 0; i < ac; ++i) { + std::string arg = av[i]; + if (arg == "-w") { + fbSize.x = std::atoi(av[++i]); + } else if (arg == "-h") { + fbSize.y = std::atoi(av[++i]); + } else if (arg == "-spn" || arg == "--spheres-per-node") { + numSpheresPerNode = std::atoi(av[++i]); + } else if (arg == "-r" || arg == "--radius") { + sphereRadius = std::atof(av[++i]); + } else if (arg == "-nf" || arg == "--num-frames") { + numFrames = std::atoi(av[++i]); + } else if (arg == "-l" || arg == "--local") { + runDistributed = false; + } else if (arg == "--log") { + logLevel = std::atoi(av[++i]); + } + } + } + + /* Manually set up the OSPRay device. In MPI distributed mode + * we use the 'mpi_distributed' renderer, which allows each + * rank to make separate independent OSPRay calls locally. + * The model created by this device will handle coordinating + * the regions of data and the renderer used in the distributed + * case 'mpi_raycast' knows how to use this information to + * perform sort-last compositing rendering of the data. + */ + void initialize_ospray() + { + ospray::cpp::Device device; + + if (runDistributed) { + ospLoadModule("mpi"); + device = ospray::cpp::Device("mpi_distributed"); + device.set("masterRank", 0); + device.set("logLevel", logLevel); + device.commit(); + device.setCurrent(); + } else { + device = ospray::cpp::Device(); + device.set("logLevel", logLevel); + device.commit(); + device.setCurrent(); + } + + ospDeviceSetStatusFunc(device.handle(), + [](const char *msg) { + std::cerr << msg; + }); + } + + extern "C" int main(int ac, const char **av) + { + parseCommandLine(ac, av); + + initialize_ospray(); + + ospray::cpp::Model model; + auto volume = makeVolume(); + model.addVolume(volume.first); + + // Generate spheres within the bounds of the volume + auto spheres = makeSpheres(volume.second); + model.addGeometry(spheres); + + // We must use the global world bounds, not our local bounds + // when computing the automatically picked camera position. + box3f worldBounds(vec3f(0), vec3f(1)); + + /* The regions listing specifies the data regions that this rank owns + * and is responsible for rendering. All volumes and geometry on the rank + * should be contained within these bounds and will be clipped against them. + * In the case of ghost regions or splitting geometry across the region border + * it's up to the user to ensure the other rank also has the geometry being + * split and renders the correct region bounds. The region data is specified + * as an OSPData of OSP_FLOAT3 to pass the lower and upper corners of each + * regions bounding box. + * + * On some ranks we add some additional regions to clip the volume + * and make some gaps, just to show usage and test multiple regions per-rank + */ + std::vector regions{volume.second}; + bool setGap = false; + if (mpicommon::numGlobalRanks() % 2 == 0) { + setGap = mpicommon::globalRank() % 3 == 0; + } else { + setGap = mpicommon::globalRank() % 2 == 0; + } + if (setGap) { + const float step = (regions[0].upper.x - regions[0].lower.x) / 4.0; + const vec3f low = regions[0].lower; + const vec3f hi = regions[0].upper; + regions[0].upper.x = low.x + step; + regions.push_back(box3f(vec3f(low.x + step * 3, low.y, low.z), + vec3f(low.x + step * 4, hi.y, hi.z))); + } + ospray::cpp::Data regionData(regions.size() * 2, OSP_FLOAT3, + regions.data()); + model.set("regions", regionData); + model.commit(); + + DefaultCameraParser cameraClParser; + cameraClParser.parse(ac, av); + auto camera = cameraClParser.camera(); + setupCamera(camera, worldBounds); + + // In the distributed mode we use the 'mpi_raycast' renderer which + // knows how to read the region information from the model and render + // the distributed data. + ospray::cpp::Renderer renderer; + if (runDistributed) { + renderer = ospray::cpp::Renderer("mpi_raycast"); + } else { + renderer = ospray::cpp::Renderer("raycast"); + } + renderer.set("world", model); + renderer.set("model", model); + renderer.set("camera", camera); + renderer.set("bgColor", vec3f(0.02)); + renderer.commit(); + + ospray::cpp::FrameBuffer fb(fbSize,OSP_FB_SRGBA,OSP_FB_COLOR|OSP_FB_ACCUM); + fb.clear(OSP_FB_ACCUM); + + if (runDistributed) { + + mpicommon::world.barrier(); + + auto frameStartTime = ospcommon::getSysTime(); + + for (int i = 0; i < numFrames; ++i) { + if (mpicommon::IamTheMaster()) + std::cout << "rendering frame " << i << std::endl; + + renderer.renderFrame(fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } + + double seconds = ospcommon::getSysTime() - frameStartTime; + + // Only the OSPRay master rank will have the final framebuffer which + // can be saved out or displayed to the user, the others only store + // the tiles which they composite. + if (mpicommon::IamTheMaster()) { + auto *lfb = (uint32_t*)fb.map(OSP_FB_COLOR); + writePPM("randomSciVisTestDistributed.ppm", fbSize.x, fbSize.y, lfb); + fb.unmap(lfb); + std::cout << "\noutput: 'randomSciVisTestDistributed.ppm'" << std::endl; + std::cout << "\nrendered " << numFrames << " frames at an avg rate of " + << numFrames / seconds << " frames per second" << std::endl; + } + + mpicommon::world.barrier(); + + } else { + + auto frameStartTime = ospcommon::getSysTime(); + + for (int i = 0; i < numFrames; ++i) { + std::cout << "rendering frame " << i << std::endl; + + renderer.renderFrame(fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } + + double seconds = ospcommon::getSysTime() - frameStartTime; + + auto *lfb = (uint32_t*)fb.map(OSP_FB_COLOR); + writePPM("randomSciVisTestLocal.ppm", fbSize.x, fbSize.y, lfb); + fb.unmap(lfb); + std::cout << "\noutput: 'randomSciVisTestLocal.ppm'" << std::endl; + std::cout << "\nrendered " << numFrames << " frames at an avg rate of " + << numFrames / seconds << " frames per second" << std::endl; + + } + + return 0; + } + +} + diff --git a/modules/mpi/apps/ospRandSphereTest.cpp b/modules/mpi/apps/ospRandSphereTest.cpp new file mode 100644 index 0000000000..2ec995afca --- /dev/null +++ b/modules/mpi/apps/ospRandSphereTest.cpp @@ -0,0 +1,283 @@ +// ======================================================================== // +// Copyright 2009-2016 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// mpiCommon +#include "mpiCommon/MPICommon.h" +// public-ospray +#include "ospray/ospray_cpp/Camera.h" +#include "ospray/ospray_cpp/Data.h" +#include "ospray/ospray_cpp/Device.h" +#include "ospray/ospray_cpp/FrameBuffer.h" +#include "ospray/ospray_cpp/Renderer.h" +#include "ospray/ospray_cpp/TransferFunction.h" +// ospray apps +#include "common/commandline/CameraParser.h" +#include "widgets/imguiViewer.h" +// stl +#include + +#define RUN_LOCAL 0 + +namespace ospRandSphereTest { + + using namespace ospcommon; + + int numSpheresPerNode = 100; + float sphereRadius = 0.01f; + float sceneLowerBound = 0.f; + float sceneUpperBound = 1.f; + vec2i fbSize = vec2i(1024, 768); + int numFrames = 32; + bool runDistributed = true; + + //TODO: factor this into a reusable piece inside of ospcommon!!!!!! + // helper function to write the rendered image as PPM file + void writePPM(const std::string &fileName, + const int sizeX, const int sizeY, + const uint32_t *pixel) + { + FILE *file = fopen(fileName.c_str(), "wb"); + fprintf(file, "P6\n%i %i\n255\n", sizeX, sizeY); + unsigned char *out = (unsigned char *)alloca(3*sizeX); + for (int y = 0; y < sizeY; y++) { + auto *in = (const unsigned char *)&pixel[(sizeY-1-y)*sizeX]; + for (int x = 0; x < sizeX; x++) { + out[3*x + 0] = in[4*x + 0]; + out[3*x + 1] = in[4*x + 1]; + out[3*x + 2] = in[4*x + 2]; + } + fwrite(out, 3*sizeX, sizeof(char), file); + } + fprintf(file, "\n"); + fclose(file); + } + + // Compute an X x Y x Z grid to have num bricks, + // only gives a nice grid for numbers with even factors since + // we don't search for factors of the number, we just try dividing by two + vec3i computeGrid(int num) + { + vec3i grid(1); + int axis = 0; + while (num % 2 == 0) { + grid[axis] *= 2; + num /= 2; + axis = (axis + 1) % 3; + } + if (num != 1) { + grid[axis] = num; + } + return grid; + } + + std::pair makeSpheres() + { + struct Sphere + { + vec3f org; + int colorID {0}; + }; + + std::vector spheres(numSpheresPerNode); + + std::mt19937 rng; + rng.seed(std::random_device()()); + std::uniform_real_distribution dist(sceneLowerBound, + sceneUpperBound); + + for (auto &s : spheres) { + s.org.x = dist(rng); + s.org.y = dist(rng); + s.org.z = dist(rng); + } + + ospray::cpp::Data sphere_data(numSpheresPerNode * sizeof(Sphere), + OSP_UCHAR, spheres.data()); + + auto numRanks = static_cast(mpicommon::numGlobalRanks()); + auto myRank = mpicommon::globalRank(); + +#if 1 + float r = (numRanks - myRank) / numRanks; + float b = myRank / numRanks; + float g = myRank > numRanks / 2 ? 2 * r : 2 * b; +#else + float normRank = myRank / numRanks; + float r = 2 * (1.f - normRank - 0.5f); + if (r < 0.f) r = 0.f; + float b = 2 * (normRank - 0.5f); + if (b < 0.f) b = 0.f; + float g = myRank < numRanks / 2 ? 1.f - r : 1.f - b; + g *= 0.5f; +#endif + + vec4f color(r, g, b, 1.f); + + ospray::cpp::Data color_data(1, OSP_FLOAT4, &color); + + ospray::cpp::Geometry geom("spheres"); + geom.set("spheres", sphere_data); + geom.set("color", color_data); + geom.set("offset_colorID", int(sizeof(vec3f))); + geom.set("radius", sphereRadius); + geom.commit(); + + //NOTE: all ranks will have the same bounding box, no matter what spheres + // were randomly generated + auto bbox = box3f(vec3f(sceneLowerBound), vec3f(sceneUpperBound)); + + return std::make_pair(geom, bbox); + } + + void setupCamera(ospray::cpp::Camera &camera, box3f worldBounds) + { + vec3f center = ospcommon::center(worldBounds); + vec3f diag = worldBounds.size(); + diag = max(diag,vec3f(0.3f*length(diag))); + vec3f from = center - .85f*vec3f(-.6*diag.x,-1.2f*diag.y,.8f*diag.z); + vec3f dir = center - from; + + camera.set("pos", from); + camera.set("dir", dir); + camera.set("aspect", static_cast(fbSize.x)/fbSize.y); + + camera.commit(); + } + + void parseCommandLine(int ac, const char **av) + { + for (int i = 0; i < ac; ++i) { + std::string arg = av[i]; + if (arg == "-w") { + fbSize.x = std::atoi(av[++i]); + } else if (arg == "-h") { + fbSize.y = std::atoi(av[++i]); + } else if (arg == "-spn" || arg == "--spheres-per-node") { + numSpheresPerNode = std::atoi(av[++i]); + } else if (arg == "-r" || arg == "--radius") { + sphereRadius = std::atof(av[++i]); + } else if (arg == "-nf" || arg == "--num-frames") { + numFrames = std::atoi(av[++i]); + } else if (arg == "-l" || arg == "--local") { + runDistributed = false; + } + } + } + + void initialize_ospray() + { + ospray::cpp::Device device; + + if (runDistributed) { + ospLoadModule("mpi"); + device = ospray::cpp::Device("mpi_distributed"); + device.set("masterRank", 0); + device.commit(); + device.setCurrent(); + } else { + device = ospray::cpp::Device(); + device.commit(); + device.setCurrent(); + } + + ospDeviceSetStatusFunc(device.handle(), + [](const char *msg) { + std::cerr << msg; + }); + } + + extern "C" int main(int ac, const char **av) + { + parseCommandLine(ac, av); + + initialize_ospray(); + + ospray::cpp::Model model; + auto spheres = makeSpheres(); + model.addGeometry(spheres.first); + model.commit(); + + DefaultCameraParser cameraClParser; + cameraClParser.parse(ac, av); + auto camera = cameraClParser.camera(); + setupCamera(camera, spheres.second); + + ospray::cpp::Renderer renderer; + if (runDistributed) { + renderer = ospray::cpp::Renderer("mpi_raycast"); + } else { + renderer = ospray::cpp::Renderer("raycast"); + } + renderer.set("world", model); + renderer.set("model", model); + renderer.set("camera", camera); + renderer.set("bgColor", vec3f(0.01f, 0.01f, 0.01f)); + renderer.commit(); + + ospray::cpp::FrameBuffer fb(fbSize,OSP_FB_SRGBA,OSP_FB_COLOR|OSP_FB_ACCUM); + fb.clear(OSP_FB_ACCUM); + + if (runDistributed) { + + mpicommon::world.barrier(); + + auto frameStartTime = ospcommon::getSysTime(); + + for (int i = 0; i < numFrames; ++i) { + if (mpicommon::IamTheMaster()) + std::cout << "rendering frame " << i << std::endl; + + renderer.renderFrame(fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } + + double seconds = ospcommon::getSysTime() - frameStartTime; + + if (mpicommon::IamTheMaster()) { + auto *lfb = (uint32_t*)fb.map(OSP_FB_COLOR); + writePPM("randomSphereTestDistributed.ppm", fbSize.x, fbSize.y, lfb); + fb.unmap(lfb); + std::cout << "\noutput: 'randomSphereTestDistributed.ppm'" << std::endl; + std::cout << "\nrendered " << numFrames << " frames at an avg rate of " + << numFrames / seconds << " frames per second" << std::endl; + } + + mpicommon::world.barrier(); + + } else { + + auto frameStartTime = ospcommon::getSysTime(); + + for (int i = 0; i < numFrames; ++i) { + std::cout << "rendering frame " << i << std::endl; + + renderer.renderFrame(fb, OSP_FB_COLOR | OSP_FB_ACCUM); + } + + double seconds = ospcommon::getSysTime() - frameStartTime; + + auto *lfb = (uint32_t*)fb.map(OSP_FB_COLOR); + writePPM("randomSphereTestLocal.ppm", fbSize.x, fbSize.y, lfb); + fb.unmap(lfb); + std::cout << "\noutput: 'randomSphereTestLocal.ppm'" << std::endl; + std::cout << "\nrendered " << numFrames << " frames at an avg rate of " + << numFrames / seconds << " frames per second" << std::endl; + + } + + return 0; + } + +} // ::ospRandSphereTest diff --git a/modules/mpi/common/DistributedModel.cpp b/modules/mpi/common/DistributedModel.cpp new file mode 100644 index 0000000000..a371db9f33 --- /dev/null +++ b/modules/mpi/common/DistributedModel.cpp @@ -0,0 +1,118 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include +#include +// ospray +#include "api/Device.h" +#include "DistributedModel.h" +#include "mpiCommon/MPICommon.h" +#include "Messaging.h" +#include "common/Data.h" +// ispc exports +#include "DistributedModel_ispc.h" + +namespace ospray { + namespace mpi { + + extern "C" void *ospray_getEmbreeDevice() + { + return api::Device::current->embreeDevice; + } + + DistributedModel::DistributedModel() + { + managedObjectType = OSP_MODEL; + this->ispcEquivalent = ispc::DistributedModel_create(this); + } + + std::string DistributedModel::toString() const + { + return "ospray::mpi::DistributedModel"; + } + + void DistributedModel::commit() + { + othersRegions.clear(); + + // TODO: We may need to override the ISPC calls made + // to the Model or customize the model struct on the ISPC + // side. In which case we need some ISPC side inheritence + // for the model type. Currently the code is actually identical. + Model::commit(); + // Send my bounding boxes to other nodes, recieve theirs for a + // "full picture" of what geometries live on what nodes + Data *regionData = getParamData("regions"); + + // The box3f data is sent as data of FLOAT3 items + // TODO: It's a little awkward to copy the boxes again like this, maybe + // can re-thinkg the send side of the bcast call? One that takes + // a ptr and a size since we know we won't be writing out to it? + // TODO: For now it doesn't matter that we don't know who owns the + // other boxes, just that we know they exist and their bounds, and that + // they aren't ours. + if (regionData) { + box3f *boxes = reinterpret_cast(regionData->data); + myRegions = std::vector(boxes, boxes + regionData->numItems / 2); + } + + // If the user hasn't set any regions, there's an implicit infinitely + // large region box we can place around the entire world. + if (myRegions.empty()) { + postStatusMsg("No regions found, making implicit " + "infinitely large region", 1); + myRegions.push_back(box3f(vec3f(neg_inf), vec3f(pos_inf))); + } + + for (size_t i = 0; i < mpicommon::numGlobalRanks(); ++i) { + if (i == mpicommon::globalRank()) { + messaging::bcast(i, myRegions); + } else { + std::vector recv; + messaging::bcast(i, recv); + std::copy(recv.begin(), recv.end(), + std::back_inserter(othersRegions)); + } + } + + if (logLevel() >= 1) { + const bool asyncWasRunning = messaging::asyncMessagingEnabled(); + messaging::disableAsyncMessaging(); + + // TODO: WILL: Just for making the debug log more readable, + // this will NOT stick around + for (size_t i = 0; i < mpicommon::numGlobalRanks(); ++i) { + if (i == mpicommon::globalRank()) { + postStatusMsg(1) << "Rank " << mpicommon::globalRank() + << ": Got regions from others {"; + for (const auto &b : othersRegions) { + postStatusMsg(1) << "\t" << b << ","; + } + postStatusMsg(1) << "}"; + } + + mpicommon::world.barrier(); + } + + if (asyncWasRunning) { + postStatusMsg("Async was running, re-enabling", 1); + messaging::enableAsyncMessaging(); + } + } + } + + } // ::ospray::mpi +} // ::ospray diff --git a/apps/glutViewer/ScriptedOSPGlutViewer.h b/modules/mpi/common/DistributedModel.h similarity index 65% rename from apps/glutViewer/ScriptedOSPGlutViewer.h rename to modules/mpi/common/DistributedModel.h index 860f76634a..f066d19065 100644 --- a/apps/glutViewer/ScriptedOSPGlutViewer.h +++ b/modules/mpi/common/DistributedModel.h @@ -16,31 +16,34 @@ #pragma once -#include +// ospray stuff +#include "geometry/Geometry.h" +#include "volume/Volume.h" +#include "common/Model.h" -#include "common/widgets/OSPGlutViewer.h" -#include "GlutViewerScriptHandler.h" +// stl +#include -namespace ospray { - - class ScriptedOSPGlutViewer : public OSPGlutViewer - { - public: +// embree +#include "embree2/rtcore.h" - ScriptedOSPGlutViewer(const std::deque &worldBounds, - std::deque model, cpp::Renderer renderer, - cpp::Camera camera, std::string scriptFileName = ""); - - int getFrameID() const; +namespace ospray { + namespace mpi { - private: + struct DistributedModel : public Model + { + DistributedModel(); + virtual ~DistributedModel() = default; - void display() override; - void keypress(char key, const ospcommon::vec2i &where) override; + virtual std::string toString() const override; - GlutViewerScriptHandler scriptHandler; + // commit synchronizes the distributed models between processes + // so that ranks know how many tiles to expect for sort-last + // compositing. + virtual void commit() override; - std::atomic frameID; - }; + std::vector myRegions, othersRegions; + }; -}// namespace ospray + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/common/DistributedModel.ispc b/modules/mpi/common/DistributedModel.ispc new file mode 100644 index 0000000000..80a4bcd7dc --- /dev/null +++ b/modules/mpi/common/DistributedModel.ispc @@ -0,0 +1,87 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "common/Model.ih" +#include "embree2/rtcore_scene.isph" + +typedef uniform Geometry *uniform uniGeomPtr; +typedef uniform Material *uniform uniMaterialPtr; +typedef uniform Volume *uniform uniVolumePtr; + +export void *uniform DistributedModel_create(void *uniform cppE) +{ + uniform Model *uniform model = uniform new uniform Model; + model->cppEquivalent = cppE; + model->embreeSceneHandle = NULL; + model->geometry = NULL; + model->volumes = NULL; + return (void *uniform)model; +} + +export void DistributedModel_init(void *uniform _model, + void *uniform embreeDevice, + uniform int32 numGeometries, + uniform int32 numVolumes) +{ + uniform Model *uniform model = (uniform Model *uniform)_model; + if (model->embreeSceneHandle) + rtcDeleteScene(model->embreeSceneHandle); + + uniform RTCSceneFlags scene_flags = RTC_SCENE_STATIC | RTC_SCENE_HIGH_QUALITY; + + uniform RTCAlgorithmFlags traversal_flags = + RTC_INTERSECT_UNIFORM | RTC_INTERSECT_VARYING; + + model->embreeSceneHandle = rtcDeviceNewScene((RTCDevice)embreeDevice, + scene_flags, + traversal_flags); + + if (model->geometry) delete[] model->geometry; + model->geometryCount = numGeometries; + if (numGeometries > 0) + model->geometry = uniform new uniform uniGeomPtr[numGeometries]; + else + model->geometry = NULL; + if (model->volumes) delete[] model->volumes; + model->volumeCount = numVolumes; + if (numVolumes > 0) + model->volumes = uniform new uniform uniVolumePtr[numVolumes]; + else + model->volumes = NULL; +} + +export void *uniform DistributedModel_getEmbreeSceneHandle(void *uniform _model) +{ + uniform Model *uniform model = (uniform Model *uniform)_model; + return (void *uniform)model->embreeSceneHandle; +} + +export void DistributedModel_setGeometry(void *uniform _model, + uniform int32 geomID, + void *uniform _geom) +{ + uniform Model *uniform model = (uniform Model *uniform)_model; + uniform Geometry *uniform geom = (uniform Geometry *uniform)_geom; + model->geometry[geomID] = geom; +} + +export void DistributedModel_setVolume(void *uniform pointer, + uniform int32 index, + void *uniform volume) +{ + uniform Model *uniform model = (uniform Model *uniform) pointer; + model->volumes[index] = (uniform Volume *uniform) volume; +} diff --git a/modules/mpi/common/Messaging.cpp b/modules/mpi/common/Messaging.cpp new file mode 100644 index 0000000000..74669d2057 --- /dev/null +++ b/modules/mpi/common/Messaging.cpp @@ -0,0 +1,112 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "Messaging.h" +// stl +#include + +namespace ospray { + namespace mpi { + namespace messaging { + + using namespace mpicommon; + + // Internal maml message handler for all of OSPRay ////////////////////// + + struct ObjectMessageHandler : maml::MessageHandler + { + void registerMessageListener(int handleObjID, + maml::MessageHandler *listener); + + void incoming(const std::shared_ptr &message) override; + + // Data members // + + std::unordered_map objectListeners; + }; + + // Inlined ObjectMessageHandler definitions ///////////////////////////// + + inline void ObjectMessageHandler::registerMessageListener( + int handleObjID, + maml::MessageHandler *listener + ) + { + if (objectListeners.find(handleObjID) != objectListeners.end()) + postStatusMsg() << "WARNING: overwriting an existing listener!"; + + objectListeners[handleObjID] = listener; + } + + inline void ObjectMessageHandler::incoming( + const std::shared_ptr &message + ) + { + auto obj = objectListeners.find(message->tag); + if (obj != objectListeners.end()) { + obj->second->incoming(message); + } else { + postStatusMsg() << "No destination for incoming message!"; + } + } + + // Singleton instance (hidden) and helper creation function ///////////// + + std::unique_ptr createHandler() + { + auto instance = ospcommon::make_unique(); + maml::registerHandlerFor(world.comm, instance.get()); + return instance; + } + + static std::unique_ptr handler; + + // ospray::mpi::messaging definitions /////////////////////////////////// + + void registerMessageListener(int handleObjID, + maml::MessageHandler *listener) + { + if (!handler.get()) + handler = createHandler(); + + handler->registerMessageListener(handleObjID, listener); + } + + void enableAsyncMessaging() + { + maml::start(); + } + + void sendTo(int globalRank, ObjectHandle object, + std::shared_ptr msg) + { + msg->tag = object.objID(); + maml::sendTo(world.comm, globalRank, msg); + } + + bool asyncMessagingEnabled() + { + return maml::isRunning(); + } + + void disableAsyncMessaging() + { + maml::stop(); + } + + } // ::ospray::mpi::messaging + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/common/Messaging.h b/modules/mpi/common/Messaging.h new file mode 100644 index 0000000000..b41c2e5769 --- /dev/null +++ b/modules/mpi/common/Messaging.h @@ -0,0 +1,87 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "ospcommon/networking/BufferedDataStreaming.h" +// mpiCommon +#include "mpiCommon/MPICommon.h" +#include "mpiCommon/MPIBcastFabric.h" +// maml +#include "maml/maml.h" +// ospray +#include "ospray/common/ObjectHandle.h" + +namespace ospray { + namespace mpi { + namespace messaging { + + // async point messaging interface ////////////////////////////////////// + + void registerMessageListener(int handleObjID, + maml::MessageHandler *listener); + + void enableAsyncMessaging(); + + bool asyncMessagingEnabled(); + + void sendTo(int globalRank, ObjectHandle object, + std::shared_ptr msg); + + void disableAsyncMessaging(); + + // collective messaging interface /////////////////////////////////////// + + // Broadcast some data, if rank == rootGlobalRank we send data, otherwise + // the received data will be put in data. + // TODO: Handling non-world groups? I'm not sure how + // we could set up object handlers for the bcast or collective layer, + // since it's inherently a global sync operation. If we async dispatched + // collectives then maybe? But how could you ensure the queue of + // collectives seen by all nodes was the same order? You might + // mismatch collectives or hang trying to dispatch + // the wrong ones? + template + inline void bcast(const int rootGlobalRank, T &data) + { + using namespace mpicommon; + const bool asyncWasRunning = asyncMessagingEnabled(); + disableAsyncMessaging(); + + MPIBcastFabric fabric(world, rootGlobalRank, rootGlobalRank); + + if (globalRank() == rootGlobalRank) { + networking::BufferedWriteStream stream(fabric); + stream << data; + stream.flush(); + } else { + networking::BufferedReadStream stream(fabric); + stream >> data; + } + + // TODO: What if some other thread re-enables async messaging during the + // bcast? Can we like lock it out or something until we're done? Or + // send the bcasts through the same messaging layer so we know for + // sure no other MPI calls are being made? + if (asyncWasRunning) { + postStatusMsg("Async was running, re-enabling", 1); + enableAsyncMessaging(); + } + } + + } // ::ospray::mpi::messaging + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/common/OSPWork.cpp b/modules/mpi/common/OSPWork.cpp index 73cbc50e0a..97917302bc 100644 --- a/modules/mpi/common/OSPWork.cpp +++ b/modules/mpi/common/OSPWork.cpp @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2016 Intel Corporation // +// Copyright 2009-2017 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -22,181 +22,251 @@ #include "mpi/fb/DistributedFrameBuffer.h" #include "mpi/render/MPILoadBalancer.h" +#include "common/Data.h" +#include "common/Library.h" +#include "common/Model.h" +#include "geometry/TriangleMesh.h" +#include "texture/Texture2D.h" + namespace ospray { namespace mpi { namespace work { -#define REGISTER_WORK_UNIT(W) W::TAG, make_work_unit - - void initWorkMap() - { - Work::WORK_MAP = Work::WorkMap{ - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - { REGISTER_WORK_UNIT(NewObject) }, - - { REGISTER_WORK_UNIT(NewRendererObject) }, - { REGISTER_WORK_UNIT(NewRendererObject) }, - - { REGISTER_WORK_UNIT(NewData) }, - { REGISTER_WORK_UNIT(NewTexture2d) }, - - { REGISTER_WORK_UNIT(CommitObject) }, - { REGISTER_WORK_UNIT(CommandRelease) }, - - { REGISTER_WORK_UNIT(LoadModule) }, - - { REGISTER_WORK_UNIT(AddObject) }, - { REGISTER_WORK_UNIT(AddObject) }, - { REGISTER_WORK_UNIT(RemoveObject) }, - { REGISTER_WORK_UNIT(RemoveObject) }, - - { REGISTER_WORK_UNIT(CreateFrameBuffer) }, - { REGISTER_WORK_UNIT(ClearFrameBuffer) }, - { REGISTER_WORK_UNIT(RenderFrame) }, - - { REGISTER_WORK_UNIT(SetRegion) }, - { REGISTER_WORK_UNIT(SetPixelOp) }, - - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - { REGISTER_WORK_UNIT(SetParam) }, - - { REGISTER_WORK_UNIT(RemoveParam) }, - - { REGISTER_WORK_UNIT(CommandFinalize) } - }; - } - -#undef REGISTER_WORK_UNIT - - // All the tags so they can be linked in properly - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - const size_t NewObjectTag::TAG; - template - const size_t NewObject::TAG; - // should they init here or in the header? Header probably? - const size_t NewRendererObjectTag::TAG; - const size_t NewRendererObjectTag::TAG; - template - const size_t NewRendererObject::TAG; - const size_t NewData::TAG; - const size_t NewTexture2d::TAG; - const size_t SetRegion::TAG; - const size_t CommitObject::TAG; - const size_t ClearFrameBuffer::TAG; - const size_t RenderFrame::TAG; - const size_t AddObjectTag::TAG; - const size_t AddObjectTag::TAG; - template - const size_t AddObject::TAG; - const size_t RemoveObjectTag::TAG; - const size_t RemoveObjectTag::TAG; - template - const size_t RemoveObject::TAG; - const size_t CreateFrameBuffer::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - const size_t ParamTag::TAG; - template - const size_t SetParam::TAG; - const size_t SetParam::TAG; - const size_t SetParam::TAG; - const size_t RemoveParam::TAG; - const size_t SetPixelOp::TAG; - const size_t CommandRelease::TAG; - const size_t LoadModule::TAG; - const size_t CommandFinalize::TAG; + void registerOSPWorkItems(WorkTypeRegistry ®istry) + { + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit(registry); - template<> - void NewObject::run() { - Renderer *renderer = Renderer::createRenderer(type.c_str()); - if (!renderer) { - throw std::runtime_error("unknown renderer type '" + type + "'"); + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit(registry); + + registerWorkUnit(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + registerWorkUnit>(registry); + + registerWorkUnit(registry); + + registerWorkUnit(registry); + } + + // ospCommit //////////////////////////////////////////////////////////// + + CommitObject::CommitObject(ObjectHandle handle) + : handle(handle) + {} + + void CommitObject::run() + { + ManagedObject *obj = handle.lookup(); + if (obj) { + obj->commit(); + } else { + throw std::runtime_error("Error: rank " + + std::to_string(mpicommon::world.rank) + + " did not have object to commit!"); + } + // TODO: Work units should not be directly making MPI calls. + // What should be responsible for this barrier? + // MPI_Barrier(MPI_COMM_WORLD); + + /// iw: nah, perfectly OK to do MPI calls, as long as they + /// don't get into each other's ways, nad make sure there's no + /// cyclical dependencies (ie, that the server unit actually + /// does get flushed etcpp) + + mpicommon::app.barrier(); + } + + void CommitObject::runOnMaster() + { + if (handle.defined()) { + ManagedObject *obj = handle.lookup(); + if (dynamic_cast(obj)) { + obj->commit(); + } } - handle.assign(renderer); + mpicommon::worker.barrier(); } - template<> - void NewObject::runOnMaster() { + + void CommitObject::serialize(WriteStream &b) const + { + b << (int64)handle; + } + + void CommitObject::deserialize(ReadStream &b) + { + b >> handle.i64; + } + + // ospNewFrameBuffer //////////////////////////////////////////////////// + + CreateFrameBuffer::CreateFrameBuffer(ObjectHandle handle, + vec2i dimensions, + OSPFrameBufferFormat format, + uint32 channels) + : handle(handle), + dimensions(dimensions), + format(format), + channels(channels) + { + } + + void CreateFrameBuffer::run() + { + const bool hasDepthBuffer = channels & OSP_FB_DEPTH; + const bool hasAccumBuffer = channels & OSP_FB_ACCUM; + const bool hasVarianceBuffer = channels & OSP_FB_VARIANCE; + + assert(dimensions.x > 0); + assert(dimensions.y > 0); + + FrameBuffer *fb + = new DistributedFrameBuffer(dimensions, handle, + format, hasDepthBuffer, + hasAccumBuffer, hasVarianceBuffer); + fb->refInc(); + handle.assign(fb); + } + + void CreateFrameBuffer::runOnMaster() + { run(); } - template<> - void NewObject::run() { - Model *model = new Model; - handle.assign(model); + + void CreateFrameBuffer::serialize(WriteStream &b) const + { + b << (int64)handle << dimensions << (int32)format << channels; } - template<> - void NewObject::run() { - Geometry *geometry = Geometry::createGeometry(type.c_str()); - if (!geometry) { - throw std::runtime_error("unknown geometry type '" + type + "'"); - } - // TODO: Why is this manual reference increment needed!? - // is it to keep the object alive until ospRelease is called - // since in the distributed mode no app has a reference to this object? - geometry->refInc(); - handle.assign(geometry); + + void CreateFrameBuffer::deserialize(ReadStream &b) + { + int32 fmt; + b >> handle.i64 >> dimensions >> fmt >> channels; + format = (OSPFrameBufferFormat)fmt; } + + // ospLoadModule //////////////////////////////////////////////////////// + + LoadModule::LoadModule(const std::string &name) + : name(name) + {} + + void LoadModule::run() + { + errorCode = loadLocalModule(name); + } + + void LoadModule::runOnMaster() + { + run(); + } + + void LoadModule::serialize(WriteStream &b) const + { + b << name; + } + + void LoadModule::deserialize(ReadStream &b) + { + b >> name; + } + + // ospSetParam ////////////////////////////////////////////////////////// + template<> - void NewObject::run() { - Camera *camera = Camera::createCamera(type.c_str()); - Assert(camera); - handle.assign(camera); + void SetParam::run() + { + ManagedObject *obj = handle.lookup(); + Assert(obj); + obj->findParam(name.c_str(), true)->set(val.c_str()); } + template<> - void NewObject::run() { - Volume *volume = Volume::createInstance(type.c_str()); - if (!volume) { - throw std::runtime_error("unknown volume type '" + type + "'"); + void SetParam::runOnMaster() + { + if (!handle.defined()) + return; + + ManagedObject *obj = handle.lookup(); + if (dynamic_cast(obj) || dynamic_cast(obj)) { + obj->findParam(name.c_str(), true)->set(val.c_str()); } - volume->refInc(); - handle.assign(volume); } + + // ospSetMaterial /////////////////////////////////////////////////////// + + void SetMaterial::run() + { + Geometry *geom = (Geometry*)handle.lookup(); + Material *mat = (Material*)material.lookup(); + Assert(geom); + Assert(mat); + /* might we worthwhile doing a dyncast here to check if that + is actually a proper geometry .. */ + geom->setMaterial(mat); + } + + // ospNewRenderer /////////////////////////////////////////////////////// + template<> - void NewObject::runOnMaster() { + void NewRenderer::runOnMaster() + { run(); } + + // ospNewVolume ///////////////////////////////////////////////////////// + template<> - void NewObject::run() { - TransferFunction *tfn = TransferFunction::createInstance(type.c_str()); - if (!tfn) { - throw std::runtime_error("unknown transfer functon type '" + type + "'"); - } - tfn->refInc(); - handle.assign(tfn); + void NewVolume::runOnMaster() + { + run(); } + + // ospNewModel ////////////////////////////////////////////////////////// + template<> - void NewObject::run() { - PixelOp *pixelOp = PixelOp::createPixelOp(type.c_str()); - if (!pixelOp) { - throw std::runtime_error("unknown pixel op type '" + type + "'"); - } - handle.assign(pixelOp); + void NewModel::run() + { + auto *model = new Model; + handle.assign(model); } - template<> - void NewRendererObject::run() { + // ospNewMaterial /////////////////////////////////////////////////////// + + void NewMaterial::run() + { Renderer *renderer = (Renderer*)rendererHandle.lookup(); Material *material = nullptr; if (renderer) { @@ -207,16 +277,14 @@ namespace ospray { } // No renderer present or the renderer doesn't intercept this // material type. - if (!material) { - material = Material::createMaterial(type.c_str()); - } - if (!material) { - throw std::runtime_error("unknown material type '" + type + "'"); - } + if (!material) material = Material::createMaterial(type.c_str()); handle.assign(material); } - template<> - void NewRendererObject::run() { + + // ospNewLight ////////////////////////////////////////////////////////// + + void NewLight::run() + { Renderer *renderer = (Renderer*)rendererHandle.lookup(); Light *light = nullptr; if (renderer) { @@ -227,49 +295,82 @@ namespace ospray { } // No renderer present or the renderer doesn't intercept this // material type. - if (!light) { - light = Light::createLight(type.c_str()); - } - if (!light) { - throw std::runtime_error("unknown light type '" + type + "'"); - } + if (!light) light = Light::createLight(type.c_str()); handle.assign(light); } - - NewData::NewData(){} - NewData::NewData(ObjectHandle handle, size_t nItems, - OSPDataType format, void *init, int flags) - : handle(handle), nItems(nItems), format(format), localData(nullptr), flags(flags) + + // ospNewData /////////////////////////////////////////////////////////// + + NewData::NewData(ObjectHandle handle, + size_t nItems, + OSPDataType format, + void *init, + int flags) + : handle(handle), + nItems(nItems), + format(format), + localData(nullptr), + flags(flags) { // TODO: Is this check ok for ParaView e.g. what Carson is changing in 2e81c005 ? if (init && nItems) { if (flags & OSP_DATA_SHARED_BUFFER) { localData = init; } else { + static WarnOnce warning("#osp.mpi: warning - newdata currently " + "creates a std::vector copy of input data"); data.resize(ospray::sizeOf(format) * nItems); std::memcpy(data.data(), init, data.size()); } } } - void NewData::run() { + + void NewData::run() + { Data *ospdata = nullptr; if (!data.empty()) { + // iw - shouldn't we _always_ set the shared_data flag here? + // after deserialization all the data is in a std::vector + // (data) that we own, and never free, anywya -- shouldn't + // we just share this? ospdata = new Data(nItems, format, data.data(), flags); } else if (localData) { + // iw - how can that ever trigger? localdata should get set + // only on the master, but 'run' happens only on the + // workers.... right? ospdata = new Data(nItems, format, localData, flags); } else { + // iw - can this ever happen? (empty data?) if so, shouldn't + // we make sure that flags get the shared flag removed (in + // case it was set) ospdata = new Data(nItems, format, nullptr, flags); } Assert(ospdata); + // iw - not sure if string would be handled correctly (I doubt + // it), so let's assert that nobody accidentally uses it. + assert(format != OSP_STRING); handle.assign(ospdata); - if (format == OSP_OBJECT) { + if (format == OSP_OBJECT || + format == OSP_CAMERA || + format == OSP_DATA || + format == OSP_FRAMEBUFFER || + format == OSP_GEOMETRY || + format == OSP_LIGHT || + format == OSP_MATERIAL || + format == OSP_MODEL || + format == OSP_RENDERER || + format == OSP_TEXTURE || + format == OSP_TRANSFER_FUNCTION || + format == OSP_VOLUME || + format == OSP_PIXEL_OP + ) { /* translating handles to managedobject pointers: if a data array has 'object' or 'data' entry types, then what the host sends are _handles_, not pointers, but what the core expects are pointers; to make the core happy we translate all data items back to pointers at this stage */ - ObjectHandle *asHandle = (ObjectHandle*)ospdata->data; + ObjectHandle *asHandle = (ObjectHandle*)ospdata->data; ManagedObject **asObjPtr = (ManagedObject**)ospdata->data; for (size_t i = 0; i < nItems; ++i) { if (asHandle[i] != NULL_HANDLE) { @@ -279,59 +380,86 @@ namespace ospray { } } } - size_t NewData::getTag() const { - return TAG; - } - void NewData::serialize(SerialBuffer &b) const { + + void NewData::serialize(WriteStream &b) const + { + static WarnOnce warning("#osp.mpi: Warning - newdata serialize " + "currently uses a std::vector... "); + /* note there are two issues with this: first is that when + sharing data buffer we'd have only localdata set (not the + this->data vector; second is that even _if_ we use the data + vector we're (temporarily) doubling memory consumption + because we copy all data into the std::vector first, just + so we can send it.... */ b << (int64)handle << nItems << (int32)format << flags << data; } - void NewData::deserialize(SerialBuffer &b) { + + void NewData::deserialize(ReadStream &b) + { int32 fmt; b >> handle.i64 >> nItems >> fmt >> flags >> data; format = (OSPDataType)fmt; } - NewTexture2d::NewTexture2d() {} - NewTexture2d::NewTexture2d(ObjectHandle handle, vec2i dimensions, - OSPTextureFormat format, void *texture, uint32 flags) - : handle(handle), dimensions(dimensions), format(format), flags(flags) + // ospNewTexture2d ////////////////////////////////////////////////////// + + NewTexture2d::NewTexture2d(ObjectHandle handle, + vec2i dimensions, + OSPTextureFormat format, + void *texture, + uint32 flags) + : handle(handle), + dimensions(dimensions), + format(format), + flags(flags) { size_t sz = ospray::sizeOf(format) * dimensions.x * dimensions.y; data.resize(sz); std::memcpy(data.data(), texture, sz); } - void NewTexture2d::run() { - Texture2D *texture = Texture2D::createTexture(dimensions, format, data.data(), - flags & ~OSP_TEXTURE_SHARED_BUFFER); + + void NewTexture2d::run() + { + Texture2D *texture = + Texture2D::createTexture(dimensions, format, data.data(), + flags & ~OSP_TEXTURE_SHARED_BUFFER); Assert(texture); handle.assign(texture); } - size_t NewTexture2d::getTag() const { - return TAG; - } - void NewTexture2d::serialize(SerialBuffer &b) const { + + void NewTexture2d::serialize(WriteStream &b) const + { b << (int64)handle << dimensions << (int32)format << flags << data; } - void NewTexture2d::deserialize(SerialBuffer &b) { + + void NewTexture2d::deserialize(ReadStream &b) + { int32 fmt; b >> handle.i64 >> dimensions >> fmt >> flags >> data; format = (OSPTextureFormat)fmt; } - SetRegion::SetRegion() {} + // ospSetRegion ///////////////////////////////////////////////////////// + SetRegion::SetRegion(OSPVolume volume, vec3i start, vec3i size, const void *src, OSPDataType type) - : handle((ObjectHandle&)volume), regionStart(start), regionSize(size), type(type) + : handle((ObjectHandle&)volume), regionStart(start), + regionSize(size), type(type) { size_t bytes = ospray::sizeOf(type) * size.x * size.y * size.z; // TODO: With the MPI batching this limitation should be lifted if (bytes > 2000000000LL) { - throw std::runtime_error("MPI ospSetRegion does not support region sizes > 2GB"); + throw std::runtime_error("MPI ospSetRegion does not support " + "region sizes > 2GB"); } data.resize(bytes); - std::memcpy(data.data(), src, bytes); //TODO: should support sending data without copy + + //TODO: should support sending data without copy + std::memcpy(data.data(), src, bytes); } - void SetRegion::run() { + + void SetRegion::run() + { Volume *volume = (Volume*)handle.lookup(); Assert(volume); // TODO: Does it make sense to do the allreduce & report back fails? @@ -341,140 +469,97 @@ namespace ospray { throw std::runtime_error("Failed to set region for volume"); } } - size_t SetRegion::getTag() const { - return TAG; - } - void SetRegion::serialize(SerialBuffer &b) const { + + void SetRegion::serialize(WriteStream &b) const + { b << (int64)handle << regionStart << regionSize << (int32)type << data; } - void SetRegion::deserialize(SerialBuffer &b) { + + void SetRegion::deserialize(ReadStream &b) + { int32 ty; b >> handle.i64 >> regionStart >> regionSize >> ty >> data; type = (OSPDataType)ty; } - CommitObject::CommitObject(){} - CommitObject::CommitObject(ObjectHandle handle) : handle(handle) {} - void CommitObject::run() { - ManagedObject *obj = handle.lookup(); - if (obj) { - obj->commit(); + // ospFrameBufferClear ////////////////////////////////////////////////// - // TODO: Do we need this hack anymore? - // It looks like yes? or at least glutViewer segfaults if we don't do this - // hack, to stay compatible with earlier version - Model *model = dynamic_cast(obj); - if (model) { - model->finalize(); - } - } else { - throw std::runtime_error("Error: rank " + std::to_string(mpi::world.rank) - + " did not have object to commit!"); - } - // TODO: Work units should not be directly making MPI calls. - // What should be responsible for this barrier? - // MPI_Barrier(MPI_COMM_WORLD); - mpi::barrier(mpi::world); - } - void CommitObject::runOnMaster() { - ManagedObject *obj = handle.lookup(); - if (dynamic_cast(obj)) { - obj->commit(); - } - mpi::barrier(mpi::world); - } - size_t CommitObject::getTag() const { - return TAG; - } - bool CommitObject::flushing() const { - return true; - } - void CommitObject::serialize(SerialBuffer &b) const { - b << (int64)handle; - } - void CommitObject::deserialize(SerialBuffer &b) { - b >> handle.i64; - } - - ClearFrameBuffer::ClearFrameBuffer(){} ClearFrameBuffer::ClearFrameBuffer(OSPFrameBuffer fb, uint32 channels) : handle((ObjectHandle&)fb), channels(channels) {} - void ClearFrameBuffer::run() { + + void ClearFrameBuffer::run() + { FrameBuffer *fb = (FrameBuffer*)handle.lookup(); Assert(fb); fb->clear(channels); } - void ClearFrameBuffer::runOnMaster() { + + void ClearFrameBuffer::runOnMaster() + { run(); } - size_t ClearFrameBuffer::getTag() const { - return TAG; - } - void ClearFrameBuffer::serialize(SerialBuffer &b) const { + + void ClearFrameBuffer::serialize(WriteStream &b) const + { b << (int64)handle << channels; } - void ClearFrameBuffer::deserialize(SerialBuffer &b) { + + void ClearFrameBuffer::deserialize(ReadStream &b) + { b >> handle.i64 >> channels; } - RenderFrame::RenderFrame() : varianceResult(0.f) {} - RenderFrame::RenderFrame(OSPFrameBuffer fb, OSPRenderer renderer, uint32 channels) - : fbHandle((ObjectHandle&)fb), rendererHandle((ObjectHandle&)renderer), channels(channels), - varianceResult(0.f) + // ospRenderFrame /////////////////////////////////////////////////////// + + RenderFrame::RenderFrame(OSPFrameBuffer fb, + OSPRenderer renderer, + uint32 channels) + : fbHandle((ObjectHandle&)fb), + rendererHandle((ObjectHandle&)renderer), + channels(channels), + varianceResult(0.f) {} - void RenderFrame::run() { - FrameBuffer *fb = (FrameBuffer*)fbHandle.lookup(); + + void RenderFrame::run() + { Renderer *renderer = (Renderer*)rendererHandle.lookup(); + FrameBuffer *fb = (FrameBuffer*)fbHandle.lookup(); Assert(renderer); Assert(fb); - // TODO: This function execution must run differently - // if we're the master vs. the worker in master/worker - // mode. Actually, if the Master has the Master load balancer, - // it should be fine??? Actually not if the renderer - // takes over scheduling of tile work like the distributed volume renderer - // We need some way to pick the right function to call, either to the - // renderer or directly to the load balancer to render the frame -#if 1 varianceResult = renderer->renderFrame(fb, channels); -#else - if (mpi::world.rank > 0) { - renderer->renderFrame(fb, channels); - } else { - TiledLoadBalancer::instance->renderFrame(nullptr, fb, channels); - } -#endif - } - void RenderFrame::runOnMaster() { - Renderer *renderer = (Renderer*)rendererHandle.lookup(); - FrameBuffer *fb = (FrameBuffer*)fbHandle.lookup(); - Assert(renderer); - Assert(fb); - varianceResult = TiledLoadBalancer::instance->renderFrame(renderer, fb, channels); - } - size_t RenderFrame::getTag() const { - return TAG; } - bool RenderFrame::flushing() const { - return true; + + void RenderFrame::runOnMaster() + { + run(); } - void RenderFrame::serialize(SerialBuffer &b) const { + + void RenderFrame::serialize(WriteStream &b) const + { b << (int64)fbHandle << (int64)rendererHandle << channels; } - void RenderFrame::deserialize(SerialBuffer &b) { + + void RenderFrame::deserialize(ReadStream &b) + { b >> fbHandle.i64 >> rendererHandle.i64 >> channels; } - template<> - void AddObject::run() { + // ospAddGeometry /////////////////////////////////////////////////////// + + void AddGeometry::run() + { Model *model = (Model*)modelHandle.lookup(); Geometry *geometry = (Geometry*)objectHandle.lookup(); Assert(model); Assert(geometry); model->geometry.push_back(geometry); } - template<> - void AddObject::run() { + + // ospAddVolume ///////////////////////////////////////////////////////// + + void AddVolume::run() + { Model *model = (Model*)modelHandle.lookup(); Volume *volume = (Volume*)objectHandle.lookup(); Assert(model); @@ -482,177 +567,132 @@ namespace ospray { model->volume.push_back(volume); } - template<> - void RemoveObject::run() { + // ospRemoveGeometry //////////////////////////////////////////////////// + + void RemoveGeometry::run() + { Model *model = (Model*)modelHandle.lookup(); Geometry *geometry = (Geometry*)objectHandle.lookup(); Assert(model); Assert(geometry); auto it = std::find_if(model->geometry.begin(), model->geometry.end(), - [&](const Ref &g) { - return geometry == &*g; - }); + [&](const Ref &g) { + return geometry == &*g; + }); if (it != model->geometry.end()) { model->geometry.erase(it); } } - template<> - void RemoveObject::run() { + + // ospRemoveVolume ////////////////////////////////////////////////////// + + void RemoveVolume::run() + { Model *model = (Model*)modelHandle.lookup(); Volume *volume = (Volume*)objectHandle.lookup(); Assert(model); Assert(volume); model->volume.push_back(volume); auto it = std::find_if(model->volume.begin(), model->volume.end(), - [&](const Ref &v) { - return volume == &*v; - }); + [&](const Ref &v) { + return volume == &*v; + }); if (it != model->volume.end()) { model->volume.erase(it); } } - CreateFrameBuffer::CreateFrameBuffer() {} - CreateFrameBuffer::CreateFrameBuffer(ObjectHandle handle, vec2i dimensions, - OSPFrameBufferFormat format, uint32 channels) - : handle(handle), dimensions(dimensions), format(format), channels(channels) - {} - void CreateFrameBuffer::run() { - const bool hasDepthBuffer = channels & OSP_FB_DEPTH; - const bool hasAccumBuffer = channels & OSP_FB_ACCUM; - const bool hasVarianceBuffer = channels & OSP_FB_VARIANCE; - FrameBuffer *fb = new DistributedFrameBuffer(ospray::mpi::async::CommLayer::WORLD, - dimensions, handle, format, hasDepthBuffer, hasAccumBuffer, hasVarianceBuffer); - - // TODO: Only the master does this increment, though should the workers do it too? - fb->refInc(); - handle.assign(fb); - } - void CreateFrameBuffer::runOnMaster() { - run(); - } - size_t CreateFrameBuffer::getTag() const { - return TAG; - } - void CreateFrameBuffer::serialize(SerialBuffer &b) const { - b << (int64)handle << dimensions << (int32)format << channels; - } - void CreateFrameBuffer::deserialize(SerialBuffer &b) { - int32 fmt; - b >> handle.i64 >> dimensions >> fmt >> channels; - format = (OSPFrameBufferFormat)fmt; - } - - template<> - void SetParam::run() { - ManagedObject *obj = handle.lookup(); - Assert(obj); - obj->findParam(name.c_str(), true)->set(val.c_str()); - } - template<> - void SetParam::runOnMaster() { - ManagedObject *obj = handle.lookup(); - if (dynamic_cast(obj) || dynamic_cast(obj)) { - obj->findParam(name.c_str(), true)->set(val.c_str()); - } - } + // ospRemoveParam /////////////////////////////////////////////////////// - RemoveParam::RemoveParam(){} - RemoveParam::RemoveParam(ObjectHandle handle, const char *name) : handle(handle), name(name) { + RemoveParam::RemoveParam(ObjectHandle handle, const char *name) + : handle(handle), name(name) + { Assert(handle != nullHandle); } - void RemoveParam::run() { + + void RemoveParam::run() + { ManagedObject *obj = handle.lookup(); Assert(obj); obj->removeParam(name.c_str()); } - void RemoveParam::runOnMaster() { + + void RemoveParam::runOnMaster() + { ManagedObject *obj = handle.lookup(); if (dynamic_cast(obj) || dynamic_cast(obj)) { obj->removeParam(name.c_str()); } } - size_t RemoveParam::getTag() const { - return TAG; - } - void RemoveParam::serialize(SerialBuffer &b) const { + + void RemoveParam::serialize(WriteStream &b) const + { b << (int64)handle << name; } - void RemoveParam::deserialize(SerialBuffer &b) { + + void RemoveParam::deserialize(ReadStream &b) + { b >> handle.i64 >> name; } - SetPixelOp::SetPixelOp(){} + // ospSetPixelOp //////////////////////////////////////////////////////// + SetPixelOp::SetPixelOp(OSPFrameBuffer fb, OSPPixelOp op) - : fbHandle((ObjectHandle&)fb), poHandle((ObjectHandle&)op) + : fbHandle((ObjectHandle&)fb), + poHandle((ObjectHandle&)op) {} - void SetPixelOp::run() { + + void SetPixelOp::run() + { FrameBuffer *fb = (FrameBuffer*)fbHandle.lookup(); - PixelOp *po = (PixelOp*)poHandle.lookup(); + PixelOp *po = (PixelOp*)poHandle.lookup(); Assert(fb); Assert(po); fb->pixelOp = po->createInstance(fb, fb->pixelOp.ptr); + if (!fb->pixelOp) { - std::cout << "#osp:mpi: WARNING: PixelOp did not create an instance!" << std::endl; + postStatusMsg("#osp:mpi: WARNING: PixelOp did not create " + "an instance!", 1); } } - size_t SetPixelOp::getTag() const { - return TAG; - } - void SetPixelOp::serialize(SerialBuffer &b) const { + + void SetPixelOp::serialize(WriteStream &b) const + { b << (int64)fbHandle << (int64)poHandle; } - void SetPixelOp::deserialize(SerialBuffer &b) { + + void SetPixelOp::deserialize(ReadStream &b) + { b >> fbHandle.i64 >> poHandle.i64; } - CommandRelease::CommandRelease() {} - CommandRelease::CommandRelease(ObjectHandle handle) : handle(handle){} - void CommandRelease::run() { - ManagedObject *obj = handle.lookup(); - Assert(obj); + // ospRelease /////////////////////////////////////////////////////////// + + CommandRelease::CommandRelease(ObjectHandle handle) + : handle(handle) + {} + + void CommandRelease::run() + { handle.freeObject(); } - size_t CommandRelease::getTag() const { - return TAG; - } - void CommandRelease::serialize(SerialBuffer &b) const { + + void CommandRelease::serialize(WriteStream &b) const + { b << (int64)handle; } - void CommandRelease::deserialize(SerialBuffer &b) { + + void CommandRelease::deserialize(ReadStream &b) + { b >> handle.i64; } - LoadModule::LoadModule(){} - LoadModule::LoadModule(const std::string &name) : name(name){} - void LoadModule::run() { - const std::string libName = "ospray_module_" + name; - loadLibrary(libName); - - const std::string initSymName = "ospray_init_module_" + name; - void *initSym = getSymbol(initSymName); - if (!initSym) { - throw std::runtime_error("could not find module initializer " + initSymName); - } - void (*initMethod)() = (void(*)())initSym; - initMethod(); - } - size_t LoadModule::getTag() const { - return TAG; - } - bool LoadModule::flushing() const { - return true; - } - void LoadModule::serialize(SerialBuffer &b) const { - b << name; - } - void LoadModule::deserialize(SerialBuffer &b) { - b >> name; - } + // ospFinalize ////////////////////////////////////////////////////////// + + void CommandFinalize::run() + { + runOnMaster(); - CommandFinalize::CommandFinalize(){} - void CommandFinalize::run() { - async::shutdown(); // TODO: Is it ok to call exit again here? // should we be calling exit? When the MPIDevice is // destroyed (at program exit) we'll send this command @@ -662,18 +702,20 @@ namespace ospray { // be exiting. std::exit(0); } - void CommandFinalize::runOnMaster() { - async::shutdown(); - } - size_t CommandFinalize::getTag() const { - return TAG; - } - bool CommandFinalize::flushing() const { - return true; + + void CommandFinalize::runOnMaster() + { + world.barrier(); + MPI_CALL(Finalize()); } - void CommandFinalize::serialize(SerialBuffer &b) const {} - void CommandFinalize::deserialize(SerialBuffer &b) {} - } - } -} + + void CommandFinalize::serialize(WriteStream &b) const + {} + + void CommandFinalize::deserialize(ReadStream &b) + {} + + } // ::ospray::mpi::work + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/common/OSPWork.h b/modules/mpi/common/OSPWork.h index d96ddacb85..cc733b37b5 100644 --- a/modules/mpi/common/OSPWork.h +++ b/modules/mpi/common/OSPWork.h @@ -1,5 +1,5 @@ // ======================================================================== // -// Copyright 2009-2016 Intel Corporation // +// Copyright 2009-2017 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // // you may not use this file except in compliance with the License. // @@ -14,468 +14,573 @@ // limitations under the License. // // ======================================================================== // +/*! \file OSPWork.h implements everything require to encode and + serialize work items that represent api calls + + this code currently lives only in the mpi device, but shuld in + theory also be applicable to other sorts of 'fabrics' for conveying + such encoded work items +*/ + #pragma once +#include #include "mpiCommon/MPICommon.h" -#include "mpiCommon/command.h" -#include "mpiCommon/SerialBuffer.h" -#include "common/Model.h" -#include "common/Data.h" -#include "common/Library.h" +#include "common/ObjectHandle.h" +#include "ospcommon/networking/DataStreaming.h" + #include "common/Model.h" -#include "geometry/TriangleMesh.h" #include "render/Renderer.h" #include "camera/Camera.h" #include "volume/Volume.h" #include "lights/Light.h" -#include "texture/Texture2D.h" #include "transferFunction/TransferFunction.h" -#include "common/ObjectHandle.h" + +#include namespace ospray { namespace mpi { namespace work { - void initWorkMap(); + using namespace mpicommon; + using namespace ospcommon::networking; - template - struct NewObjectTag; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_RENDERER; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_MODEL; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_GEOMETRY; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_CAMERA; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_VOLUME; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_TRANSFERFUNCTION; - }; - template<> - struct NewObjectTag { - const static size_t TAG = CMD_NEW_PIXELOP; + /*! abstract interface for a work item. a work item can + serialize itself, de-serialize itself, and return a tag that + allows the unbuffering code form figuring out what kind of + work this is */ + struct Work + { + /*! type we use for representing tags */ + using tag_t = size_t; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + virtual void serialize(WriteStream &b) const = 0; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + virtual void deserialize(ReadStream &b) = 0; + + /*! what to do to execute this work item on a worker */ + virtual void run() {} + + /*! what to do to execute this work item on the master */ + virtual void runOnMaster() {} }; - // All of the simple CMD_NEW_* can be implemented with the same - // template. The more unique ones like NEW_DATA, NEW_TEXTURE2D - // or render specific objects like lights and materials require - // some more special treatment to handle sending the data or - // other params around as well. + using CreateWorkFct = std::unique_ptr(*)(); + using WorkTypeRegistry = std::map; + + /*! create a work unit of given type */ template - struct NewObject : Work { - const static size_t TAG = NewObjectTag::TAG; - std::string type; - ObjectHandle handle; + inline std::unique_ptr make_work_unit() + { + return make_unique(); + } - NewObject() {} - NewObject(const char* type, ObjectHandle handle) : type(type), handle(handle) {} - void run() override {} - void runOnMaster() override {} - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)handle << type; - } - void deserialize(SerialBuffer &b) override { - b >> handle.i64 >> type; + template + inline CreateWorkFct createMakeWorkFct() + { + return make_work_unit; + } + + template + inline void registerWorkUnit(WorkTypeRegistry ®istry) + { + static_assert(std::is_base_of::value, + "WORK_T must be a child class of ospray::work::Work!"); + + registry[typeIdOf()] = createMakeWorkFct(); + } + + void registerOSPWorkItems(WorkTypeRegistry ®istry); + + /*! All of the simple CMD_NEW_* can be implemented with the same + template. The more unique ones like NEW_DATA, NEW_TEXTURE2D or + render specific objects like lights and materials require some + more special treatment to handle sending the data or other + params around as well. */ + template + struct NewObjectT : public Work + { + NewObjectT() = default; + NewObjectT(const char* type, ObjectHandle handle) + : type(type), handle(handle) {} + + void run() override + { + auto *obj = T::createInstance(type.c_str()); + handle.assign(obj); } + + void runOnMaster() override {} + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)handle << type; } + + /*! de-serialize from a buffer that an object of this type ha + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> handle.i64 >> type; } + + std::string type; + ObjectHandle handle; }; - // Specializations of the run method for actually creating the - // new objects of each type. - template<> - void NewObject::run(); - // For the renderer stored error threshold we need to make - // the renderer on the master as well. - template<> - void NewObject::runOnMaster(); - template<> - void NewObject::run(); - template<> - void NewObject::run(); - template<> - void NewObject::run(); - template<> - void NewObject::run(); - // We need to make volumes on master so we can set the string param - template<> - void NewObject::runOnMaster(); - template<> - void NewObject::run(); + + // NewObjectT explicit instantiations /////////////////////////////////// + + using NewModel = NewObjectT; + using NewPixelOp = NewObjectT; + using NewRenderer = NewObjectT; + using NewCamera = NewObjectT; + using NewVolume = NewObjectT; + using NewGeometry = NewObjectT; + using NewTransferFunction = NewObjectT; + + // Specializations for objects template<> - void NewObject::run(); + void NewRenderer::runOnMaster(); - template - struct NewRendererObjectTag; template<> - struct NewRendererObjectTag { - const static size_t TAG = CMD_NEW_MATERIAL; - }; + void NewVolume::runOnMaster(); + template<> - struct NewRendererObjectTag { - const static size_t TAG = CMD_NEW_LIGHT; - }; - template - struct NewRendererObject : Work { - const static size_t TAG = NewRendererObjectTag::TAG; - std::string type; + void NewModel::run(); + + struct NewMaterial : public Work + { + NewMaterial() = default; + NewMaterial(const char* type, OSPRenderer renderer, ObjectHandle handle) + : type(type), rendererHandle((ObjectHandle&)renderer), handle(handle) + {} + + void run() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)rendererHandle << (int64)handle << type; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> rendererHandle.i64 >> handle.i64 >> type; } + + // const static size_t TAG = NewRendererObjectTag::TAG; + std::string type; ObjectHandle rendererHandle; ObjectHandle handle; + }; - NewRendererObject() {} - NewRendererObject(const char* type, OSPRenderer renderer, ObjectHandle handle) + struct NewLight : public Work + { + NewLight() = default; + NewLight(const char* type, OSPRenderer renderer, ObjectHandle handle) : type(type), rendererHandle((ObjectHandle&)renderer), handle(handle) {} - void run() override {} - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)rendererHandle << (int64)handle << type; - } - void deserialize(SerialBuffer &b) override { - b >> rendererHandle.i64 >> handle.i64 >> type; - } + + void run() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)rendererHandle << (int64)handle << type; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> rendererHandle.i64 >> handle.i64 >> type; } + + // const static size_t TAG = NewRendererObjectTag::TAG; + std::string type; + ObjectHandle rendererHandle; + ObjectHandle handle; }; + // TODO: I don't do any of the sending back # of failures to // the "master", what would make sense to do in master/worker // and collaborative modes? Right now it will just throw and abort - template<> - void NewRendererObject::run(); - template<> - void NewRendererObject::run(); + // template<> + // void NewRendererObject::run(); + // template<> + // void NewRendererObject::run(); - struct NewData : Work { - const static size_t TAG = CMD_NEW_DATA; - ObjectHandle handle; - size_t nItems; - OSPDataType format; - std::vector data; - // If we're in collab/independent mode we can - // be setting with just a local shared pointer - void *localData; - int32 flags; - NewData(); + struct NewData : public Work + { + NewData() = default; NewData(ObjectHandle handle, size_t nItems, - OSPDataType format, void *initData, int flags); + OSPDataType format, void *initData, int flags); + void run() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct NewTexture2d : Work { - const static size_t TAG = CMD_NEW_TEXTURE2D; - ObjectHandle handle; - vec2i dimensions; - OSPTextureFormat format; - std::vector data; - uint32 flags; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + size_t nItems; + OSPDataType format; + std::vector data; + // If we're in collab/independent mode we can + // be setting with just a local shared pointer + void *localData; + int32 flags; + }; - NewTexture2d(); + + struct NewTexture2d : public Work + { + NewTexture2d() = default; NewTexture2d(ObjectHandle handle, vec2i dimensions, - OSPTextureFormat format, void *texture, uint32 flags); + OSPTextureFormat format, void *texture, uint32 flags); + void run() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct SetRegion : Work { - const static size_t TAG = CMD_SET_REGION; - ObjectHandle handle; - vec3i regionStart, regionSize; - OSPDataType type; - std::vector data; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + vec2i dimensions; + OSPTextureFormat format; + std::vector data; + uint32 flags; + }; - SetRegion(); + + struct SetRegion : public Work + { + SetRegion() = default; SetRegion(OSPVolume volume, vec3i start, vec3i size, const void *src, OSPDataType type); + void run() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct CommitObject : Work { - const static size_t TAG = CMD_COMMIT; - ObjectHandle handle; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; - CommitObject(); + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + vec3i regionStart; + vec3i regionSize; + OSPDataType type; + std::vector data; + }; + + struct CommitObject : public Work + { + CommitObject() = default; CommitObject(ObjectHandle handle); + void run() override; // TODO: Which objects should the master commit? void runOnMaster() override; - size_t getTag() const override; - bool flushing() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct ClearFrameBuffer : Work { - const static size_t TAG = CMD_FRAMEBUFFER_CLEAR; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + ObjectHandle handle; - uint32 channels; + }; - ClearFrameBuffer(); + struct ClearFrameBuffer : public Work + { + ClearFrameBuffer() = default; ClearFrameBuffer(OSPFrameBuffer fb, uint32 channels); + void run() override; void runOnMaster() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + uint32 channels; }; - struct RenderFrame : Work { - const static size_t TAG = CMD_RENDER_FRAME; + struct RenderFrame : public Work + { + RenderFrame() = default; + RenderFrame(OSPFrameBuffer fb, OSPRenderer renderer, uint32 channels); + + void run() override; + void runOnMaster() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + ObjectHandle fbHandle; ObjectHandle rendererHandle; uint32 channels; // Variance result for adaptive accumulation - float varianceResult; + float varianceResult {0.f}; + }; + + struct AddVolume : public Work + { + AddVolume() = default; + AddVolume(OSPModel model, const OSPVolume &t) + : modelHandle((const ObjectHandle&)model), + objectHandle((const ObjectHandle&)t) + {} - RenderFrame(); - RenderFrame(OSPFrameBuffer fb, OSPRenderer renderer, uint32 channels); void run() override; - void runOnMaster() override; - size_t getTag() const override; - bool flushing() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - template - struct AddObjectTag; - template<> - struct AddObjectTag { - const static size_t TAG = CMD_ADD_GEOMETRY; - }; - template<> - struct AddObjectTag { - const static size_t TAG = CMD_ADD_VOLUME; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)modelHandle << (int64)objectHandle; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> modelHandle.i64 >> objectHandle.i64; } + + ObjectHandle modelHandle; + ObjectHandle objectHandle; }; - template - struct AddObject : Work { - const static size_t TAG = AddObjectTag::TAG; + + struct AddGeometry : public Work + { + AddGeometry() = default; + AddGeometry(OSPModel model, const OSPGeometry &t) + : modelHandle((const ObjectHandle&)model), + objectHandle((const ObjectHandle&)t) + {} + + void run() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)modelHandle << (int64)objectHandle; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> modelHandle.i64 >> objectHandle.i64; } + ObjectHandle modelHandle; ObjectHandle objectHandle; + }; - AddObject(){} - AddObject(OSPModel model, const T &t) - : modelHandle((const ObjectHandle&)model), objectHandle((const ObjectHandle&)t) + + struct RemoveGeometry : public Work + { + RemoveGeometry() = default; + RemoveGeometry(OSPModel model, const OSPGeometry &t) + : modelHandle((const ObjectHandle&)model), + objectHandle((const ObjectHandle&)t) {} - void run() override {} - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)modelHandle << (int64)objectHandle; - } - void deserialize(SerialBuffer &b) override { - b >> modelHandle.i64 >> objectHandle.i64; - } - }; - template<> - void AddObject::run(); - template<> - void AddObject::run(); - template - struct RemoveObjectTag; - template<> - struct RemoveObjectTag { - const static size_t TAG = CMD_REMOVE_GEOMETRY; - }; - template<> - struct RemoveObjectTag { - const static size_t TAG = CMD_REMOVE_VOLUME; - }; + void run() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)modelHandle << (int64)objectHandle; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> modelHandle.i64 >> objectHandle.i64; } - template - struct RemoveObject : Work { - const static size_t TAG = RemoveObjectTag::TAG; ObjectHandle modelHandle; ObjectHandle objectHandle; + }; - RemoveObject(){} - RemoveObject(OSPModel model, const T &t) - : modelHandle((const ObjectHandle&)model), objectHandle((const ObjectHandle&)t) + struct RemoveVolume : public Work + { + RemoveVolume() = default; + RemoveVolume(OSPModel model, const OSPVolume &t) + : modelHandle((const ObjectHandle&)model), + objectHandle((const ObjectHandle&)t) {} - void run() override {} - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)modelHandle << (int64)objectHandle; - } - void deserialize(SerialBuffer &b) override { - b >> modelHandle.i64 >> objectHandle.i64; - } - }; - template<> - void RemoveObject::run(); - template<> - void RemoveObject::run(); - struct CreateFrameBuffer : Work { - const static size_t TAG = CMD_FRAMEBUFFER_CREATE; - ObjectHandle handle; - vec2i dimensions; - OSPFrameBufferFormat format; - uint32 channels; + void run() override; - CreateFrameBuffer(); + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)modelHandle << (int64)objectHandle; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> modelHandle.i64 >> objectHandle.i64; } + + ObjectHandle modelHandle; + ObjectHandle objectHandle; + }; + + struct CreateFrameBuffer : public Work + { + CreateFrameBuffer() = default; CreateFrameBuffer(ObjectHandle handle, vec2i dimensions, - OSPFrameBufferFormat format, uint32 channels); + OSPFrameBufferFormat format, uint32 channels); + void run() override; void runOnMaster() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - // Need specializations of the SetParam TAG for the actual - // type that is being set so we can decode properly on the - // other end. - template - struct ParamTag; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_STRING; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_INT; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_FLOAT; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_VEC2F; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_VEC2I; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_VEC3F; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_VEC3I; - }; - template<> - struct ParamTag { - const static size_t TAG = CMD_SET_VEC4F; - }; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; - template - struct SetParam : Work { - const static size_t TAG = ParamTag::TAG; ObjectHandle handle; - std::string name; - T val; + vec2i dimensions {-1}; + OSPFrameBufferFormat format; + uint32 channels; + }; - SetParam(){} + /*! this should go into implementation section ... */ + template + struct SetParam : public Work + { + SetParam() = default; + SetParam(ObjectHandle handle, const char *name, const T &val) : handle(handle), name(name), val(val) { Assert(handle != nullHandle); } - void run() override { + + inline void run() override + { ManagedObject *obj = handle.lookup(); Assert(obj); obj->findParam(name.c_str(), true)->set(val); } - void runOnMaster() override { + + void runOnMaster() override + { + if (!handle.defined()) + return; + ManagedObject *obj = handle.lookup(); if (dynamic_cast(obj) || dynamic_cast(obj)) { obj->findParam(name.c_str(), true)->set(val); } } - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)handle << name << val; - } - void deserialize(SerialBuffer &b) override { - b >> handle.i64 >> name >> val; - } + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)handle << name << val; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> handle.i64 >> name >> val; } + + ObjectHandle handle; + std::string name; + T val; }; + // run for setString needs to know to pass the C string to // set the param so we need to provide a different run. template<> void SetParam::run(); + template<> void SetParam::runOnMaster(); // both SetMaterial and SetObject take more different forms than the other // set operations since it doesn't take a name at all so // we go through a full specializations for them. - template<> - struct SetParam : Work { - const static size_t TAG = CMD_SET_MATERIAL; - ObjectHandle handle; - ObjectHandle material; - - SetParam(){} - SetParam(ObjectHandle handle, OSPMaterial val) + struct SetMaterial : public Work + { + SetMaterial() = default; + SetMaterial(ObjectHandle handle, OSPMaterial val) : handle(handle), material((ObjectHandle&)val) { Assert(handle != nullHandle); Assert(material != nullHandle); } - void run() override { - Geometry *obj = (Geometry*)handle.lookup(); - Material *mat = (Material*)material.lookup(); - Assert(obj); - Assert(mat); - obj->setMaterial(mat); - } - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)handle << (int64)material; - } - void deserialize(SerialBuffer &b) override { - b >> handle.i64 >> material.i64; - } - }; - template<> - struct SetParam : Work { - const static size_t TAG = CMD_SET_OBJECT; + void run() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)handle << (int64)material; } + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> handle.i64 >> material.i64; } + ObjectHandle handle; - std::string name; - ObjectHandle val; + ObjectHandle material; + }; - SetParam(){} + template<> + struct SetParam : public Work + { + SetParam() = default; + SetParam(ObjectHandle handle, const char *name, OSPObject &obj) - : handle(handle), name(name), val((ObjectHandle&)obj) + : handle(handle), + name(name), + val((ObjectHandle&)obj) { Assert(handle != nullHandle); } - void run() override { + + void run() override + { ManagedObject *obj = handle.lookup(); Assert(obj); ManagedObject *param = NULL; @@ -485,80 +590,123 @@ namespace ospray { } obj->findParam(name.c_str(), true)->set(param); } - size_t getTag() const override { - return TAG; - } - void serialize(SerialBuffer &b) const override { - b << (int64)handle << name << (int64)val; - } - void deserialize(SerialBuffer &b) override { - b >> handle.i64 >> name >> val.i64; - } - }; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override + { b << (int64)handle << name << (int64)val; } + + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override + { b >> handle.i64 >> name >> val.i64; } - struct RemoveParam : Work { - const static size_t TAG = CMD_REMOVE_PARAM; ObjectHandle handle; - std::string name; + std::string name; + ObjectHandle val; + }; - RemoveParam(); + struct RemoveParam : public Work + { + RemoveParam() = default; RemoveParam(ObjectHandle handle, const char *name); + void run() override; void runOnMaster() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct SetPixelOp : Work { - const static size_t TAG = CMD_SET_PIXELOP; - ObjectHandle fbHandle; - ObjectHandle poHandle; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + std::string name; + }; - SetPixelOp(); + struct SetPixelOp : public Work + { + SetPixelOp() = default; SetPixelOp(OSPFrameBuffer fb, OSPPixelOp op); + void run() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct CommandRelease : Work { - const static size_t TAG = CMD_RELEASE; - ObjectHandle handle; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle fbHandle; + ObjectHandle poHandle; + }; - CommandRelease(); + struct CommandRelease : public Work + { + CommandRelease() = default; CommandRelease(ObjectHandle handle); + void run() override; - size_t getTag() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; - }; - struct LoadModule : Work { - const static size_t TAG = CMD_LOAD_MODULE; - std::string name; + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + ObjectHandle handle; + }; - LoadModule(); + struct LoadModule : public Work + { + LoadModule() = default; LoadModule(const std::string &name); + void run() override; - size_t getTag() const override; - bool flushing() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; + // We do need to load modules on master in the case of scripted modules + void runOnMaster() override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; + + std::string name; + int errorCode {0}; }; - struct CommandFinalize : Work { - const static size_t TAG = CMD_FINALIZE; + struct CommandFinalize : public Work + { + CommandFinalize() = default; - CommandFinalize(); void run() override; void runOnMaster() override; - size_t getTag() const override; - bool flushing() const override; - void serialize(SerialBuffer &b) const override; - void deserialize(SerialBuffer &b) override; + + /*! serializes itself on the given serial buffer - will write + all data into this buffer in a way that it can afterwards + un-serialize itself 'on the other side'*/ + void serialize(WriteStream &b) const override; + + /*! de-serialize from a buffer that an object of this type has + serialized itself in */ + void deserialize(ReadStream &b) override; }; - }// namespace work - }// namespace mpi -}// namespace ospray + + } // ::ospray::mpi::work + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/fb/DistributedFrameBuffer.cpp b/modules/mpi/fb/DistributedFrameBuffer.cpp index d5b90c9c73..4e50db5621 100644 --- a/modules/mpi/fb/DistributedFrameBuffer.cpp +++ b/modules/mpi/fb/DistributedFrameBuffer.cpp @@ -21,11 +21,17 @@ #include "ospcommon/tasking/parallel_for.h" #include "ospcommon/tasking/schedule.h" +#include "mpiCommon/MPICommon.h" + #ifdef _WIN32 # include // for Sleep #endif -#define DBG(a) /* ignore */ +#if 0 +# define DBG(a) a +#else +# define DBG(a) /* ignore */ +#endif using std::cout; using std::endl; @@ -36,7 +42,13 @@ namespace ospray { using DFB = DistributedFrameBuffer; - struct MasterTileMessage : public mpi::async::CommLayer::Message { + struct TileMessage + { + int command {-1}; + }; + + struct MasterTileMessage : public TileMessage + { vec2i coords; float error; }; @@ -44,7 +56,8 @@ namespace ospray { /*! message sent to the master when a tile is finished. Todo: compress the color data */ template - struct MasterTileMessage_FB : public MasterTileMessage { + struct MasterTileMessage_FB : public MasterTileMessage + { FBType color[TILE_SIZE][TILE_SIZE]; }; @@ -54,7 +67,8 @@ namespace ospray { /*! message sent from one node's instance to another, to tell that instance to write that tile */ - struct WriteTileMessage : public mpi::async::CommLayer::Message { + struct WriteTileMessage : public TileMessage + { // TODO: add compression of pixels during transmission vec2i coords; // XXX redundant: it's also in tile.region.lower ospray::Tile tile; @@ -96,43 +110,43 @@ namespace ospray { if (tiles <= 0) return; - int rc = MPI_Bcast(tileErrorBuffer, tiles, MPI_FLOAT, 0, MPI_COMM_WORLD); - mpi::checkMpiError(rc); + MPI_CALL(Bcast(tileErrorBuffer, tiles, MPI_FLOAT, 0, MPI_COMM_WORLD)); } // DistributedFrameBuffer definitions /////////////////////////////////////// - DFB::DistributedFrameBuffer(mpi::async::CommLayer *comm, - const vec2i &numPixels, - size_t myID, + DFB::DistributedFrameBuffer(const vec2i &numPixels, + ObjectHandle myID, ColorBufferFormat colorBufferFormat, bool hasDepthBuffer, bool hasAccumBuffer, - bool hasVarianceBuffer) + bool hasVarianceBuffer, + bool masterIsAWorker) : FrameBuffer(numPixels,colorBufferFormat,hasDepthBuffer, hasAccumBuffer,hasVarianceBuffer), - mpi::async::CommLayer::Object(comm,myID), + myID(myID), tileErrorRegion(hasVarianceBuffer ? getNumTiles() : vec2i(0)), localFBonMaster(nullptr), frameMode(WRITE_ONCE), frameIsActive(false), - frameIsDone(false) + frameIsDone(false), + masterIsAWorker(masterIsAWorker) { - assert(comm); this->ispcEquivalent = ispc::DFB_create(this); ispc::DFB_set(getIE(), numPixels.x, numPixels.y, colorBufferFormat); - comm->registerObject(this,myID); + + mpi::messaging::registerMessageListener(myID.objID(), this); createTiles(); const size_t bytes = sizeof(int32)*getTotalTiles(); tileAccumID = (int32*)alignedMalloc(bytes); memset(tileAccumID, 0, bytes); - if (comm->group->rank == 0) { + if (mpicommon::IamTheMaster()) { if (colorBufferFormat == OSP_FB_NONE) { DBG(cout << "#osp:mpi:dfb: we're the master, but framebuffer has 'NONE' " - << "format; creating distributed frame buffer WITHOUT having a " - << "mappable copy on the master" << endl); + << "format; creating distributed frame buffer WITHOUT having a " + << "mappable copy on the master" << endl); } else { localFBonMaster = new LocalFrameBuffer(numPixels, colorBufferFormat, @@ -151,10 +165,11 @@ namespace ospray { void DFB::startNewFrame(const float errorThreshold) { - std::vector delayedMessage; + std::vector> delayedMessage; + { SCOPED_LOCK(mutex); - DBG(printf("rank %i starting new frame\n",mpi::world.rank)); + DBG(printf("rank %i starting new frame\n", mpicommon::globalRank())); assert(!frameIsActive); if (pixelOp) @@ -168,20 +183,25 @@ namespace ospray { delayedMessage = this->delayedMessage; this->delayedMessage.clear(); + // NOTE: Doing error sync may do a broadcast, needs to be done before + // async messaging enabled in beginFrame() tileErrorRegion.sync(); + numTilesCompletedThisFrame = 0; if (hasAccumBuffer) { // increment accumID only for active tiles - for (int t = 0; t < getTotalTiles(); t++) + for (int t = 0; t < getTotalTiles(); t++) { if (tileError(vec2i(t, 0)) <= errorThreshold) { - if (IamTheMaster() || allTiles[t]->mine()) + if (mpicommon::IamTheMaster() || allTiles[t]->mine()) numTilesCompletedThisFrame++; - } else + } else { tileAccumID[t]++; + } + } } - frameIsDone = false; + frameIsDone = false; // set frame to active - this HAS TO BE the last thing we do // before unlockign the mutex, because the 'incoming()' message @@ -197,8 +217,9 @@ namespace ospray { this->incoming(msg); if (numTilesCompletedThisFrame - == (IamTheMaster() ? getTotalTiles() : myTiles.size())) + == (mpicommon::IamTheMaster() ? getTotalTiles() : myTiles.size())) { closeCurrentFrame(); + } } void DFB::freeTiles() @@ -210,6 +231,12 @@ namespace ospray { myTiles.clear(); } + size_t DistributedFrameBuffer::ownerIDFromTileID(size_t tileID) + { + return masterIsAWorker ? tileID % (mpicommon::numGlobalRanks()) : + (tileID % (mpicommon::numGlobalRanks() - 1) + 1); + } + TileData *DFB::createTile(const vec2i &xy, size_t tileID, size_t ownerID) { TileData *td = nullptr; @@ -223,7 +250,9 @@ namespace ospray { break; case Z_COMPOSITE: default: - td = new ZCompositeTile(this, xy, tileID, ownerID); + size_t numWorkers = masterIsAWorker ? mpicommon::numGlobalRanks() : + mpicommon::numWorkers(); + td = new ZCompositeTile(this, xy, tileID, ownerID, numWorkers); break; } @@ -236,8 +265,8 @@ namespace ospray { vec2i numPixels = getNumPixels(); for (int y = 0; y < numPixels.y; y += TILE_SIZE) { for (int x = 0; x < numPixels.x; x += TILE_SIZE, tileID++) { - size_t ownerID = tileID % (comm->group->size - 1); - if (workerRank(ownerID) == comm->group->rank) { + size_t ownerID = ownerIDFromTileID(tileID); + if (ownerID == size_t(mpicommon::globalRank())) { TileData *td = createTile(vec2i(x, y), tileID, ownerID); myTiles.push_back(td); allTiles.push_back(td); @@ -309,7 +338,7 @@ namespace ospray { // and finally, tell the master that this tile is done auto *tileDesc = this->getTileDescFor(msg->coords); TileData *td = (TileData*)tileDesc; - this->tileIsCompleted(td); + this->finalizeTileOnMaster(td); } void DFB::processMessage(WriteTileMessage *msg) @@ -322,68 +351,20 @@ namespace ospray { void DFB::tileIsCompleted(TileData *tile) { - DBG(printf("rank %i: tilecompleted %i,%i\n",mpi::world.rank, + DBG(printf("rank %i: tilecompleted %i,%i\n",mpicommon::globalRank(), tile->begin.x,tile->begin.y)); - if (IamTheMaster()) { - int numTilesCompletedByMyTile = 0; - /*! we will not do anything with the tile other than mark it's done */ - { - SCOPED_LOCK(mutex); - numTilesCompletedByMyTile = ++numTilesCompletedThisFrame; - DBG(printf("MASTER: MARKING AS COMPLETED %i,%i -> %li %i\n", - tile->begin.x,tile->begin.y,numTilesCompletedThisFrame, - numTiles.x*numTiles.y)); - } - if (numTilesCompletedByMyTile == numTiles.x*numTiles.y) - closeCurrentFrame(); - } else { - if (pixelOp) { - pixelOp->postAccum(tile->final); - } - - switch(colorBufferFormat) { - case OSP_FB_NONE: { - /* if the master doesn't have a framebufer (i.e., format - 'none'), we're only telling it that we're done, but are not - sending any pixels */ - MasterTileMessage_NONE *mtm = new MasterTileMessage_NONE; - mtm->command = MASTER_WRITE_TILE_NONE; - mtm->coords = tile->begin; - mtm->error = tile->error; - comm->sendTo(this->master,mtm,sizeof(*mtm)); - } break; - case OSP_FB_RGBA8: - case OSP_FB_SRGBA: { - /*! if the master has RGBA8 or SRGBA format, we're sending him a tile - of the proper data */ - MasterTileMessage_RGBA_I8 *mtm = new MasterTileMessage_RGBA_I8; - mtm->command = MASTER_WRITE_TILE_I8; - mtm->coords = tile->begin; - mtm->error = tile->error; - memcpy(mtm->color,tile->color,TILE_SIZE*TILE_SIZE*sizeof(uint32)); - comm->sendTo(this->master,mtm,sizeof(*mtm)); - } break; - case OSP_FB_RGBA32F: { - /*! if the master has RGBA32F format, we're sending him a tile of the - proper data */ - MasterTileMessage_RGBA_F32 *mtm = new MasterTileMessage_RGBA_F32; - mtm->command = MASTER_WRITE_TILE_F32; - mtm->coords = tile->begin; - mtm->error = tile->error; - memcpy(mtm->color,tile->color,TILE_SIZE*TILE_SIZE*sizeof(vec4f)); - comm->sendTo(this->master,mtm,sizeof(*mtm)); - } break; - default: - throw std::runtime_error("#osp:mpi:dfb: color buffer format not " - "implemented for distributed frame buffer"); - }; + if (pixelOp) { + pixelOp->postAccum(tile->final); + } + sendTileToMaster(tile); + if (!mpicommon::IamTheMaster()) { size_t numTilesCompletedByMe = 0; { SCOPED_LOCK(mutex); numTilesCompletedByMe = ++numTilesCompletedThisFrame; DBG(printf("rank %i: MARKING AS COMPLETED %i,%i -> %i %i\n", - mpi::world.rank, + mpicommon::globalRank(), tile->begin.x,tile->begin.y,(int)numTilesCompletedThisFrame, numTiles.x*numTiles.y)); } @@ -394,19 +375,106 @@ namespace ospray { } } - void DFB::incoming(mpi::async::CommLayer::Message *_msg) + void DFB::finalizeTileOnMaster(TileData *tile) { + assert(mpicommon::IamTheMaster()); + int numTilesCompletedByMyTile = 0; + /*! we will not do anything with the tile other than mark it's done */ + { + SCOPED_LOCK(mutex); + numTilesCompletedByMyTile = ++numTilesCompletedThisFrame; + DBG(printf("MASTER: MARKING AS COMPLETED %i,%i -> %li %i\n", + tile->begin.x,tile->begin.y,numTilesCompletedThisFrame, + numTiles.x*numTiles.y)); + } + DBG(printf("MASTER: num tilescmpletedbymytiles: %i/%i\n", + numTilesCompletedByMyTile,numTiles.x*numTiles.y)); + if (numTilesCompletedByMyTile == numTiles.x*numTiles.y) + closeCurrentFrame(); + } + + void DistributedFrameBuffer::sendTileToMaster(TileData *tile) + { + switch(colorBufferFormat) { + case OSP_FB_NONE: { + /* if the master doesn't have a framebufer (i.e., format + 'none'), we're only telling it that we're done, but are not + sending any pixels */ + MasterTileMessage_NONE mtm; + mtm.command = MASTER_WRITE_TILE_NONE; + mtm.coords = tile->begin; + mtm.error = tile->error; + + auto msg = std::make_shared(&mtm, sizeof(mtm)); + + mpi::messaging::sendTo(mpicommon::masterRank(), myID, msg); + } break; + case OSP_FB_RGBA8: + case OSP_FB_SRGBA: { + /*! if the master has RGBA8 or SRGBA format, we're sending him a tile + of the proper data */ + MasterTileMessage_RGBA_I8 mtm; + mtm.command = MASTER_WRITE_TILE_I8; + mtm.coords = tile->begin; + mtm.error = tile->error; + memcpy(mtm.color, tile->color, TILE_SIZE*TILE_SIZE*sizeof(uint32)); + + auto msg = std::make_shared(&mtm, sizeof(mtm)); + + mpi::messaging::sendTo(mpicommon::masterRank(), myID, msg); + } break; + case OSP_FB_RGBA32F: { + /*! if the master has RGBA32F format, we're sending him a tile of the + proper data */ + MasterTileMessage_RGBA_F32 mtm; + mtm.command = MASTER_WRITE_TILE_F32; + mtm.coords = tile->begin; + mtm.error = tile->error; + memcpy(mtm.color, tile->color, TILE_SIZE*TILE_SIZE*sizeof(vec4f)); + + auto msg = std::make_shared(&mtm, sizeof(mtm)); + + mpi::messaging::sendTo(mpicommon::masterRank(), myID, msg); + } break; + default: + throw std::runtime_error("#osp:mpi:dfb: color buffer format not " + "implemented for distributed frame buffer"); + }; + } + + size_t DFB::numMyTiles() const + { + return myTiles.size(); + } + + TileDesc *DFB::getTileDescFor(const vec2i &coords) const + { + return allTiles[getTileIDof(coords)]; + } + + size_t DFB::getTileIDof(const vec2i &c) const + { + return (c.x/TILE_SIZE) + (c.y/TILE_SIZE)*numTiles.x; + } + + std::string DFB::toString() const + { + return "ospray::DFB"; + } + + void DFB::incoming(const std::shared_ptr &message) { if (!frameIsActive) { SCOPED_LOCK(mutex); if (!frameIsActive) { // frame is not actually active, yet - put the tile into the // delayed processing buffer, and return WITHOUT deleting it. - delayedMessage.push_back(_msg); + delayedMessage.push_back(message); return; } } - schedule([=]() { + tasking::schedule([=]() { + auto *_msg = (TileMessage*)message->data; switch (_msg->command) { case MASTER_WRITE_TILE_NONE: this->processMessage((MasterTileMessage_NONE*)_msg); @@ -421,19 +489,18 @@ namespace ospray { this->processMessage((WriteTileMessage*)_msg); break; default: - assert(0); + throw std::runtime_error("#dfb: unknown tile type processed!"); }; - delete _msg; }); } void DFB::closeCurrentFrame() { - DBG(printf("rank %i CLOSES frame\n",mpi::world.rank)); + DBG(printf("rank %i CLOSES frame\n", mpicommon::globalRank())); frameIsActive = false; frameIsDone = true; - if (IamTheMaster()) { + if (mpicommon::IamTheMaster() && !masterIsAWorker) { /* do nothing */ } else { if (pixelOp) { @@ -452,19 +519,21 @@ namespace ospray { if (!tileDesc->mine()) { // NOT my tile... - WriteTileMessage *msg = new WriteTileMessage; - msg->coords = tile.region.lower; + WriteTileMessage msgPayload; + msgPayload.coords = tile.region.lower; // TODO: compress pixels before sending ... - memcpy(&msg->tile,&tile,sizeof(ospray::Tile)); - msg->command = WORKER_WRITE_TILE; + memcpy(&msgPayload.tile, &tile, sizeof(ospray::Tile)); + msgPayload.command = WORKER_WRITE_TILE; - comm->sendTo(this->worker[tileDesc->ownerID], msg,sizeof(*msg)); - } else { - // this is my tile... - assert(frameIsActive); + auto msg = std::make_shared(&msgPayload, + sizeof(msgPayload)); + int dstRank = tileDesc->ownerID; + mpi::messaging::sendTo(dstRank, myID, msg); + } else { + if (!frameIsActive) + throw std::runtime_error("#dfb: cannot setTile if frame is inactive!"); TileData *td = (TileData*)tileDesc; - td->process(tile); } } @@ -480,7 +549,7 @@ namespace ospray { { frameID = -1; // we increment at the start of the frame if (!myTiles.empty()) { - parallel_for(myTiles.size(), [&](int taskIndex){ + tasking::parallel_for(myTiles.size(), [&](int taskIndex) { TileData *td = this->myTiles[taskIndex]; assert(td); if (fbChannelFlags & OSP_FB_ACCUM) { @@ -518,9 +587,17 @@ namespace ospray { return tileErrorRegion[tile]; } + void DistributedFrameBuffer::beginFrame() + { + mpi::messaging::enableAsyncMessaging(); + FrameBuffer::beginFrame(); + } + float DFB::endFrame(const float errorThreshold) { - return tileErrorRegion.refine(errorThreshold); + mpi::messaging::disableAsyncMessaging(); + return mpicommon::IamTheMaster() ? + tileErrorRegion.refine(errorThreshold) : errorThreshold; } } // ::ospray diff --git a/modules/mpi/fb/DistributedFrameBuffer.h b/modules/mpi/fb/DistributedFrameBuffer.h index bc3c6bc5b3..93e2ac0914 100644 --- a/modules/mpi/fb/DistributedFrameBuffer.h +++ b/modules/mpi/fb/DistributedFrameBuffer.h @@ -16,18 +16,14 @@ #pragma once -// ours -// ospray components -#include "components/mpiCommon/async/CommLayer.h" // ospray #include "ospray/fb/LocalFB.h" +// ospray_mpi +#include "../common/Messaging.h" // std -#include +#include namespace ospray { - using std::cout; - using std::endl; - struct TileDesc; struct TileData; @@ -36,24 +32,24 @@ namespace ospray { struct MasterTileMessage_FB; struct WriteTileMessage; - class DistributedTileError : public TileError { + class DistributedTileError : public TileError + { public: DistributedTileError(const vec2i &numTiles); ~DistributedTileError() = default; void sync(); // broadcast tileErrorBuffer to all workers }; - struct DistributedFrameBuffer - : public mpi::async::CommLayer::Object, - public virtual FrameBuffer + struct DistributedFrameBuffer : public maml::MessageHandler, + public FrameBuffer { - DistributedFrameBuffer(mpi::async::CommLayer *comm, - const vec2i &numPixels, - size_t myHandle, + DistributedFrameBuffer(const vec2i &numPixels, + ObjectHandle myHandle, ColorBufferFormat, bool hasDepthBuffer, bool hasAccumBuffer, - bool hasVarianceBuffer); + bool hasVarianceBuffer, + bool masterIsAWorker = false); ~DistributedFrameBuffer(); @@ -88,22 +84,23 @@ namespace ospray { void waitUntilFinished(); - // ================================================================== - // remaining framebuffer interface - // ================================================================== - int32 accumID(const vec2i &) override; float tileError(const vec2i &tile) override; + void beginFrame() override; float endFrame(const float errorThreshold) override; + enum FrameMode { WRITE_ONCE, ALPHA_BLEND, Z_COMPOSITE }; + + void setFrameMode(FrameMode newFrameMode) ; + // ================================================================== - // interface for the comm layer, to enable communication between + // interface for maml messaging, enables communication between // different instances of same object // ================================================================== //! handle incoming message from commlayer. it's the //! recipient's job to properly delete the message. - void incoming(mpi::async::CommLayer::Message *msg) override; + void incoming(const std::shared_ptr &message) override; //! process an empty client-to-master write tile message */ void processMessage(MasterTileMessage *msg); @@ -115,6 +112,13 @@ namespace ospray { //! process a client-to-client write tile message */ void processMessage(WriteTileMessage *msg); + private: + + friend struct TileData; + friend struct AlphaBlendTile_simple; + friend struct WriteOnlyOnceTile; + friend struct ZCompositeTile; + // ================================================================== // internal helper functions // ================================================================== @@ -129,46 +133,48 @@ namespace ospray { to the master (if required), and properly do the bookkeeping that this tile is now done. */ void tileIsCompleted(TileData *tile); + /*! This function is called when a master write tile is completed, on the + master process. It only marks on the master that the tile is done, and + checks if we've completed rendering the frame. */ + void finalizeTileOnMaster(TileData *tile); + + void sendTileToMaster(TileData *tile); //! number of tiles that "I" own - size_t numMyTiles() const { return myTiles.size(); } - bool IamTheMaster() const { return comm->IamTheMaster(); } - static int32 workerRank(int id) { return mpi::async::CommLayer::workerRank(id); } + size_t numMyTiles() const; + + //! \brief common function to help printf-debugging + /*! \detailed Every derived class should overrride this! */ + std::string toString() const override; /*! return tile descriptor for given pixel coordinates. this tile ! may or may not belong to current instance */ - TileDesc *getTileDescFor(const vec2i &coords) const - { return allTiles[getTileIDof(coords)]; } + TileDesc *getTileDescFor(const vec2i &coords) const; /*! return the tile ID for given pair of coordinates. this tile may or may not belong to current instance */ - size_t getTileIDof(const vec2i &c) const - { return (c.x/TILE_SIZE)+(c.y/TILE_SIZE)*numTiles.x; } + size_t getTileIDof(const vec2i &c) const; - //! \brief common function to help printf-debugging - /*! \detailed Every derived class should overrride this! */ - std::string toString() const override - { return "ospray::DistributedFrameBuffer"; } + void createTiles(); + TileData *createTile(const vec2i &xy, size_t tileID, size_t ownerID); + void freeTiles(); + + size_t ownerIDFromTileID(size_t tileID); + + // Data members /////////////////////////////////////////////////////////// - typedef enum { - WRITE_ONCE, ALPHA_BLEND, Z_COMPOSITE - } FrameMode; + ObjectHandle myID; int32 *tileAccumID; //< holds accumID per tile, for adaptive accumulation //!< holds error per tile and adaptive regions, for variance estimation / stopping DistributedTileError tileErrorRegion; - /*! local frame buffer on the master used for storing the final tiles. will be null on all workers, and _may_ be null on the master if the master does not have a color buffer */ Ref localFBonMaster; FrameMode frameMode; - void setFrameMode(FrameMode newFrameMode) ; - void createTiles(); - TileData *createTile(const vec2i &xy, size_t tileID, size_t ownerID); - void freeTiles(); /*! #tiles we've (already) sent to / received by the master this frame (used to track when current node is done with this frame - we are done @@ -186,7 +192,7 @@ namespace ospray { /*! mutex used to protect all threading-sensitive data in this object */ - Mutex mutex; + std::mutex mutex; //! set to true when the frame becomes 'active', and write tile //! messages can be consumed. @@ -196,8 +202,10 @@ namespace ospray { frame */ bool frameIsDone; + bool masterIsAWorker {false}; + //! condition that gets triggered when the frame is done - Condition frameDoneCond; + std::condition_variable frameDoneCond; /*! a vector of async messages for the *current* frame that got received before that frame actually started, and that we have @@ -205,7 +213,7 @@ namespace ospray { condition can actually happen if another node is just fast enough, and sends us the first rendered tile before our node's loadbalancer even started working on that frame. */ - std::vector delayedMessage; + std::vector> delayedMessage; }; // Inlined definitions ////////////////////////////////////////////////////// @@ -238,7 +246,7 @@ namespace ospray { // and finally, tell the master that this tile is done auto *tileDesc = this->getTileDescFor(msg->coords); TileData *td = (TileData*)tileDesc; - this->tileIsCompleted(td); + this->finalizeTileOnMaster(td); } } // ::ospray diff --git a/modules/mpi/fb/DistributedFrameBuffer.ispc b/modules/mpi/fb/DistributedFrameBuffer.ispc index fff6f62832..9ecfaeab86 100644 --- a/modules/mpi/fb/DistributedFrameBuffer.ispc +++ b/modules/mpi/fb/DistributedFrameBuffer.ispc @@ -88,42 +88,44 @@ export uniform float DFB_accumulate_##name(void *uniform _self, \ const uniform float accHalfScale = rcpf(accumID/2+1); \ float err = 0.f; \ for (uniform int i = 0; i < maxi; i++) { \ - vec3f col3 = make_vec3f(tile->r[i], tile->g[i], tile->b[i]); \ - vec4f col = make_vec4f(col3, tile->a[i]) \ - + make_vec4f(accum->r[i], accum->g[i], accum->b[i], accum->a[i]); \ + vec4f col = make_vec4f(tile->r[i], tile->g[i], tile->b[i], tile->a[i]);\ + vec4f acc = col \ + + make_vec4f(accum->r[i], accum->g[i], accum->b[i], accum->a[i]); \ \ - accum->r[i] = col.x; \ - accum->g[i] = col.y; \ - accum->b[i] = col.z; \ - accum->a[i] = col.w; \ + accum->r[i] = acc.x; \ + accum->g[i] = acc.y; \ + accum->b[i] = acc.z; \ + accum->a[i] = acc.w; \ \ - col = col * rcpAccumID; \ + acc = acc * rcpAccumID; \ \ /* variance buffer accumulates every other frame */ \ if (hasVarianceBuffer && (accumID & 1) == 1) { \ - varying vec3f vari = make_vec3f(0.f); \ + varying vec4f vari = make_vec4f(0.f); \ if (accumID > 1) \ - vari = make_vec3f(variance->r[i], variance->g[i], variance->b[i]); \ - vari = vari + col3; \ + vari = make_vec4f(variance->r[i], variance->g[i], variance->b[i], \ + variance->a[i]); \ + vari = vari + col; \ variance->r[i] = vari.x; \ variance->g[i] = vari.y; \ variance->b[i] = vari.z; \ + variance->a[i] = vari.w; \ \ - const vec3f acc3 = make_vec3f(col); \ - const float den2 = reduce_add(acc3); \ + /* invert alpha (bright alpha is more important */ \ + const float den2 = reduce_add(make_vec3f(acc)) + (1.f-acc.w); \ if (den2 > 0.0f) { \ - const vec3f diff = absf(acc3 - accHalfScale * vari); \ + const vec4f diff = absf(acc - accHalfScale * vari); \ if ((i*programCount % TILE_SIZE) + programIndex < size.x) \ err += reduce_add(diff) * rsqrtf(den2); \ } \ } \ \ - final->r[i] = col.x; \ - final->g[i] = col.y; \ - final->b[i] = col.z; \ - final->a[i] = col.w; \ + final->r[i] = acc.x; \ + final->g[i] = acc.y; \ + final->b[i] = acc.z; \ + final->a[i] = acc.w; \ \ - color[i] = cvt(col); \ + color[i] = cvt(acc); \ } \ /* error is also only updated every other frame to avoid alternating \ * error (get a monotone sequence) */ \ diff --git a/modules/mpi/fb/DistributedFrameBuffer_TileTypes.cpp b/modules/mpi/fb/DistributedFrameBuffer_TileTypes.cpp index 23e3187656..6912f9fe79 100644 --- a/modules/mpi/fb/DistributedFrameBuffer_TileTypes.cpp +++ b/modules/mpi/fb/DistributedFrameBuffer_TileTypes.cpp @@ -32,7 +32,13 @@ namespace ospray { : TileDesc(dfb,begin,tileID,ownerID) {} - /*! called exactly once at the beginning of each frame */ + AlphaBlendTile_simple::AlphaBlendTile_simple(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID) + : TileData(dfb,begin,tileID,ownerID) + {} + void AlphaBlendTile_simple::newFrame() { currentGeneration = 0; @@ -175,6 +181,15 @@ namespace ospray { dfb->tileIsCompleted(this); } + ZCompositeTile::ZCompositeTile(DistributedFrameBuffer *dfb, + const vec2i &begin, + size_t tileID, + size_t ownerID, + size_t numWorkers) + : TileData(dfb,begin,tileID,ownerID), + numWorkers(numWorkers) + {} + void ZCompositeTile::newFrame() { numPartsComposited = 0; @@ -187,12 +202,12 @@ namespace ospray { { SCOPED_LOCK(mutex); if (numPartsComposited == 0) - memcpy(&compositedTileData,&tile,sizeof(tile)); + memcpy(&compositedTileData, &tile, sizeof(tile)); else ispc::DFB_zComposite((ispc::VaryingTile*)&tile, (ispc::VaryingTile*)&this->compositedTileData); - done = (++numPartsComposited == size_t(dfb->comm->numWorkers())); + done = (++numPartsComposited == numWorkers); } if (done) { diff --git a/modules/mpi/fb/DistributedFrameBuffer_TileTypes.h b/modules/mpi/fb/DistributedFrameBuffer_TileTypes.h index 7a7f82d37b..89bb3ae4ca 100644 --- a/modules/mpi/fb/DistributedFrameBuffer_TileTypes.h +++ b/modules/mpi/fb/DistributedFrameBuffer_TileTypes.h @@ -29,16 +29,14 @@ namespace ospray { that 'size' is the tile size used by the frame buffer, _NOT_ necessariy 'end-begin'. 'color' and 'depth' arrays are always alloc'ed in TILE_SIZE pixels */ - struct TileDesc { - ALIGNED_STRUCT - - /*! constructor */ + struct TileDesc + { TileDesc(DistributedFrameBuffer *dfb, const vec2i &begin, size_t tileID, size_t ownerID); - virtual ~TileDesc() {} + virtual ~TileDesc() = default; /*! returns whether this tile is one of this particular node's tiles */ @@ -55,7 +53,8 @@ namespace ospray { do not have a RGBA-I8 color field, because typically that'll be done by the postop and send-to-master op, and not stored in the DFB tile itself */ - struct TileData : public TileDesc { + struct TileData : public TileDesc + { TileData(DistributedFrameBuffer *dfb, const vec2i &begin, size_t tileID, @@ -92,8 +91,8 @@ namespace ospray { // ------------------------------------------------------- /*! specialized tile for plain sort-first rendering, where each tile is written only exactly once. */ - struct WriteOnlyOnceTile : public TileData { - + struct WriteOnlyOnceTile : public TileData + { /*! constructor */ WriteOnlyOnceTile(DistributedFrameBuffer *dfb, const vec2i &begin, @@ -119,15 +118,10 @@ namespace ospray { // ------------------------------------------------------- /*! specialized tile for doing Z-compositing. this does not have additional data, but a different write op. */ - struct ZCompositeTile : public TileData { - - /*! constructor */ - ZCompositeTile(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileData(dfb,begin,tileID,ownerID) - {} + struct ZCompositeTile : public TileData + { + ZCompositeTile(DistributedFrameBuffer *dfb, const vec2i &begin, + size_t tileID, size_t ownerID, size_t numWorkers); /*! called exactly once at the beginning of each frame */ void newFrame() override; @@ -140,26 +134,25 @@ namespace ospray { tile */ size_t numPartsComposited; + size_t numWorkers; + /*! since we do not want to mess up the existing accumulatation buffer in the parent tile we temporarily composite into this buffer until all the composites have been done. */ ospray::Tile compositedTileData; - Mutex mutex; + std::mutex mutex; }; /*! specialized tile implementation that first buffers all ospray::Tile's until all input tiles are available, then sorts them by closest z component per tile, and only tthen does front-to-back compositing of those tiles */ - struct AlphaBlendTile_simple : public TileData { - - /*! constructor */ + struct AlphaBlendTile_simple : public TileData + { AlphaBlendTile_simple(DistributedFrameBuffer *dfb, - const vec2i &begin, - size_t tileID, - size_t ownerID) - : TileData(dfb,begin,tileID,ownerID) - {} + const vec2i &begin, + size_t tileID, + size_t ownerID); /*! called exactly once at the beginning of each frame */ void newFrame() override; @@ -168,7 +161,8 @@ namespace ospray { written into / composited into this dfb tile */ void process(const ospray::Tile &tile) override; - struct BufferedTile { + struct BufferedTile + { ospray::Tile tile; /*! determines order of this tile relative to other tiles. @@ -177,11 +171,12 @@ namespace ospray { increasing 'BufferedTile::sortOrder' value */ float sortOrder; }; + std::vector bufferedTile; int currentGeneration; int expectedInNextGeneration; int missingInCurrentGeneration; - Mutex mutex; + std::mutex mutex; }; } // namespace ospray diff --git a/apps/volumeViewer/ColorMap.h b/modules/mpi/mpi_offload_worker_main.cpp similarity index 70% rename from apps/volumeViewer/ColorMap.h rename to modules/mpi/mpi_offload_worker_main.cpp index b478fc54aa..d9d67ccf18 100644 --- a/apps/volumeViewer/ColorMap.h +++ b/modules/mpi/mpi_offload_worker_main.cpp @@ -14,30 +14,25 @@ // limitations under the License. // // ======================================================================== // -#pragma once - -// ospray public -#include -// ospcommon -#include "ospcommon/vec.h" -// std -#include -#include -#include - -class ColorMap +#ifdef _WIN32 +# include +#else +# include +#endif +#include "ospray/ospray.h" + +int main(int ac, const char *av[]) { -public: - - ColorMap(std::string name, std::vector colors); - - std::string getName() const; - std::vector getColors() const; - - QImage getImage(); - -protected: - - std::string name; - std::vector colors; -}; + // always append "--osp:mpi" to args; multiple occurences are not harmful, + // and we want to retain the original args (which could have been e.g. + // "--osp:logoutput") + int argc = ac+1; + const char **argv = (const char **)alloca(argc * sizeof(void *)); + for(int i = 0; i < ac; i++) + argv[i] = av[i]; + argv[ac] = "--osp:mpi"; + + ospInit(&argc, argv); + + return 0; +} diff --git a/modules/mpi/render/MPILoadBalancer.cpp b/modules/mpi/render/MPILoadBalancer.cpp index 2c94fb067e..70aa2df05b 100644 --- a/modules/mpi/render/MPILoadBalancer.cpp +++ b/modules/mpi/render/MPILoadBalancer.cpp @@ -28,11 +28,12 @@ namespace ospray { namespace mpi { - using std::cout; - using std::endl; + using namespace mpicommon; namespace staticLoadBalancer { + // staticLoadBalancer::Master definitions /////////////////////////////// + float Master::renderFrame(Renderer *renderer, FrameBuffer *fb, const uint32 channelFlags) @@ -41,9 +42,9 @@ namespace ospray { DistributedFrameBuffer *dfb = dynamic_cast(fb); assert(dfb); + dfb->startNewFrame(renderer->errorThreshold); dfb->beginFrame(); - dfb->startNewFrame(renderer->errorThreshold); /* the client will do its magic here, and the distributed frame buffer will be writing tiles here, without us doing anything ourselves */ @@ -57,19 +58,20 @@ namespace ospray { return "ospray::mpi::staticLoadBalancer::Master"; } + // staticLoadBalancer::Slave definitions //////////////////////////////// + float Slave::renderFrame(Renderer *renderer, FrameBuffer *fb, const uint32 channelFlags) { auto *dfb = dynamic_cast(fb); - dfb->beginFrame(); + dfb->startNewFrame(renderer->errorThreshold); + dfb->beginFrame(); void *perFrameData = renderer->beginFrame(fb); const int ALLTASKS = fb->getTotalTiles(); - // TODO WILL: In collaborative mode rank 0 should join the worker group - // as well so this worker should actually just be worker as before int NTASKS = ALLTASKS / worker.size; // NOTE(jda) - If all tiles do not divide evenly among all worker ranks @@ -79,8 +81,7 @@ namespace ospray { if ((ALLTASKS % worker.size) > worker.rank) NTASKS++; - // serial_for(NTASKS, [&](int taskIndex){ - parallel_for(NTASKS, [&](int taskIndex){ + tasking::parallel_for(NTASKS, [&](int taskIndex) { const size_t tileID = taskIndex * worker.size + worker.rank; const size_t numTiles_x = fb->getNumTiles().x; const size_t tile_y = tileID / numTiles_x; @@ -93,28 +94,25 @@ namespace ospray { #define MAX_TILE_SIZE 128 -#if TILE_SIZE>MAX_TILE_SIZE - Tile *tilePtr = new Tile(tileId, fb->size, accumID); - Tile &tile = *tilePtr; +#if TILE_SIZE > MAX_TILE_SIZE + auto tilePtr = make_unique(tileId, fb->size, accumID); + auto &tile = *tilePtr; #else Tile __aligned(64) tile(tileId, fb->size, accumID); #endif - // serial_for(numJobs(renderer->spp, accumID), [&](int tid){ - parallel_for(numJobs(renderer->spp, accumID), [&](int tid){ + tasking::parallel_for(numJobs(renderer->spp, accumID), [&](int tid) { renderer->renderTile(perFrameData, tile, tid); }); fb->setTile(tile); -#if TILE_SIZE>MAX_TILE_SIZE - delete tilePtr; -#endif }); dfb->waitUntilFinished(); renderer->endFrame(perFrameData,channelFlags); - return inf; // irrelevant on slave + return dfb->endFrame(inf); // irrelevant return value on slave, still + // call to stop maml layer } std::string Slave::toString() const @@ -122,6 +120,59 @@ namespace ospray { return "ospray::mpi::staticLoadBalancer::Slave"; } + // staticLoadBalancer::Distributed definitions ////////////////////////// + + float Distributed::renderFrame(Renderer *renderer, + FrameBuffer *fb, + const uint32 channelFlags) + { + auto *dfb = dynamic_cast(fb); + + dfb->startNewFrame(renderer->errorThreshold); + dfb->beginFrame(); + + auto *perFrameData = renderer->beginFrame(dfb); + + tasking::parallel_for(dfb->getTotalTiles(), [&](int taskIndex) { + const size_t numTiles_x = fb->getNumTiles().x; + const size_t tile_y = taskIndex / numTiles_x; + const size_t tile_x = taskIndex - tile_y*numTiles_x; + const vec2i tileID(tile_x, tile_y); + const int32 accumID = fb->accumID(tileID); + const bool tileOwner = (taskIndex % numGlobalRanks()) == globalRank(); + + if (dfb->tileError(tileID) <= renderer->errorThreshold) + return; + + Tile __aligned(64) tile(tileID, dfb->size, accumID); + + const int NUM_JOBS = (TILE_SIZE*TILE_SIZE)/RENDERTILE_PIXELS_PER_JOB; + tasking::parallel_for(NUM_JOBS, [&](int tIdx) { + renderer->renderTile(perFrameData, tile, tIdx); + }); + + if (tileOwner) { + tile.generation = 0; + tile.children = numGlobalRanks() - 1; + } else { + tile.generation = 1; + tile.children = 0; + } + + fb->setTile(tile); + }); + + dfb->waitUntilFinished(); + renderer->endFrame(nullptr, channelFlags); + + return dfb->endFrame(renderer->errorThreshold); + } + + std::string Distributed::toString() const + { + return "ospray::mpi::staticLoadBalancer::Distributed"; + } + }// ::ospray::mpi::staticLoadBalancer } // ::ospray::mpi } // ::ospray diff --git a/modules/mpi/render/MPILoadBalancer.h b/modules/mpi/render/MPILoadBalancer.h index 87ac8594b9..1785b54afe 100644 --- a/modules/mpi/render/MPILoadBalancer.h +++ b/modules/mpi/render/MPILoadBalancer.h @@ -24,9 +24,6 @@ namespace ospray { namespace mpi { - // ======================================================= - // ======================================================= - // ======================================================= namespace staticLoadBalancer { /*! \brief the 'master' in a tile-based master-slave *static* load balancer @@ -38,8 +35,8 @@ namespace ospray { struct Master : public TiledLoadBalancer { float renderFrame(Renderer *tiledRenderer, - FrameBuffer *fb, - const uint32 channelFlags) override; + FrameBuffer *fb, + const uint32 channelFlags) override; std::string toString() const override; }; @@ -59,10 +56,20 @@ namespace ospray { int32 numTotalThreads; float renderFrame(Renderer *tiledRenderer, - FrameBuffer *fb, - const uint32 channelFlags) override; + FrameBuffer *fb, + const uint32 channelFlags) override; std::string toString() const override; }; + + struct Distributed : public TiledLoadBalancer + { + float renderFrame(Renderer *tiledRenderer, + FrameBuffer *fb, + const uint32 channelFlags) override; + + std::string toString() const override; + }; + }// ::ospray::mpi::staticLoadBalancer } // ::ospray::mpi } // ::ospray diff --git a/modules/mpi/render/distributed/DistributedRaycast.cpp b/modules/mpi/render/distributed/DistributedRaycast.cpp new file mode 100644 index 0000000000..3b034b1fdb --- /dev/null +++ b/modules/mpi/render/distributed/DistributedRaycast.cpp @@ -0,0 +1,152 @@ +// ======================================================================== // +// Copyright 2009-2017 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. // +// ======================================================================== // + +// ospcommon +#include "ospcommon/tasking/parallel_for.h" +#include "common/Data.h" +// ospray +#include "DistributedRaycast.h" +#include "../../common/DistributedModel.h" +#include "../MPILoadBalancer.h" +#include "../../fb/DistributedFrameBuffer.h" +// ispc exports +#include "DistributedRaycast_ispc.h" + +namespace ospray { + namespace mpi { + + struct RegionInfo + { + int currentRegion; + bool *regionVisible; + + RegionInfo() : currentRegion(0), regionVisible(nullptr) {} + }; + + // DistributedRaycastRenderer definitions ///////////////////////////////// + + DistributedRaycastRenderer::DistributedRaycastRenderer() + { + ispcEquivalent = ispc::DistributedRaycastRenderer_create(this); + } + + void DistributedRaycastRenderer::commit() + { + Renderer::commit(); + DistributedModel *distribModel = dynamic_cast(model); + if (distribModel) { + ispc::DistributedRaycastRenderer_setRegions(ispcEquivalent, + (ispc::box3f*)distribModel->myRegions.data(), distribModel->myRegions.size(), + (ispc::box3f*)distribModel->othersRegions.data(), distribModel->othersRegions.size()); + } else { + throw std::runtime_error("DistributedRaycastRender must use a DistributedModel from " + "the MPIDistributedDevice"); + } + } + + float DistributedRaycastRenderer::renderFrame(FrameBuffer *fb, + const uint32 channelFlags) + { + using namespace mpicommon; + + auto *dfb = dynamic_cast(fb); + dfb->setFrameMode(DistributedFrameBuffer::ALPHA_BLEND); + dfb->startNewFrame(errorThreshold); + dfb->beginFrame(); + + auto *perFrameData = beginFrame(dfb); + // This renderer doesn't use per frame data, since we sneak in some tile + // info in this pointer. + assert(!perFrameData); + DistributedModel *distribModel = dynamic_cast(model); + const size_t numRegions = distribModel->myRegions.size() + distribModel->othersRegions.size(); + + tasking::parallel_for(dfb->getTotalTiles(), [&](int taskIndex) { + const size_t numTiles_x = fb->getNumTiles().x; + const size_t tile_y = taskIndex / numTiles_x; + const size_t tile_x = taskIndex - tile_y*numTiles_x; + const vec2i tileID(tile_x, tile_y); + const int32 accumID = fb->accumID(tileID); + const bool tileOwner = (taskIndex % numGlobalRanks()) == globalRank(); + + if (dfb->tileError(tileID) <= errorThreshold) { + return; + } + + // The first 0..myRegions.size() - 1 entries are for my regions, + // the following entries are for other nodes regions + RegionInfo regionInfo; + regionInfo.regionVisible = STACK_BUFFER(bool, numRegions); + std::fill(regionInfo.regionVisible, regionInfo.regionVisible + numRegions, false); + + Tile __aligned(64) tile(tileID, dfb->size, accumID); + + // We use the task of rendering the first region to also fill out the block visiblility list + const int NUM_JOBS = (TILE_SIZE * TILE_SIZE) / RENDERTILE_PIXELS_PER_JOB; + tasking::parallel_for(NUM_JOBS, [&](int tIdx) { + renderTile(®ionInfo, tile, tIdx); + }); + + if (regionInfo.regionVisible[0]) { + tile.generation = 1; + tile.children = 0; + fb->setTile(tile); + } + + // If we own the tile send the background color and the count of children for the + // number of regions projecting to it that will be sent. + if (tileOwner) { + tile.generation = 0; + tile.children = std::count(regionInfo.regionVisible, regionInfo.regionVisible + numRegions, true); + std::fill(tile.r, tile.r + TILE_SIZE * TILE_SIZE, bgColor.x); + std::fill(tile.g, tile.g + TILE_SIZE * TILE_SIZE, bgColor.y); + std::fill(tile.b, tile.b + TILE_SIZE * TILE_SIZE, bgColor.z); + std::fill(tile.a, tile.a + TILE_SIZE * TILE_SIZE, 1.0); + std::fill(tile.z, tile.z + TILE_SIZE * TILE_SIZE, std::numeric_limits::infinity()); + fb->setTile(tile); + } + + // Render the rest of our regions that project to this tile and ship them off + tile.generation = 1; + tile.children = 0; + for (size_t bid = 1; bid < distribModel->myRegions.size(); ++bid) { + if (!regionInfo.regionVisible[bid]) { + continue; + } + regionInfo.currentRegion = bid; + tasking::parallel_for(NUM_JOBS, [&](int tIdx) { + renderTile(®ionInfo, tile, tIdx); + }); + fb->setTile(tile); + } + }); + + dfb->waitUntilFinished(); + endFrame(nullptr, channelFlags); + + return dfb->endFrame(errorThreshold); + } + + std::string DistributedRaycastRenderer::toString() const + { + return "ospray::mpi::DistributedRaycastRenderer"; + } + + OSP_REGISTER_RENDERER(DistributedRaycastRenderer, mpi_raycast); + + } // ::ospray::mpi +} // ::ospray + diff --git a/modules/mpi/render/distributed/DistributedRaycast.h b/modules/mpi/render/distributed/DistributedRaycast.h new file mode 100644 index 0000000000..00b40fa3a3 --- /dev/null +++ b/modules/mpi/render/distributed/DistributedRaycast.h @@ -0,0 +1,49 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +// ospray +#include "render/Renderer.h" + +namespace ospray { + namespace mpi { + + /* The distributed raycast renderer supports rendering distributed + * geometry and volume data, assuming that the data distribution is suitable + * for sort-last compositing. Specifically, the data must be organized + * among nodes such that each nodes region is convex and disjoint from the + * others. The renderer uses the 'regions' data set on the distributed model + * from the MPIDistributedDevice to determine the number of tiles to + * render and expect for compositing. + * + * Also see apps/ospRandSciVisTest.cpp and apps/ospRandSphereTest.cpp for + * example usage. + */ + struct DistributedRaycastRenderer : public Renderer + { + DistributedRaycastRenderer(); + virtual ~DistributedRaycastRenderer() = default;//TODO! + + void commit() override; + + float renderFrame(FrameBuffer *fb, const uint32 fbChannelFlags) override; + + std::string toString() const override; + }; + + } // ::ospray::mpi +} // ::ospray diff --git a/modules/mpi/render/distributed/DistributedRaycast.ispc b/modules/mpi/render/distributed/DistributedRaycast.ispc new file mode 100644 index 0000000000..2bff50c345 --- /dev/null +++ b/modules/mpi/render/distributed/DistributedRaycast.ispc @@ -0,0 +1,187 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +//ospray +#include "common/Model.ih" +#include "render/Renderer.ih" +#include "render/util.ih" + +struct DistributedRaycastRenderer +{ + uniform Renderer super; + // TODO: For now it's sufficient just to know which regions + // we own and their bounds, and which regions 'others' own and + // their bounds. With that info we can properly setup the information + // about the total # of tiles to expect for each image tile. + uniform box3f *uniform myRegions; + uniform int numMyRegions; + uniform box3f *uniform othersRegions; + uniform int numOthersRegions; +}; + +struct RegionInfo +{ + uniform int currentRegion; + uniform bool *uniform regionVisible; +}; + +void DistributedRaycastRenderer_testRegions(uniform DistributedRaycastRenderer *uniform self, + uniform RegionInfo *uniform regionInfo, + const varying ScreenSample &sample) +{ + for (uniform int i = 0; i < self->numMyRegions; ++i) { + float t0, t1; + intersectBox(sample.ray, self->myRegions[i], t0, t1); + if (t0 < t1 && t0 >= sample.ray.t0 && t0 <= sample.ray.t) { + regionInfo->regionVisible[i] = true; + } + } + for (uniform int i = 0; i < self->numOthersRegions; ++i) { + float t0, t1; + intersectBox(sample.ray, self->othersRegions[i], t0, t1); + if (t0 < t1 && t0 >= sample.ray.t0 && t0 <= sample.ray.t) { + regionInfo->regionVisible[self->numMyRegions + i] = true; + } + } +} + +void DistributedRaycastRenderer_renderSample(uniform Renderer *uniform _self, + void *uniform perFrameData, + varying ScreenSample &sample) +{ + uniform DistributedRaycastRenderer *uniform self = + (uniform DistributedRaycastRenderer *uniform)_self; + + uniform RegionInfo *uniform regionInfo = (uniform RegionInfo *uniform)perFrameData; + if (regionInfo && regionInfo->currentRegion == 0) { + DistributedRaycastRenderer_testRegions(self, regionInfo, sample); + } + + // Ray offset for this sample, as a fraction of the nominal step size. + float rayOffset = precomputedHalton2(sample.sampleID.z); + int ix = sample.sampleID.x % 4; + int iy = sample.sampleID.y % 4; + int patternID = ix + 4 * iy; + rayOffset += precomputedHalton3(patternID); + if (rayOffset > 1.f) { + rayOffset -= 1.f; + } + + // Intersect with current region for this node's local data + if (self->myRegions && regionInfo) { + intersectBox(sample.ray, self->myRegions[regionInfo->currentRegion], sample.ray.t0, sample.ray.t); + } + + traceRay(self->super.model, sample.ray); + sample.z = sample.ray.t; + + if (sample.ray.geomID < 0) { + // The owner sends the background color, so we composite with a transparent + // black instead of the scene's bgcolor + sample.rgb = make_vec3f(0.0f); + sample.alpha = 0.f; + } else { + // Spheres are cheap to shade so it's fine we do it early, potentially tossing + // the result if the volume is opaque before the hit. This also assumes that + // the spheres are opaque. + DifferentialGeometry dg; + dg.color = make_vec4f(0.f); + postIntersect(self->super.model, dg, sample.ray, + DG_COLOR | DG_MATERIALID | DG_NG | DG_NS); + sample.rgb = make_vec3f(dg.color) * + abs(dot(normalize(sample.ray.dir), normalize(dg.Ns))); + sample.alpha = 1.f; + } + + vec4f volumeColor = make_vec4f(0.f); + // TODO: Support for more than one volume (put them in the Embree BVH?) + if (self->super.model->volumeCount > 0) { + // See if we hit the volume bounds + Ray ray = sample.ray; + float t0, t1; + Volume *uniform volume = self->super.model->volumes[0]; + intersectBox(ray, volume->boundingBox, t0, t1); + if (t0 < t1 && t0 >= ray.t0 && t0 <= ray.t) { + // Sample offset placement correction, like in the data-parallel + // raycast renderer. We must offset and step as if we're sampling a continuous + // volume on a single node. + float dt = volume->samplingStep * rcpf(volume->samplingRate); + int i0 = (int)(t0 / dt); + ray.t0 = (i0 + rayOffset)*dt; + if (ray.t0 < t0) { + ray.t0 += dt; + } + ray.t = min(t1, sample.ray.t); + + // Now raymarch the volume + while (ray.t0 < ray.t && volumeColor.w < 1.0) { + const vec3f coordinates = ray.org + ray.t0 * ray.dir; + const float sample = volume->computeSample(volume, coordinates); + + TransferFunction *uniform tfcn = volume->transferFunction; + // Look up the color associated with the volume sample. + const vec3f sampleColor = tfcn->getColorForValue(tfcn, sample); + const float opacity = tfcn->getOpacityForValue(tfcn, sample); + + // Set the color contribution for this sample only (do not accumulate). + const vec4f color = clamp(opacity / volume->samplingRate) + * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); + + volumeColor = volumeColor + (1.f - volumeColor.w) * color; + + // Advance the ray + volume->intersect(volume, ray); + } + volumeColor.w = clamp(volumeColor.w); + if (sample.ray.geomID < 0) { + sample.z = t1; + } + } + } + // Composite the geometry + sample.rgb = make_vec3f(volumeColor.x, volumeColor.y, volumeColor.z) + + (1.f - volumeColor.w) * sample.rgb; + sample.alpha = volumeColor.w + (1.f - volumeColor.w) * sample.alpha; +} + +// Exported functions ///////////////////////////////////////////////////////// + +export void *uniform DistributedRaycastRenderer_create(void *uniform cppE) { + uniform DistributedRaycastRenderer *uniform self = + uniform new uniform DistributedRaycastRenderer; + + Renderer_Constructor(&self->super, cppE, NULL, NULL, 1); + self->super.renderSample = DistributedRaycastRenderer_renderSample; + self->myRegions = NULL; + self->othersRegions = NULL; + + return self; +} + +export void DistributedRaycastRenderer_setRegions(void *uniform _self, + uniform box3f *uniform myRegions, + uniform int numMyRegions, + uniform box3f *uniform othersRegions, + uniform int numOthersRegions) +{ + uniform DistributedRaycastRenderer *uniform self = + (uniform DistributedRaycastRenderer *uniform)_self; + self->myRegions = myRegions; + self->numMyRegions = numMyRegions; + self->othersRegions = othersRegions; + self->numOthersRegions = numOthersRegions; +} + diff --git a/modules/mpi/render/volume/RaycastVolumeMaterial.h b/modules/mpi/render/volume/RaycastVolumeMaterial.h index 940d81002e..c1a06b79da 100644 --- a/modules/mpi/render/volume/RaycastVolumeMaterial.h +++ b/modules/mpi/render/volume/RaycastVolumeMaterial.h @@ -26,13 +26,13 @@ namespace ospray { typedef vec3f Color; //! Material used by RaycastVolumeRenderer. - struct RaycastVolumeMaterial : public Material { - + struct RaycastVolumeMaterial : public Material + { RaycastVolumeMaterial(); - void commit(); + void commit() override; - std::string toString() const; + std::string toString() const override; /*! opacity: 0 (transparent), 1 (opaque) */ Texture2D *map_d; float d; diff --git a/modules/mpi/render/volume/RaycastVolumeRenderer.cpp b/modules/mpi/render/volume/RaycastVolumeRenderer.cpp index df2c01594b..5684dfdba0 100644 --- a/modules/mpi/render/volume/RaycastVolumeRenderer.cpp +++ b/modules/mpi/render/volume/RaycastVolumeRenderer.cpp @@ -35,6 +35,8 @@ namespace ospray { + using namespace mpicommon; + Material *RaycastVolumeRenderer::createMaterial(const char *type) { UNUSED(type); @@ -46,15 +48,18 @@ namespace ospray { CacheForBlockTiles(size_t numBlocks) : numBlocks(numBlocks), blockTile(new Tile *[numBlocks]) { - for (size_t i = 0; i < numBlocks; i++) blockTile[i] = nullptr; + for (size_t i = 0; i < numBlocks; i++) + blockTile[i] = nullptr; } ~CacheForBlockTiles() { - for (size_t i = 0; i < numBlocks; i++) - if (blockTile[i]) delete blockTile[i]; + for (size_t i = 0; i < numBlocks; i++) { + if (blockTile[i]) + delete blockTile[i]; + } - delete[] blockTile; + delete [] blockTile; } Tile *allocTile() @@ -92,7 +97,7 @@ namespace ospray { #endif } - Mutex mutex; + std::mutex mutex; size_t numBlocks; Tile *volatile *blockTile; }; @@ -119,27 +124,27 @@ namespace ospray { void DPRenderTask::operator()(int taskIndex) const { - const size_t tileID = taskIndex; - const size_t tile_y = taskIndex / numTiles_x; - const size_t tile_x = taskIndex - tile_y*numTiles_x; + const int &tileID = taskIndex; + const int tile_y = tileID / numTiles_x; + const int tile_x = tileID - tile_y*numTiles_x; const vec2i tileId(tile_x, tile_y); - const int32 accumID = fb->accumID(tileId); + const int accumID = fb->accumID(tileId); Tile bgTile(tileId, fb->size, accumID); Tile fgTile(bgTile); - size_t numBlocks = dpv->numDDBlocks; + int numBlocks = dpv->numDDBlocks; CacheForBlockTiles blockTileCache(numBlocks); bool *blockWasVisible = STACK_BUFFER(bool, numBlocks); - for (size_t i = 0; i < numBlocks; i++) + for (int i = 0; i < numBlocks; i++) blockWasVisible[i] = false; bool renderForeAndBackground = - (taskIndex % mpi::getWorkerCount()) == mpi::getWorkerRank(); + (tileID % mpicommon::numWorkers()) == mpicommon::workerRank(); const int numJobs = (TILE_SIZE*TILE_SIZE)/RENDERTILE_PIXELS_PER_JOB; - parallel_for(numJobs, [&](int tid){ + tasking::parallel_for(numJobs, [&](int tid){ ispc::DDDVRRenderer_renderTile(renderer->getIE(), (ispc::Tile&)fgTile, (ispc::Tile&)bgTile, @@ -148,12 +153,11 @@ namespace ospray { dpv->ddBlock, blockWasVisible, tileID, - mpi::getWorkerRank(), + mpicommon::workerRank(), renderForeAndBackground, tid); }); - if (renderForeAndBackground) { // this is a tile owned by me - i'm responsible for writing // generaition #0, and telling the fb how many more tiles will @@ -170,8 +174,6 @@ namespace ospray { + totalBlocksInTile /* plus how many blocks map to this tile, IN TOTAL (ie, INCLUDING blocks on other nodes)*/; - // printf("rank %i total tiles in tile %i is %i\n", - // core::getWorkerRank(),taskIndex,nextGenTiles); // set background tile bgTile.generation = 0; @@ -193,7 +195,7 @@ namespace ospray { // _across_all_clients_, but we only have to send ours (assuming // that all clients together send exactly as many as the owner // told the DFB to expect) - for (size_t blockID = 0; blockID < numBlocks; blockID++) { + for (int blockID = 0; blockID < numBlocks; blockID++) { Tile *tile = blockTileCache.blockTile[blockID]; if (tile == nullptr) continue; @@ -203,7 +205,7 @@ namespace ospray { tile->rcp_fbSize = bgTile.rcp_fbSize; tile->accumID = accumID; tile->generation = 1; - tile->children = 0; //nextGenTile-1; + tile->children = 0; fb->setTile(*tile); } @@ -218,6 +220,12 @@ namespace ospray { float RaycastVolumeRenderer::renderFrame(FrameBuffer *fb, const uint32 channelFlags) { + if (mpicommon::IamTheMaster()) { + //NOTE: This function is for offload render worker ranks only, thus the + // master punts to default behavior + return Renderer::renderFrame(fb, channelFlags); + } + using DDBV = DataDistributedBlockedVolume; std::vector ddVolumeVec; for (size_t volumeID = 0; volumeID < model->volume.size(); volumeID++) { @@ -227,28 +235,19 @@ namespace ospray { } if (ddVolumeVec.empty()) { - static bool printed = false; - if (!printed) { - cout << "no data parallel volumes, rendering in traditional" - << " raycast_volume_render mode" << endl; - printed = true; - } - - return Renderer::renderFrame(fb,channelFlags); + static WarnOnce warning("no data parallel volumes, rendering in " + "traditional raycast_volume_render mode"); + return Renderer::renderFrame(fb, channelFlags); } // ======================================================= // OK, we _need_ data-parallel rendering .... - static bool printed = false; - if (!printed) { - std::cout << "#dvr: at least one dp volume" - " -> needs data-parallel rendering ..." << std::endl; - printed = true; - } + static WarnOnce dataParMsg("#dvr: at least one dp volume ->" + " needs data-parallel rendering ..."); // check if we're even in mpi parallel mode (can't do // data-parallel otherwise) - if (!ospray::mpi::isMpiParallel()) { + if (!mpicommon::isMpiParallel()) { throw std::runtime_error("#dvr: need data-parallel rendering, " "but not running in mpi mode!?"); } @@ -262,10 +261,9 @@ namespace ospray { } dfb->setFrameMode(DistributedFrameBuffer::ALPHA_BLEND); - + dfb->startNewFrame(errorThreshold); Renderer::beginFrame(fb); - dfb->startNewFrame(errorThreshold); if (ddVolumeVec.size() > 1) { /* note: our assumption below is that all blocks together are @@ -288,7 +286,7 @@ namespace ospray { renderTask.dpv = ddVolumeVec[0]; size_t NTASKS = renderTask.numTiles_x * renderTask.numTiles_y; - parallel_for(NTASKS, std::move(renderTask)); + tasking::parallel_for(NTASKS, renderTask); dfb->waitUntilFinished(); Renderer::endFrame(nullptr, channelFlags); diff --git a/modules/mpi/render/volume/RaycastVolumeRenderer.h b/modules/mpi/render/volume/RaycastVolumeRenderer.h index 260ce5f34e..34facf1a82 100644 --- a/modules/mpi/render/volume/RaycastVolumeRenderer.h +++ b/modules/mpi/render/volume/RaycastVolumeRenderer.h @@ -16,7 +16,6 @@ #pragma once -#include "OSPMPIConfig.h" #include "render/Renderer.h" namespace ospray { @@ -24,12 +23,10 @@ namespace ospray { //! \brief A concrete implemetation of the Renderer class for rendering //! volumes optionally containing embedded surfaces. //! - class RaycastVolumeRenderer : public Renderer { - - public: - - RaycastVolumeRenderer() = default; - ~RaycastVolumeRenderer() = default; + struct RaycastVolumeRenderer : public Renderer + { + RaycastVolumeRenderer() = default; + virtual ~RaycastVolumeRenderer() = default; //! Create a material of the given type. Material* createMaterial(const char *type) override; @@ -46,15 +43,8 @@ namespace ospray { private: - //! Print an error message. - void emitMessage(const std::string &kind, const std::string &message) const; - - //! Error checking. - void exitOnCondition(bool condition, const std::string &message) const; - //! ISPC equivalents for lights. std::vector lights; - }; // Inlined function definitions ///////////////////////////////////////////// @@ -64,22 +54,4 @@ namespace ospray { return("ospray::RaycastVolumeRenderer"); } - inline void RaycastVolumeRenderer::emitMessage(const std::string &kind, - const std::string &msg) const - { - std::cerr << " " + toString() + " " << kind + ": " + msg + "." - << std::endl; - } - - inline void - RaycastVolumeRenderer::exitOnCondition(bool condition, - const std::string &message) const - { - if (!condition) - return; - - emitMessage("ERROR", message); - exit(1); - } - } // ::ospray diff --git a/modules/mpi/render/volume/RaycastVolumeRenderer.ih b/modules/mpi/render/volume/RaycastVolumeRenderer.ih index b4cfcd3677..91ade9189b 100644 --- a/modules/mpi/render/volume/RaycastVolumeRenderer.ih +++ b/modules/mpi/render/volume/RaycastVolumeRenderer.ih @@ -16,7 +16,6 @@ #pragma once -#include "OSPMPIConfig.h" #include "common/Ray.ih" #include "lights/Light.ih" #include "math/box.ih" diff --git a/modules/mpi/render/volume/RaycastVolumeRenderer.ispc b/modules/mpi/render/volume/RaycastVolumeRenderer.ispc index 8db1cc7ef9..2165b0826a 100644 --- a/modules/mpi/render/volume/RaycastVolumeRenderer.ispc +++ b/modules/mpi/render/volume/RaycastVolumeRenderer.ispc @@ -426,10 +426,8 @@ void RaycastVolumeRenderer_renderSample(Renderer *uniform pointer, } // Attenuate the foreground and background colors by the opacity. - if (useBG && self->super.backgroundEnabled) { - const vec4f background = make_vec4f(self->super.bgColor, 1.f); - color = color.w * color + (1.0f - color.w) * background; - } + if (useBG) + color = lerp(color.w, self->super.bgColor, color); // Store the result in the sample. sample.rgb.x = color.x; diff --git a/modules/mpi/volume/DataDistributedBlockedVolume.cpp b/modules/mpi/volume/DataDistributedBlockedVolume.cpp index 51b18f8b95..4fbce57f55 100644 --- a/modules/mpi/volume/DataDistributedBlockedVolume.cpp +++ b/modules/mpi/volume/DataDistributedBlockedVolume.cpp @@ -52,9 +52,8 @@ namespace ospray { DataDistributedBlockedVolume::DataDistributedBlockedVolume() : numDDBlocks(0), - ddBlock(NULL) + ddBlock(nullptr) { - PING; } void DataDistributedBlockedVolume::updateEditableParameters() @@ -68,7 +67,7 @@ namespace ospray { } Ref transferFunction - = (TransferFunction *)getParamObject("transferFunction", NULL); + = (TransferFunction *)getParamObject("transferFunction", nullptr); auto &cppVolume = block->cppVolume; cppVolume->findParam("transferFunction",1)->set(transferFunction.ptr); @@ -89,8 +88,9 @@ namespace ospray { void DataDistributedBlockedVolume::buildAccelerator() { - std::cout << "intentionally SKIP building an accelerator for data parallel " - << "volume (this'll be done on the brick level)" << std::endl; + postStatusMsg("intentionally SKIP building an accelerator for data " + "parallel volume (this'll be done on the brick level)", + OSPRAY_MPI_VERBOSE_LEVEL); } std::string DataDistributedBlockedVolume::toString() const @@ -117,7 +117,7 @@ namespace ospray { { // Create the equivalent ISPC volume container and allocate memory for voxel // data. - if (ispcEquivalent == NULL) createEquivalentISPC(); + if (ispcEquivalent == nullptr) createEquivalentISPC(); // The block domains are in terms of the scaled regions so we must check // if the data is for the block in the scaled volume @@ -161,7 +161,7 @@ namespace ospray { void DataDistributedBlockedVolume::createEquivalentISPC() { - if (ispcEquivalent != NULL) return; + if (ispcEquivalent != nullptr) return; // Get the voxel type. voxelType = getParamString("voxelType", "unspecified"); @@ -177,9 +177,11 @@ namespace ospray { ddBlocks = getParam3i("num_dp_blocks",vec3i(4,4,4)); blockSize = divRoundUp(dimensions,ddBlocks); - std::cout << "#osp:dp: using data parallel volume of " << ddBlocks - << " blocks, blockSize is " << blockSize << std::endl; - + + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "#osp:dp: using data parallel volume of " << ddBlocks + << " blocks, blockSize is " << blockSize; + // Set the grid origin, default to (0,0,0). this->gridOrigin = getParam3f("gridOrigin", vec3f(0.f)); @@ -196,18 +198,18 @@ namespace ospray { numDDBlocks = ospcommon::reduce_mul(ddBlocks); ddBlock = new DDBlock[numDDBlocks]; - printf("=======================================================\n"); - printf("created %ix%ix%i data distributed volume blocks\n", - ddBlocks.x,ddBlocks.y,ddBlocks.z); - printf("=======================================================\n"); + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "=======================================================\n" + << "created " << ddBlocks.x << "x" << ddBlocks.y << "x" << ddBlocks.z + << " data distributed volume blocks\n" + << "======================================================="; - if (!ospray::mpi::isMpiParallel()) { + if (!mpicommon::isMpiParallel()) { throw std::runtime_error("data parallel volume, but not in mpi parallel " "mode..."); } - uint64_t numWorkers = mpi::getWorkerCount(); - // PRINT(numDDBlocks); - // PRINT(numWorkers); + + uint64_t numWorkers = mpicommon::numWorkers(); voxelType = getParamString("voxelType", "unspecified"); @@ -228,8 +230,8 @@ namespace ospray { block->numOwners = nextBlockFirstOwner - block->firstOwner; // + 1; } block->isMine - = (ospray::mpi::getWorkerRank() >= block->firstOwner) - && (ospray::mpi::getWorkerRank() < + = (mpicommon::workerRank() >= block->firstOwner) + && (mpicommon::workerRank() < (block->firstOwner + block->numOwners)); block->domain.lower = vec3i(ix,iy,iz) * blockSize; block->domain.upper = min(block->domain.lower+blockSize,dimensions); @@ -259,14 +261,17 @@ namespace ospray { volume->findParam("scaleFactor",1)->set(scaleFactor); } - printf("worker rank %li owns block %i,%i,%i (ID %i), dims %i %i %i\n", - (size_t)mpi::getWorkerRank(),ix,iy,iz, - blockID,blockDims.x,blockDims.y,blockDims.z); + postStatusMsg(OSPRAY_MPI_VERBOSE_LEVEL) + << "worker rank " << mpicommon::workerRank() + << " owns block " << ix << "," << iy << "," << iz + << " (ID " << blockID << "), dims " << blockDims.x + << " " << blockDims.y << " " << blockDims.z; + block->cppVolume = volume; - block->ispcVolume = NULL; //volume->getIE(); + block->ispcVolume = nullptr; //volume->getIE(); } else { - block->ispcVolume = NULL; - block->cppVolume = NULL; + block->ispcVolume = nullptr; + block->cppVolume = nullptr; } } diff --git a/modules/mpi/volume/DataDistributedBlockedVolume.h b/modules/mpi/volume/DataDistributedBlockedVolume.h index 95423cce1c..6e16cf8dc3 100644 --- a/modules/mpi/volume/DataDistributedBlockedVolume.h +++ b/modules/mpi/volume/DataDistributedBlockedVolume.h @@ -16,7 +16,6 @@ #pragma once -#include "OSPMPIConfig.h" #include "volume/BlockBrickedVolume.h" namespace ospray { diff --git a/modules/pluggableGeometryExample/CMakeLists.txt b/modules/pluggableGeometryExample/CMakeLists.txt new file mode 100644 index 0000000000..35bb9d8dc6 --- /dev/null +++ b/modules/pluggableGeometryExample/CMakeLists.txt @@ -0,0 +1,47 @@ +## ======================================================================== ## +## Copyright 2009-2017 Intel Corporation ## +## ## +## Licensed under the Apache License, Version 2.0 (the "License"); ## +## you may not use this file except in compliance with the License. ## +## You may obtain a copy of the License at ## +## ## +## http://www.apache.org/licenses/LICENSE-2.0 ## +## ## +## Unless required by applicable law or agreed to in writing, software ## +## distributed under the License is distributed on an "AS IS" BASIS, ## +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ## +## See the License for the specific language governing permissions and ## +## limitations under the License. ## +## ======================================================================== ## + +# Define an option to enable/disable this module. +# +# Though not actually required we guard each module 'moduleName' with +# a OSPRAY_MODULE_MODULENAME flag that enables/disables its building. +# +OPTION(OSPRAY_MODULE_BILINEAR_PATCH "Build pluggable geometry example module (a Bilinear Patch)" OFF) +IF (OSPRAY_MODULE_BILINEAR_PATCH) + + # build the actual new ospray geometry type. since this plug in + # directly into the ospray core, we'll put it into a + # '/ospray' subdirectory. + # + # This subdirectory will eventually build the + # libospray_module_.so library that defiens the actual + # core geometry type. + # + ADD_SUBDIRECTORY(ospray) + + # build the module-specific sample apps and tools. since these are + # stand-alone apps outside of the ospray core we'll put those into a + # '/ospray' subdirectory. + # + # For this module this will build the 'ospBilinearPatchViewer' + # example. If other modules come with their own + # tools/converters/importers/etc, this is where those sohuld go. + # + IF (OSPRAY_APPS_EXAMPLEVIEWER)#Must build example viewer app to build this one + ADD_SUBDIRECTORY(apps) + ENDIF() + +ENDIF (OSPRAY_MODULE_BILINEAR_PATCH) diff --git a/modules/pluggableGeometryExample/README.md b/modules/pluggableGeometryExample/README.md new file mode 100644 index 0000000000..78efd63538 --- /dev/null +++ b/modules/pluggableGeometryExample/README.md @@ -0,0 +1,33 @@ +PluggableGeometryExample: Module that demonstrates how to use the Module Concept to extend OSPRay with a new geometry type +========================================================================================================================== + + +This module serves as a simple example of + +1) how to define a new ospray geometry type and 'plug' this into ospray. In particular, how this geometry can use its "commit()" method to query data the user has added to it. + +2) how to use embree and ispc to implement this geometry type's intersect() and postIntersect() + functoins + +3) how to do this through a 'module' that is dynamically loadable; and + +4) how to use this module - and geometry - from within your own applications + +Using this example you should be able to write your own modules, with +your own geometry types. For the purpose of this geometry we have +chosen a intentionally very simple type of geometry - a bilinear +patch. + +Notes: + +- Names matter. E.g., for ospray to find the module "myModuleName" the + module _has_ to build a library of name + libospray_module_myModuleName.so. Similarly, a module that wants to + define a new geometry type "MyGeometry" _has_ to have a + OSPRAY_REGISTER_GEOMETRY(WhatEverYouCallItInYourImplementation,MyGeometry) + macro invocation in one of its cpp files. + +- This example only demonstrates how to write a _geometry_ type. It is + similarly possible to write new renderers, new camera types, new + volume types, etc. + diff --git a/apps/glutViewer/CMakeLists.txt b/modules/pluggableGeometryExample/apps/CMakeLists.txt similarity index 68% rename from apps/glutViewer/CMakeLists.txt rename to modules/pluggableGeometryExample/apps/CMakeLists.txt index d2acbec079..9c8e305233 100644 --- a/apps/glutViewer/CMakeLists.txt +++ b/modules/pluggableGeometryExample/apps/CMakeLists.txt @@ -14,41 +14,28 @@ ## limitations under the License. ## ## ======================================================================== ## -INCLUDE_DIRECTORIES( - ${CMAKE_CURRENT_LIST_DIR} - ${CMAKE_CURRENT_LIST_DIR}/script -) - -# -------------------------------------------- -# main executable -# -------------------------------------------- - -SET(APP_SRCS - glutViewer.cpp -) +# this subdirectory demonstrates on how to _use_ demo module from +# within a simple viewer -IF(OSPRAY_APPS_ENABLE_SCRIPTING) - LIST(APPEND APP_SRCS - ScriptedOSPGlutViewer.cpp - ScriptedOSPGlutViewer.h - GlutViewerScriptHandler.cpp - GlutViewerScriptHandler.h - ) -ENDIF() +# the actual example viewer. Note this viewer does _NOT_ link to the +# actual module library - the module should (and _does_, in this +# example) get loaded through ospLoadModule(...), which will make sure +# the module gets properly oaded on all worker nodes, even if those +# live on different nodes, accelerator cards, memory spaces, etc. +OSPRAY_CREATE_APPLICATION(ospBilinearPatchViewer + # the actual viewer + bilinearPatchViewer.cpp + # helper code to parse demo files + Patch.cpp + + LINK -SET(LIBS ospray ospray_commandline ospray_common - ospray_glut3d + ospray_imgui3d ospray_minisg ospray_importer ${OPENGL_LIBRARIES} ${GLUT_LIBRARIES} ) - -IF (OSPRAY_APPS_ENABLE_SCRIPTING) - LIST(APPEND LIBS ospray_script) -ENDIF() - -OSPRAY_CREATE_APPLICATION(ospGlutViewer ${APP_SRCS} LINK ${LIBS}) diff --git a/modules/pluggableGeometryExample/apps/CommandLine.h b/modules/pluggableGeometryExample/apps/CommandLine.h new file mode 100644 index 0000000000..54249c8fa7 --- /dev/null +++ b/modules/pluggableGeometryExample/apps/CommandLine.h @@ -0,0 +1,57 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace bilinearPatch { + + // use ospcommon for vec3f etc + using namespace ospcommon; + + /*! helper class to parse command-line arguments */ + struct CommandLine { + CommandLine(int ac, const char **av); + std::vector inputFiles; + }; + + inline CommandLine::CommandLine(int ac, const char **av) + { + for (int i=1;i &patches, + const std::string &patchFileName) + { + FILE *file = fopen(patchFileName.c_str(),"r"); + if (!file) + throw std::runtime_error("could not open input file '"+patchFileName+"'"); + + std::vector parsedPoints; + + size_t numPatchesRead = 0; + static const size_t lineSize = 10000; + char line[lineSize]; + while (fgets(line,10000,file)) { + // try to parse three floats... + vec3f p; + int rc = sscanf(line,"%f %f %f",&p.x,&p.y,&p.z); + if (rc != 3) + // could not read a point - must be a empty or comment line; just ignore + continue; + + // add this point to list of parsed points ... + parsedPoints.push_back(p); + + // ... and if we have four of them, we have a patch! + if (parsedPoints.size() == 4) { + patches.push_back(Patch(parsedPoints[0],parsedPoints[1], + parsedPoints[2],parsedPoints[3])); + parsedPoints.clear(); + ++numPatchesRead; + } + } + std::cout << "#osp:blp: done parsing " << patchFileName + << " (" << numPatchesRead << " patches)" << std::endl; + } + + /*! create a list of patches from the list of given file names + (each fiename is supposed to a patch file. if no patches could + be read, create a simple test case. 'bounds' will be the world + bouding box of all control points in the returned patch + list */ + std::vector readPatchesFromFiles(const std::vector &fileNames, + box3f &bounds) + { + std::vector patches; + for (auto fileName : fileNames) + readPatchesFromFile(patches,fileName); + + if (patches.empty()) { + std::cout << "#osp.blp: no input files specified - creating default path" << std::endl; + patches.push_back(Patch(vec3f(0.f,1.f,0.f), + vec3f(0.f,0.f,1.f), + vec3f(1.f,0.f,0.f), + vec3f(1.f,1.f,1.f))); + } + + bounds = empty; + for (auto patch : patches) { + bounds.extend(patch.v00); + bounds.extend(patch.v01); + bounds.extend(patch.v10); + bounds.extend(patch.v11); + } + std::cout << "##################################################################" << std::endl; + std::cout << "#osp:blp: done parsing input files" << std::endl; + std::cout << "#osp:blp: found a total of " << patches.size() << " patches ..." << std::endl; + std::cout << "#osp:blp: ... with world bounds of " << bounds << std::endl; + std::cout << "##################################################################" << std::endl; + return patches; + } + + } // ::ospray::bilinearPatch +} // ::ospray diff --git a/modules/pluggableGeometryExample/apps/Patch.h b/modules/pluggableGeometryExample/apps/Patch.h new file mode 100644 index 0000000000..ca309e5e4f --- /dev/null +++ b/modules/pluggableGeometryExample/apps/Patch.h @@ -0,0 +1,63 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include +#include +#include +#include + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace bilinearPatch { + + // use ospcommon for vec3f etc + using namespace ospcommon; + + struct Patch { + Patch(const vec3f &v00, + const vec3f &v01, + const vec3f &v10, + const vec3f &v11) + : v00(v00), v01(v01), v10(v10), v11(v11) + {} + + vec3f v00, v01, v10, v11; + }; + + /*! parse a '.patch' file, and add its contents to the given list of + patches */ + void readPatchesFromFile(std::vector &patches, + const std::string &patchFileName); + + /*! create a list of patches from the list of given file names + (each fiename is supposed to a patch file. if no patches could + be read, create a simple test case. 'bounds' will be the world + bouding box of all control points in the returned patch + list */ + std::vector readPatchesFromFiles(const std::vector &fileNames, + box3f &worldBounds); + + } // ::ospray::bilinearPatch +} // ::ospray diff --git a/modules/pluggableGeometryExample/apps/bilinearPatchViewer.cpp b/modules/pluggableGeometryExample/apps/bilinearPatchViewer.cpp new file mode 100644 index 0000000000..d4596ff3c2 --- /dev/null +++ b/modules/pluggableGeometryExample/apps/bilinearPatchViewer.cpp @@ -0,0 +1,95 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include + +#include +#include "CommandLine.h" +#include "Patch.h" + +#include +#include +#include +#include +#include "common/commandline/Utility.h" +#include "exampleViewer/widgets/imguiViewer.h" + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace bilinearPatch { + + ospcommon::vec3f translate; + ospcommon::vec3f scale; + bool lockFirstFrame = false; + + // use ospcommon for vec3f etc + using namespace ospcommon; + + extern "C" int main(int ac, const char **av) + { + // initialize ospray (this also takes all ospray-related args + // off the command-line) + ospInit(&ac,av); + + ospray::imgui3D::init(&ac,av); + + std::deque bbox; + std::deque models; + ospray::cpp::Renderer renderer("ao"); + ospray::cpp::Camera camera("perspective"); + + // parse the commandline; complain about anything we do not + // recognize + CommandLine args(ac,av); + + // import the patches from the sample files (creates a default + // patch if no files were specified) + box3f worldBounds; + std::vector patches = readPatchesFromFiles(args.inputFiles,worldBounds); + + ospLoadModule("bilinear_patches"); + + ospray::cpp::Data data(patches.size()*12,OSP_FLOAT,patches.data()); + ospray::cpp::Geometry geometry("bilinear_patches"); + geometry.set("patches",data); + geometry.commit(); + + ospray::cpp::Model model; + model.addGeometry(geometry); + model.commit(); + + models.push_back(model); + bbox.push_back(worldBounds); + + ospray::ImGuiViewer window(bbox, models, renderer, camera); + + window.setScale(scale); + window.setLockFirstAnimationFrame(lockFirstFrame); + window.setTranslation(translate); + window.create("ospBilinearPatchViewer: OSPRay module example app"); + + ospray::imgui3D::run(); + } + + } // ::ospray::bilinearPatch +} // ::ospray diff --git a/modules/pluggableGeometryExample/examples/test1.patches b/modules/pluggableGeometryExample/examples/test1.patches new file mode 100644 index 0000000000..17b8cc2b58 --- /dev/null +++ b/modules/pluggableGeometryExample/examples/test1.patches @@ -0,0 +1,19 @@ +# a sample file of patches. any line that does not contain three +# floats is ignored, and any set of four successive 3-float lines +# form one bi-linear patch + +0 0 0 +1 0 0 +0 0 1 +1 1 1 + + +2 2 2 +2 2 3 +2 3 2 +2 3 3 + +2 0 1 +2 1 0 +3 1 1 +3 0 0 diff --git a/modules/pluggableGeometryExample/ospray/CMakeLists.txt b/modules/pluggableGeometryExample/ospray/CMakeLists.txt new file mode 100644 index 0000000000..dddd5ecacd --- /dev/null +++ b/modules/pluggableGeometryExample/ospray/CMakeLists.txt @@ -0,0 +1,58 @@ +## ======================================================================== ## +## Copyright 2009-2017 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. ## +## ======================================================================== ## + +# this subdirectory builds the actual ospray plugin into a library +# "libospray_module_.so". +# +# This shared library can depend on libospray, but should _not_ assume +# that it is living in the same address space as the application: When +# running in MPI mode ospray will, upon encountering the +# ospLoadModule(""), actually load and use this library on +# every worker node (which will typically not run the application). As +# such, the library should _not_ "commuincate" with the application +# through global variables or globally defind functions, and instead +# communicate solely through the ospNewGeometry(...), ospSet...(), +# ospCommit(...) etc functionality provided by ospray. + + +# build the actual shared library that defines the geometry. +# +# Note the name is important: In order for ospray to properly find and +# initialize a module referenced by a call to +# "ospLoadModule() this module _has_ to +# +# a) be called libospray_module_.so, and +# b) contain a (extern C linkage) initializatoin routine named +# void ospray_init_module_() +# +OSPRAY_CREATE_LIBRARY(ospray_module_bilinear_patches + # the cpp file that contains all the plugin code - parsing + # parameters in ospCommit(), creating and registering the object, + # building accel strcutures, etc + geometry/BilinearPatches.cpp + + # the ispc file that contains the plugins for all vector code - ie, + # for ray-primitive intersection and 'postIntersect' (reporting info + # on a previously computed ray-prim intersection) + geometry/BilinearPatches.ispc + + # and finally, the module init code (not doing much, but must be there) + moduleInit.cpp + + # this depends on ospray core: + LINK + ospray +) diff --git a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp new file mode 100644 index 0000000000..8820ccad99 --- /dev/null +++ b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.cpp @@ -0,0 +1,105 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "BilinearPatches.h" +// 'export'ed functions from the ispc file: +#include "BilinearPatches_ispc.h" +// ospray core: +#include + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace blp { + + /*! constructor - will create the 'ispc equivalent' */ + BilinearPatches::BilinearPatches() + { + /*! create the 'ispc equivalent': ie, the ispc-side class that + implements all the ispc-side code for intersection, + postintersect, etc. See BilinearPatches.ispc */ + this->ispcEquivalent = ispc::BilinearPatches_create(this); + + // note we do _not_ yet do anything else here - the actual input + // data isn't available to use until 'commit()' gets called + } + + /*! destructor - supposed to clean up all alloced memory */ + BilinearPatches::~BilinearPatches() + { + ispc::BilinearPatches_destroy(ispcEquivalent); + } + + /*! commit - this is the function that parses all the parameters + that the app has proivded for this geometry. In this simple + example we're looking for a single parameter named 'patches', + which is supposed to contain a data array of all the patches' + control points */ + void BilinearPatches::commit() + { + this->patchesData = getParamData("patches"); + + /* assert that some valid input data is available */ + if (!this->patchesData) { + + std::cout << "#osp.blp: Warning: no input patches provided " + << "for bilinear_patches geometry" << std::endl; + return; + } + } + + /*! 'finalize' is what ospray calls when everything is set and + done, and a actual user geometry has to be built */ + void BilinearPatches::finalize(Model *model) + { + // sanity check if a patches data was actually set! + if (!patchesData) + return; + + // look at the data we were provided with .... + size_t numPatchesInInput = patchesData->numBytes / sizeof(Patch); + std::cout << "#osp.blp: found " << numPatchesInInput + << " patches in data array" << std::endl; + + /* get the acual 'raw' pointer to the data (ispc doesn't konw + what to do with the 'Data' abstraction calss */ + const void *patchesDataPointer = patchesData->data; + ispc::BilinearPatches_finalize(getIE(),model->getIE(), + (float*)patchesDataPointer, + numPatchesInInput); + } + + + /*! maybe one of the most important parts of this example: this + macro 'registers' the BilinearPatches class under the ospray + geometry type name of 'bilinear_patches'. + + It is _this_ name that one can now (assuming the module has + been loaded with ospLoadModule(), of course) create geometries + with; i.e., + + OSPGeometry geom = ospNewGeometry("bilinear_patches") ; + */ + OSP_REGISTER_GEOMETRY(BilinearPatches,bilinear_patches); + + } // ::ospray::bilinearPatch +} // ::ospray diff --git a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h new file mode 100644 index 0000000000..3db77ed3cb --- /dev/null +++ b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.h @@ -0,0 +1,93 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +/*! \file ospray/geometry/BilinearPatches.h Defines a new ospray + geometry type (of name 'bilinear_patches'). Input to the geometry is + a single data array (named 'patches') that consists of for vec3fs + per patch. */ + +// ospcomon: vec3f, box3f, etcpp - generic helper stuff +#include +#include +// ospray: everything that's related to the ospray ray tracing core +#include +#include + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace blp { + // import ospcommon component - vec3f etc + using namespace ospcommon; + + /*! a geometry type that implements (a set of) bi-linear + patches. This implements a new ospray geometry, and as such has + to + + a) derive from ospray::Geometry + b) implement a 'commit()' message that parses the + parameters/data arrays that the app has specified as inputs + c) create an actual ospray geometry instance with the + proper intersect() and postIntersect() functions. + + Note that how this class is called does not particularly matter; + all that matters is under which name it is registered in the cpp + file (see comments on OSPRAY_REGISTER_GEOMETRY) + */ + struct BilinearPatches : public ospray::Geometry { + + /*! data layout of a single patch. note we do not actually use + this class anywhere on the c++ side of this example, it is + only for illustrative purposes. The input data should come + as a data array of N such patches (we compute N + automatically based on the size of this array) */ + struct Patch { + vec3f controlPoint[2][2]; + }; + + /*! constructor - will create the 'ispc equivalent' */ + BilinearPatches(); + + /*! destructor - supposed to clean up all alloced memory */ + virtual ~BilinearPatches(); + + /*! the commit() message that gets called upon the app calling + "ospCommit()" */ + virtual void commit() override; + + /*! 'finalize' is what ospray calls when everything is set and + done, and a actual user geometry has to be built */ + virtual void finalize(Model *model) override; + + /*! the input data array. the data array contains a list of + patches, each of which consists of four vec3fs. Note in this + example we do not particularly care about whether this comes + as a plain array of floats (with 12 floats per patch), or as + a array of vec3fs. */ + Ref patchesData; + }; + + } // ::ospray::bilinearPatch +} // ::ospray + diff --git a/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.ispc b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.ispc new file mode 100644 index 0000000000..90b6e63bf6 --- /dev/null +++ b/modules/pluggableGeometryExample/ospray/geometry/BilinearPatches.ispc @@ -0,0 +1,263 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +// ospray +#include "math/vec.ih" +#include "math/box.ih" +#include "math/LinearSpace.ih" +#include "common/Ray.ih" +#include "common/Model.ih" +#include "ospray/geometry/Geometry.ih" +// embree +#include "embree2/rtcore.isph" +#include "embree2/rtcore_scene.isph" +#include "embree2/rtcore_geometry_user.isph" + +/*! input data for a single patch */ +struct Patch { + vec3f v00, v01, v10, v11; +}; + +struct BilinearPatches { + /*! inherit from "Geometry" class: since ISPC doesn't support + inheritance we simply put the "parent" class as the first + member; this way any typecast to the parent class will get the + right members (including 'virtual' function pointers, etc) */ + Geometry super; + + size_t numPatches; + Patch *uniform patchArray; +}; + + + +static void BilinearPatches_postIntersect(uniform Geometry *uniform geometry, + uniform Model *uniform model, + varying DifferentialGeometry &dg, + const varying Ray &ray, + uniform int64 flags) +{ + BilinearPatches *uniform self = (BilinearPatches *uniform)geometry; + dg.Ng = dg.Ns = normalize(ray.Ng); + + if (flags & DG_MATERIALID) { + dg.materialID = 0; + dg.material = self->super.material; + } +} + + +/*! create a new ispc equivalent for the C++ class (cppEquivalent + points back, if we ever need this. this returns a void * that the + c++ side can use to call back to us when needed. Note this return + values _has_ to be a untyped 'void*' because ISPC cannot currently + export 'real' class types - with function pointers etcpp - to a c + header file */ +export void *uniform BilinearPatches_create(void *uniform cppEquivalent) +{ + BilinearPatches *uniform self = uniform new uniform BilinearPatches; + + /* recursively call the 'inherited' constructor, and pass our + 'overridden' functions for intersect and postintersect. note we + do not have to set the 'intersect' and 'occluded' functions + because we use embree to do the traversal for us (see + BilinearPatches_set()) below */ + Geometry_Constructor(&self->super,cppEquivalent, + BilinearPatches_postIntersect, + NULL,0,NULL); + return self; +} + +export void BilinearPatches_destroy(void *uniform _self) +{ + /* _actually_ this should also destroy the created embree geometry + here; not doing this to keep the code as small as possible, but + usually this is where it should be done */ + BilinearPatches *uniform self = (BilinearPatches *uniform)_self; + delete self; +} + + + + +/*! to be able to have embree build a data structure over us we have + to be able to tell it the bounding box for each primitimve. this + is the callback embree calls for querying the (uniform) bounding + box for a single given primitmive 'primID' */ +unmasked void BilinearPatches_bounds(BilinearPatches *uniform self, + uniform size_t primID, + uniform box3fa &bbox) +{ + print("bounds\n"); + Patch *uniform patch = self->patchArray+primID; + bbox = box_extend(box_extend(box_extend(box_extend(make_box3fa_empty(),make_vec3fa(patch->v00)), + make_vec3fa(patch->v01)), + make_vec3fa(patch->v10)), + make_vec3fa(patch->v11)); +} + + + + +bool recursiveBisection(varying Ray &ray, + const varying LinearSpace3f &frame, + const uniform vec3f p00, + const uniform vec3f p01, + const uniform vec3f p10, + const uniform vec3f p11, + const uniform float u0, + const uniform float u1, + const uniform float v0, + const uniform float v1, + uniform int recursionsLeft) +{ + // compute signs to x plane + const float x0 = dot(p00-ray.org,frame.vx); + const float x1 = dot(p01-ray.org,frame.vx); + const float x2 = dot(p10-ray.org,frame.vx); + const float x3 = dot(p11-ray.org,frame.vx); + const float min_x = min(min(x0,x1),min(x2,x3)); + if (min_x > 0.f) return false; + const float max_x = max(max(x0,x1),max(x2,x3)); + if (max_x < 0.f) return false; + + // compute signs to y plane + const float y0 = dot(p00-ray.org,frame.vy); + const float y1 = dot(p01-ray.org,frame.vy); + const float y2 = dot(p10-ray.org,frame.vy); + const float y3 = dot(p11-ray.org,frame.vy); + const float min_y = min(min(y0,y1),min(y2,y3)); + if (min_y > 0.f) return false; + const float max_y = max(max(y0,y1),max(y2,y3)); + if (max_y < 0.f) return false; + + // distance test + const float t0 = dot(p00-ray.org,frame.vz); + const float t1 = dot(p01-ray.org,frame.vz); + const float t2 = dot(p10-ray.org,frame.vz); + const float t3 = dot(p11-ray.org,frame.vz); + // const float min_t = min(min(t0,t1),min(t2,t3)); + // if (min_t > 0.f) return false; + const float max_t = max(max(t0,t1),max(t2,t3)); + if (max_t < 0.f) return false; + + const uniform float uc = 0.5f*(u0+u1); + const uniform float vc = 0.5f*(v0+v1); + + if (recursionsLeft == 0) { + const float t = 0.25*(t0+t1+t2+t3); + if (t < ray.t0 || t >= ray.t) return false; + + ray.t = t; + ray.u = uc; + ray.v = vc; + + const vec3f pu0 = p00+ray.u*(p01-p00); + const vec3f pu1 = p10+ray.u*(p11-p10); + + const vec3f pv0 = p00+ray.v*(p10-p00); + const vec3f pv1 = p01+ray.v*(p11-p01); + + ray.Ng = cross(pu1-pu0,pv1-pv0); + return true; + } else { + const uniform vec3f p0c = 0.5f*(p01+p00); + const uniform vec3f p1c = 0.5f*(p11+p10); + const uniform vec3f pc0 = 0.5f*(p10+p00); + const uniform vec3f pc1 = 0.5f*(p11+p01); + const uniform vec3f pcc = 0.25f*(p00+p01+p10+p11); + + bool hit = false; + hit |= recursiveBisection(ray,frame, p00,p0c,pc0,pcc, u0,uc, v0,vc, recursionsLeft-1); + hit |= recursiveBisection(ray,frame, pc0,pcc,p10,p1c, u0,uc, vc,v1, recursionsLeft-1); + + hit |= recursiveBisection(ray,frame, p0c,p01,pcc,pc1, uc,u1, v0,vc, recursionsLeft-1); + hit |= recursiveBisection(ray,frame, pcc,pc1,p1c,p11, uc,u1, vc,v1, recursionsLeft-1); + return hit; + } +} + + +/*! this is the function callback the embree calls to compute a single + ray-primitive intersection. For sake of simplicity we'll use a very, + very simple recursive test here - this is neither fast nor elegant, + but does show that you can do mere arbitray stuff in a ISPC kernel, + including 'true' recusion. */ +void BilinearPatches_intersect(BilinearPatches *uniform self, + varying Ray &ray, + uniform size_t primID) +{ + Patch *uniform patch = self->patchArray+primID; + + // create a simple coordinate system with x and y directions + // orthogonal to the ray + const LinearSpace3f coordSys = frame(ray.dir); + + if (recursiveBisection(ray,coordSys, + patch->v00,patch->v01,patch->v10,patch->v11, + 0.f,1.f, 0.f,1.f, + /* number of bisections */ 10)) { + ray.geomID = self->super.geomID; + ray.primID = primID; + } +} + + + +/*! the function called by BilinearPatches::commit() to pass the + actual user data over. for simplicity we assume that this functoin + gets called exactly once; in practice a user may also _change_ an + existing class'es data members, but handling this would make this + code significantly larger */ +export void BilinearPatches_finalize(void *uniform _self, + void *uniform _model, + float *uniform patchesDataMemory, + uniform size_t numPatches) +{ + // first, typecast to our 'real' type. since ispc can't export real + // types to c we have to pass 'self' in as a void*, and typecast + // here. + BilinearPatches *uniform self = (BilinearPatches *uniform)_self; + + // set our internal data. + self->numPatches = numPatches; + self->patchArray = (Patch *uniform)patchesDataMemory; + + // ... and let embree build a bvh, with 'numPatches' primitmives and + // our function pointers for ray-prim interseciton and + // prim-boundingbox computation callsbacks + Model *uniform model = (Model *uniform)_model; + + // create a new embree geometry with numpathces prims, in the model + // that this goemetry is in. + uint32 uniform geomID = rtcNewUserGeometry(model->embreeSceneHandle,numPatches); + + // set 'us' as user data (this will be the first arg in intersect() + // and computebounds() callbacks + rtcSetUserData(model->embreeSceneHandle,geomID,self); + + // set function pointers: + rtcSetBoundsFunction(model->embreeSceneHandle,geomID, + (uniform RTCBoundsFunc)&BilinearPatches_bounds); + rtcSetIntersectFunction(model->embreeSceneHandle,geomID, + (uniform RTCIntersectFuncVarying)&BilinearPatches_intersect); + rtcSetOccludedFunction(model->embreeSceneHandle,geomID, + (uniform RTCOccludedFuncVarying)&BilinearPatches_intersect); +} + + + + diff --git a/modules/pluggableGeometryExample/ospray/moduleInit.cpp b/modules/pluggableGeometryExample/ospray/moduleInit.cpp new file mode 100644 index 0000000000..11c840248d --- /dev/null +++ b/modules/pluggableGeometryExample/ospray/moduleInit.cpp @@ -0,0 +1,60 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +/*! \file ospray/moduleInit \brief Defines the module initialization callback */ + +#include "geometry/BilinearPatches.h" + +/*! _everything_ in the ospray core universe should _always_ be in the + 'ospray' namespace. */ +namespace ospray { + + /*! though not required, it is good practice to put any module into + its own namespace (isnide of ospray:: ). Unlike for the naming of + library and init function, the naming for this namespace doesn't + particularlly matter. E.g., 'bilinearPatch', 'module_blp', + 'bilinar_patch' etc would all work equally well. */ + namespace blp { + + /*! the actual module initialization function. This function gets + called exactly once, when the module gets first loaded through + 'ospLoadModule'. Notes: + + a) this function does _not_ get called if the application directly + links to libospray_module_ (which it + shouldn't!). Modules should _always_ be loaded through + ospLoadModule. + + b) it is _not_ valid for the module to do ospray _api_ calls + inside such an intiailzatoin function. Ie, you can _not_ do a + ospLoadModule("anotherModule") from within this function (but + you could, of course, have this module dynamically link to the + other one, and call its init function) + + c) in order for ospray to properly resolve that symbol, it + _has_ to have extern C linkage, and it _has_ to correspond to + name of the module and shared library containing this module + (see comments regarding library name in CMakeLists.txt) + */ + extern "C" void ospray_init_module_bilinear_patches() + { + std::cout << "#osp: initializing the 'bilinear_patches' module" << std::endl; + /* nothing to do, actually - this is only an example */ + } + + } // ::ospray::bilinearPatch +} // ::ospray + diff --git a/ospray/CMakeLists.txt b/ospray/CMakeLists.txt index eb0fba462d..6427f7c79d 100644 --- a/ospray/CMakeLists.txt +++ b/ospray/CMakeLists.txt @@ -179,6 +179,9 @@ SET(OSPRAY_SOURCES render/simpleAO/SimpleAOMaterial.cpp render/simpleAO/SimpleAOMaterial.ih render/simpleAO/SimpleAOMaterial.ispc + render/scivis/lightAlpha.ispc + render/scivis/volumeIntegration.ispc + render/scivis/surfaceShading.ispc render/scivis/SciVisRenderer.ispc render/scivis/SciVisMaterial.ispc render/scivis/SciVisMaterial.ih @@ -377,10 +380,27 @@ SET(OSPRAY_LIBS IF (WIN32) LIST(APPEND OSPRAY_LIBS ws2_32) - # export ISPC SDK symbols on Windows - IF (OSPRAY_BUILD_ISA STREQUAL "ALL") - LIST(APPEND OSPRAY_SOURCES ospray.def) - ENDIF() + + # ------------------------------------------------------- + # export ISPC SDK symbols on Windows ## + # ------------------------------------------------------- + + FILE(READ def_header.txt OSPRAY_DEF) + FILE(READ ispc_symbols.txt OSPRAY_ISPC_SYMBOLS_IN) + + FOREACH(isa ${OSPRAY_ISPC_TARGET_LIST}) + STRING(REPLACE "-i32x16" "" isa ${isa}) # strip avx512(knl|skx)-i32x16 + # add isa suffix + STRING(REPLACE "," ${isa} OSPRAY_ISPC_SYMBOLS ${OSPRAY_ISPC_SYMBOLS_IN}) + STRING(APPEND OSPRAY_DEF ${OSPRAY_ISPC_SYMBOLS}) + ENDFOREACH() + + FILE(WRITE ${CMAKE_CURRENT_BINARY_DIR}/ospray.def.in "${OSPRAY_DEF}") + + # changes ospray.def only if content changed, avoids unnecessary re-linking + CONFIGURE_FILE(${CMAKE_CURRENT_BINARY_DIR}/ospray.def.in ospray.def COPYONLY) + + LIST(APPEND OSPRAY_SOURCES ospray.def) ENDIF() OSPRAY_CREATE_LIBRARY(ospray ${OSPRAY_SOURCES} LINK ${OSPRAY_LIBS} COMPONENT lib) diff --git a/ospray/api/API.cpp b/ospray/api/API.cpp index 88c2951f33..5d3cf4a8ab 100644 --- a/ospray/api/API.cpp +++ b/ospray/api/API.cpp @@ -14,6 +14,10 @@ // limitations under the License. // // ======================================================================== // +//ospcommon +#include "ospcommon/utility/OnScopeExit.h" + +//ospray #include "common/OSPCommon.h" #include "common/Util.h" #include "include/ospray/ospray.h" @@ -28,64 +32,96 @@ # include // for getpid #endif -# define LOG(a) if (ospray::logLevel() > 2) { std::cout << "#ospray: " << a << std::endl; } +using ospray::api::Device; +using ospray::api::deviceIsSet; +using ospray::api::currentDevice; /*! \file api.cpp implements the public ospray api functions by routing them to a respective \ref device */ -std::string getPidString() { +inline std::string getPidString() +{ char s[100]; sprintf(s, "(pid %i)", getpid()); return s; } -#define ASSERT_DEVICE() if (!ospray::api::Device::current) \ - throw std::runtime_error("OSPRay not yet initialized " \ - "(most likely this means you tried to " \ - "call an ospray API function before " \ - "first calling ospInit())"+getPidString()); +inline std::string toString(OSPObject obj) +{ + if (obj) + return ((ospray::ManagedObject*)obj)->toString(); + else + return "nullptr"; +} + +#define ASSERT_DEVICE() if (!deviceIsSet()) \ + throw std::runtime_error("OSPRay not yet initialized " \ + "(most likely this means you tried to " \ + "call an ospray API function before " \ + "first calling ospInit())" + getPidString()); + +#define OSPRAY_CATCH_BEGIN try { \ + auto *fcn_name_ = __PRETTY_FUNCTION__; \ + ospcommon::utility::OnScopeExit guard([&]() { \ + postTraceMsg(fcn_name_); \ + }); + +#define OSPRAY_CATCH_END(a) \ + } catch (const std::bad_alloc &e) { \ + handleError(OSP_OUT_OF_MEMORY, "OSPRay was unable to allocate memory"); \ + return a; \ + } catch (const std::runtime_error &e) { \ + handleError(OSP_UNKNOWN_ERROR, e.what()); \ + return a; \ + } catch (...) { \ + handleError(OSP_UNKNOWN_ERROR, "an unrecognized exception was caught"); \ + return a; \ + } using namespace ospray; -inline ospray::api::Device *createMpiDevice() +inline Device *createMpiDevice(const std::string &type) { - ospray::api::Device *device = nullptr; + Device *device = nullptr; try { - device = ospray::api::Device::createDevice("mpi"); + device = Device::createDevice(type.c_str()); } catch (const std::runtime_error &) { try { ospLoadModule("mpi"); - device = ospray::api::Device::createDevice("mpi"); - } catch (const std::runtime_error &) { - std::string error_msg = "Cannot create a device of type 'mpi'! Make sure " - "you have enabled the OSPRAY_MODULE_MPI CMake " - "variable in your build of OSPRay."; - throw std::runtime_error(error_msg); + device = Device::createDevice(type.c_str()); + } catch (const std::runtime_error &err) { + std::stringstream error_msg; + error_msg << "Cannot create a device of type '" << type << "'! Make sure " + << "you have enabled the OSPRAY_MODULE_MPI CMake " + << "variable in your build of OSPRay.\n"; + error_msg << "(Reason device creation failed: " << err.what() << ')'; + throw std::runtime_error(error_msg.str()); } } return device; } -extern "C" void ospInit(int *_ac, const char **_av) +extern "C" OSPError ospInit(int *_ac, const char **_av) +OSPRAY_CATCH_BEGIN { - if (ospray::api::Device::current) { - throw std::runtime_error("OSPRay error: device already exists " - "(did you call ospInit twice?)"); + auto ¤tDevice = Device::current; + + if (currentDevice) { + throw std::runtime_error("device already exists [ospInit() called twice?]"); } auto OSP_MPI_LAUNCH = getEnvVar("OSPRAY_MPI_LAUNCH"); if (OSP_MPI_LAUNCH.first) { - postErrorMsg("#osp: launching ospray mpi ring - " + postStatusMsg("#osp: launching ospray mpi ring - " "make sure that mpd is running"); - auto *mpiDevice = createMpiDevice(); - ospray::api::Device::current = mpiDevice; - mpiDevice->findParam("mpiMode", true)->set("mpi-launch"); - mpiDevice->findParam("launchCommand", true) - ->set(OSP_MPI_LAUNCH.second.c_str()); + currentDevice = createMpiDevice("mpi_offload"); + currentDevice->findParam("mpiMode", true)->set("mpi-launch"); + currentDevice->findParam("launchCommand", true) + ->set(OSP_MPI_LAUNCH.second.c_str()); } if (_ac && _av) { @@ -93,7 +129,18 @@ extern "C" void ospInit(int *_ac, const char **_av) std::string av(_av[i]); if (av == "--osp:coi") { - throw std::runtime_error("OSPRay's COI device is no longer supported!"); + handleError(OSP_INVALID_ARGUMENT, + "OSPRay's COI device is no longer supported!"); + return OSP_INVALID_ARGUMENT; + } + + auto moduleSwitch = av.substr(0, 13); + if (moduleSwitch == "--osp:module:") { + removeArgs(*_ac,(char **&)_av,i,1); + + auto moduleName = av.substr(13); + loadLocalModule(moduleName); + --i; continue; } @@ -102,10 +149,9 @@ extern "C" void ospInit(int *_ac, const char **_av) if (deviceSwitch == "--osp:device:") { removeArgs(*_ac,(char **&)_av,i,1); auto deviceName = av.substr(13); - + try { - auto *device = ospray::api::Device::createDevice(deviceName.c_str()); - ospray::api::Device::current = device; + currentDevice = Device::createDevice(deviceName.c_str()); } catch (const std::runtime_error &) { throw std::runtime_error("Failed to create device of type '" + deviceName + "'! Perhaps you spelled the " @@ -116,10 +162,18 @@ extern "C" void ospInit(int *_ac, const char **_av) continue; } - if (av == "--osp:mpi") { + if (av == "--osp:mpi" || av == "--osp:mpi-offload") { removeArgs(*_ac,(char **&)_av,i,1); - auto *mpiDevice = createMpiDevice(); - ospray::api::Device::current = mpiDevice; + if (!currentDevice) + currentDevice = createMpiDevice("mpi_offload"); + --i; + continue; + } + + if (av == "--osp:mpi-distributed") { + removeArgs(*_ac,(char **&)_av,i,1); + if (!currentDevice) + currentDevice = createMpiDevice("mpi_distributed"); --i; continue; } @@ -130,10 +184,9 @@ extern "C" void ospInit(int *_ac, const char **_av) const char *launchCommand = strdup(_av[i+1]); removeArgs(*_ac,(char **&)_av,i,2); - auto *mpiDevice = createMpiDevice(); - ospray::api::Device::current = mpiDevice; - mpiDevice->findParam("mpiMode", true)->set("mpi-launch"); - mpiDevice->findParam("launchCommand", true)->set(launchCommand); + currentDevice = createMpiDevice("mpi_offload"); + currentDevice->findParam("mpiMode", true)->set("mpi-launch"); + currentDevice->findParam("launchCommand", true)->set(launchCommand); --i; continue; } @@ -146,11 +199,24 @@ extern "C" void ospInit(int *_ac, const char **_av) } removeArgs(*_ac,(char **&)_av,i,1); - auto *mpiDevice = createMpiDevice(); - ospray::api::Device::current = mpiDevice; - mpiDevice->findParam("mpiMode", true)->set("mpi-listen"); - mpiDevice->findParam("fileNameToStorePortIn", true) - ->set(fileNameToStorePortIn); + currentDevice = createMpiDevice("mpi_offload"); + currentDevice->findParam("mpiMode", true)->set("mpi-listen"); + currentDevice->findParam("fileNameToStorePortIn", true) + ->set(fileNameToStorePortIn?fileNameToStorePortIn:""); + --i; + continue; + } + + const char *connectArgName = "--osp:mpi-connect"; + if (!strncmp(_av[i], connectArgName, strlen(connectArgName))) { + std::string portName = _av[i+1]; + removeArgs(*_ac,(char **&)_av,i,2); + + if (!currentDevice) + currentDevice = createMpiDevice("mpi_offload"); + + currentDevice->findParam("mpiMode", true)->set("mpi-connect"); + currentDevice->findParam("portName", true)->set(portName.c_str()); --i; continue; } @@ -158,173 +224,188 @@ extern "C" void ospInit(int *_ac, const char **_av) } // no device created on cmd line, yet, so default to localdevice - if (!ospray::api::Device::current) { - ospray::api::Device::current = new ospray::api::LocalDevice; - } + if (!deviceIsSet()) + currentDevice = new ospray::api::LocalDevice; ospray::initFromCommandLine(_ac,&_av); - ospray::api::Device::current->commit(); + currentDevice->commit(); + + return OSP_NO_ERROR; +} +OSPRAY_CATCH_END(OSP_INVALID_OPERATION) + +extern "C" OSPDevice ospNewDevice(const char *deviceType) +OSPRAY_CATCH_BEGIN +{ + return (OSPDevice)Device::createDevice(deviceType); } +OSPRAY_CATCH_END(nullptr) +// for backward compatibility, will be removed in future extern "C" OSPDevice ospCreateDevice(const char *deviceType) { - return (OSPDevice)ospray::api::Device::createDevice(deviceType); + return ospNewDevice(deviceType); } extern "C" void ospSetCurrentDevice(OSPDevice _device) +OSPRAY_CATCH_BEGIN { - auto *device = (ospray::api::Device*)_device; + auto *device = (Device*)_device; if (!device->isCommitted()) { throw std::runtime_error("You must commit the device before using it!"); } - ospray::api::Device::current = device; + Device::current = device; } +OSPRAY_CATCH_END() extern "C" OSPDevice ospGetCurrentDevice() +OSPRAY_CATCH_BEGIN { - return (OSPDevice)ospray::api::Device::current.ptr; + return (OSPDevice)Device::current.ptr; } +OSPRAY_CATCH_END(nullptr) - -/*! destroy a given frame buffer. - - due to internal reference counting the framebuffer may or may not be deleted immediately -*/ extern "C" void ospFreeFrameBuffer(OSPFrameBuffer fb) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(fb != nullptr); - ospray::api::Device::current->release(fb); + currentDevice().release(fb); } +OSPRAY_CATCH_END() extern "C" OSPFrameBuffer ospNewFrameBuffer(const osp::vec2i &size, const OSPFrameBufferFormat mode, const uint32_t channels) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - return ospray::api::Device::current->frameBufferCreate((vec2i&)size, mode, channels); + return currentDevice().frameBufferCreate((vec2i&)size, mode, channels); } +OSPRAY_CATCH_END(nullptr) -//! load module \ from shard lib libospray_module_\.so -extern "C" int32_t ospLoadModule(const char *moduleName) +extern "C" OSPError ospLoadModule(const char *moduleName) +OSPRAY_CATCH_BEGIN { - if (ospray::api::Device::current) { - return ospray::api::Device::current->loadModule(moduleName); + if (deviceIsSet()) { + return (OSPError)currentDevice().loadModule(moduleName); } else { return loadLocalModule(moduleName); } } +OSPRAY_CATCH_END(OSP_UNKNOWN_ERROR) extern "C" const void *ospMapFrameBuffer(OSPFrameBuffer fb, OSPFrameBufferChannel channel) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - return ospray::api::Device::current->frameBufferMap(fb,channel); + return currentDevice().frameBufferMap(fb,channel); } +OSPRAY_CATCH_END(nullptr) extern "C" void ospUnmapFrameBuffer(const void *mapped, OSPFrameBuffer fb) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(mapped != nullptr && "invalid mapped pointer in ospUnmapFrameBuffer"); - ospray::api::Device::current->frameBufferUnmap(mapped,fb); + currentDevice().frameBufferUnmap(mapped,fb); } +OSPRAY_CATCH_END() extern "C" OSPModel ospNewModel() +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - return ospray::api::Device::current->newModel(); + return currentDevice().newModel(); } +OSPRAY_CATCH_END(nullptr) extern "C" void ospAddGeometry(OSPModel model, OSPGeometry geometry) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(model != nullptr && "invalid model in ospAddGeometry"); Assert(geometry != nullptr && "invalid geometry in ospAddGeometry"); - return ospray::api::Device::current->addGeometry(model,geometry); + return currentDevice().addGeometry(model, geometry); } +OSPRAY_CATCH_END() extern "C" void ospRemoveGeometry(OSPModel model, OSPGeometry geometry) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(model != nullptr && "invalid model in ospRemoveGeometry"); Assert(geometry != nullptr && "invalid geometry in ospRemoveGeometry"); - return ospray::api::Device::current->removeGeometry(model, geometry); + return currentDevice().removeGeometry(model, geometry); } +OSPRAY_CATCH_END() extern "C" void ospAddVolume(OSPModel model, OSPVolume volume) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(model != nullptr && "invalid model in ospAddVolume"); Assert(volume != nullptr && "invalid volume in ospAddVolume"); - return ospray::api::Device::current->addVolume(model, volume); + return currentDevice().addVolume(model, volume); } +OSPRAY_CATCH_END() extern "C" void ospRemoveVolume(OSPModel model, OSPVolume volume) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(model != nullptr && "invalid model in ospRemoveVolume"); Assert(volume != nullptr && "invalid volume in ospRemoveVolume"); - return ospray::api::Device::current->removeVolume(model, volume); + return currentDevice().removeVolume(model, volume); } +OSPRAY_CATCH_END() -/*! create a new data buffer, with optional init data and control flags */ -extern "C" OSPData ospNewData(size_t nitems, OSPDataType format, const void *init, const uint32_t flags) +extern "C" OSPData ospNewData(size_t nitems, OSPDataType format, + const void *init, const uint32_t flags) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - LOG("ospSetData(...)"); - LOG("ospSetData(...," << nitems << "," << ((int)format) << "," << ((int*)init) << "," << flags << ",...)"); - OSPData data = ospray::api::Device::current->newData(nitems,format,(void*)init,flags); - LOG("DONE ospSetData(...,\"" << nitems << "," << ((int)format) << "," << ((int*)init) << "," << flags << ",...)"); + OSPData data = currentDevice().newData(nitems, format, (void*)init, flags); return data; } +OSPRAY_CATCH_END(nullptr) -/*! add a data array to another object */ extern "C" void ospSetData(OSPObject object, const char *bufName, OSPData data) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - LOG("ospSetData(...,\"" << bufName << "\",...)"); - return ospray::api::Device::current->setObject(object,bufName,(OSPObject)data); + return currentDevice().setObject(object, bufName, (OSPObject)data); } +OSPRAY_CATCH_END() -/*! add an object parameter to another object */ -extern "C" void ospSetParam(OSPObject target, const char *bufName, OSPObject value) -{ - ASSERT_DEVICE(); - static WarnOnce warning("'ospSetParam()' has been deprecated. " - "Please use the new naming convention of " - "'ospSetObject()' instead"); - LOG("ospSetParam(...,\"" << bufName << "\",...)"); - return ospray::api::Device::current->setObject(target,bufName,value); -} - -/*! set/add a pixel op to a frame buffer */ extern "C" void ospSetPixelOp(OSPFrameBuffer fb, OSPPixelOp op) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - LOG("ospSetPixelOp(...,...)"); - return ospray::api::Device::current->setPixelOp(fb,op); + return currentDevice().setPixelOp(fb, op); } +OSPRAY_CATCH_END() -/*! add an object parameter to another object */ -extern "C" void ospSetObject(OSPObject target, const char *bufName, OSPObject value) +extern "C" void ospSetObject(OSPObject target, + const char *bufName, + OSPObject value) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - LOG("ospSetObject(...,\"" << bufName << "\",...)"); - return ospray::api::Device::current->setObject(target,bufName,value); + return currentDevice().setObject(target, bufName, value); } +OSPRAY_CATCH_END() -/*! \brief create a new pixelOp of given type - - return 'nullptr' if that type is not known */ extern "C" OSPPixelOp ospNewPixelOp(const char *_type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(_type,"invalid render type identifier in ospNewPixelOp"); - LOG("ospNewPixelOp(" << _type << ")"); int L = strlen(_type); char *type = STACK_BUFFER(char, L+1); for (int i=0;i<=L;i++) { @@ -333,378 +414,435 @@ extern "C" OSPPixelOp ospNewPixelOp(const char *_type) c = '_'; type[i] = c; } - OSPPixelOp pixelOp = ospray::api::Device::current->newPixelOp(type); + OSPPixelOp pixelOp = currentDevice().newPixelOp(type); return pixelOp; } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new renderer of given type - - return 'nullptr' if that type is not known */ extern "C" OSPRenderer ospNewRenderer(const char *_type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(_type,"invalid render type identifier in ospNewRenderer"); - LOG("ospNewRenderer(" << _type << ")"); std::string type(_type); for (size_t i = 0; i < type.size(); i++) { if (type[i] == '-' || type[i] == ':') type[i] = '_'; } - OSPRenderer renderer = ospray::api::Device::current->newRenderer(type.c_str()); + OSPRenderer renderer = currentDevice().newRenderer(type.c_str()); if (renderer == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create renderer '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create renderer '" << type << "'"; } return renderer; } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new geometry of given type - - return 'nullptr' if that type is not known */ extern "C" OSPGeometry ospNewGeometry(const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(type != nullptr && "invalid geometry type identifier in ospNewGeometry"); - LOG("ospNewGeometry(" << type << ")"); - OSPGeometry geometry = ospray::api::Device::current->newGeometry(type); + OSPGeometry geometry = currentDevice().newGeometry(type); if (geometry == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create geometry '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create geometry '" << type << "'"; } - LOG("DONE ospNewGeometry(" << type << ") >> " << (int *)geometry); return geometry; } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new material of given type - - return 'nullptr' if that type is not known */ extern "C" OSPMaterial ospNewMaterial(OSPRenderer renderer, const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - // Assert2(renderer != nullptr, "invalid renderer handle in ospNewMaterial"); Assert2(type != nullptr, "invalid material type identifier in ospNewMaterial"); - LOG("ospNewMaterial(" << renderer << ", " << type << ")"); - OSPMaterial material = ospray::api::Device::current->newMaterial(renderer, type); + OSPMaterial material = currentDevice().newMaterial(renderer, type); if (material == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create material '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create material '" << type << "'"; } return material; } +OSPRAY_CATCH_END(nullptr) extern "C" OSPLight ospNewLight(OSPRenderer renderer, const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(type != nullptr, "invalid light type identifier in ospNewLight"); - LOG("ospNewLight(" << renderer << ", " << type << ")"); - OSPLight light = ospray::api::Device::current->newLight(renderer, type); + OSPLight light = currentDevice().newLight(renderer, type); if (light == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create light '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create light '" << type << "'"; } return light; } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new camera of given type - - return 'nullptr' if that type is not known */ extern "C" OSPCamera ospNewCamera(const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(type != nullptr && "invalid camera type identifier in ospNewCamera"); - LOG("ospNewCamera(" << type << ")"); - OSPCamera camera = ospray::api::Device::current->newCamera(type); + OSPCamera camera = currentDevice().newCamera(type); if (camera == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create camera '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create camera '" << type << "'"; } return camera; } +OSPRAY_CATCH_END(nullptr) extern "C" OSPTexture2D ospNewTexture2D(const osp::vec2i &size, const OSPTextureFormat type, void *data, const uint32_t flags) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(size.x > 0, "Width must be greater than 0 in ospNewTexture2D"); Assert2(size.y > 0, "Height must be greater than 0 in ospNewTexture2D"); - LOG("ospNewTexture2D( (" << size.x << ", " << size.y << "), " << type << ", " << data << ", " << flags << ")"); - return ospray::api::Device::current->newTexture2D((vec2i&)size, type, data, flags); + return currentDevice().newTexture2D((vec2i&)size, type, data, flags); } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new volume of given type, return 'nullptr' if that type is not known */ extern "C" OSPVolume ospNewVolume(const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(type != nullptr && "invalid volume type identifier in ospNewVolume"); - LOG("ospNewVolume(" << type << ")"); - OSPVolume volume = ospray::api::Device::current->newVolume(type); + OSPVolume volume = currentDevice().newVolume(type); if (volume == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create volume '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create volume '" << type << "'"; } return volume; } +OSPRAY_CATCH_END(nullptr) -/*! \brief create a new transfer function of given type - return 'nullptr' if that type is not known */ extern "C" OSPTransferFunction ospNewTransferFunction(const char *type) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert(type != nullptr && "invalid transfer function type identifier in ospNewTransferFunction"); - LOG("ospNewTransferFunction(" << type << ")"); - OSPTransferFunction transferFunction = ospray::api::Device::current->newTransferFunction(type); + auto transferFunction = currentDevice().newTransferFunction(type); if (transferFunction == nullptr) { - std::stringstream msg; - msg << "#ospray: could not create transferFunction '" << type << "'" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#ospray: could not create transferFunction '" << type << "'"; } return transferFunction; } +OSPRAY_CATCH_END(nullptr) extern "C" void ospFrameBufferClear(OSPFrameBuffer fb, const uint32_t fbChannelFlags) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->frameBufferClear(fb,fbChannelFlags); + currentDevice().frameBufferClear(fb, fbChannelFlags); } +OSPRAY_CATCH_END() -/*! \brief call a renderer to render given model into given framebuffer - - model _may_ be empty (though most framebuffers will expect one!) */ extern "C" float ospRenderFrame(OSPFrameBuffer fb, - OSPRenderer renderer, - const uint32_t fbChannelFlags - ) + OSPRenderer renderer, + const uint32_t fbChannelFlags) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - return ospray::api::Device::current->renderFrame(fb,renderer,fbChannelFlags); + return currentDevice().renderFrame(fb, renderer, fbChannelFlags); } +OSPRAY_CATCH_END(inf) extern "C" void ospCommit(OSPObject object) +OSPRAY_CATCH_BEGIN { - LOG("ospCommit(...)"); ASSERT_DEVICE(); Assert(object && "invalid object handle to commit to"); - ospray::api::Device::current->commit(object); + currentDevice().commit(object); } +OSPRAY_CATCH_END() extern "C" void ospDeviceCommit(OSPDevice _object) +OSPRAY_CATCH_BEGIN { - auto *object = (ospray::api::Device *)_object; + auto *object = (Device *)_object; object->commit(); } +OSPRAY_CATCH_END() extern "C" void ospDeviceSetString(OSPDevice _object, const char *id, const char *s) +OSPRAY_CATCH_BEGIN { ManagedObject *object = (ManagedObject *)_object; object->findParam(id, true)->set(s); } +OSPRAY_CATCH_END() extern "C" void ospDeviceSet1i(OSPDevice _object, const char *id, int32_t x) +OSPRAY_CATCH_BEGIN { ManagedObject *object = (ManagedObject *)_object; object->findParam(id, true)->set(x); } +OSPRAY_CATCH_END() + +extern "C" void ospDeviceSetStatusFunc(OSPDevice object, OSPStatusFunc callback) +OSPRAY_CATCH_BEGIN +{ + auto *device = (Device *)object; + device->msg_fcn = callback; +} +OSPRAY_CATCH_END() -extern "C" void ospDeviceSetErrorMsgFunc(OSPDevice object, - OSPErrorMsgFunc callback) +// for backward compatibility, will be removed in futur +extern "C" void ospDeviceSetErrorMsgFunc(OSPDevice dev, OSPErrorMsgFunc cb) { - auto *device = (ospray::api::Device *)object; + ospDeviceSetStatusFunc(dev, cb); +} + +extern "C" void ospDeviceSetErrorFunc(OSPDevice object, OSPErrorFunc callback) +OSPRAY_CATCH_BEGIN +{ + auto *device = (Device *)object; device->error_fcn = callback; } +OSPRAY_CATCH_END() + +extern "C" OSPError ospDeviceGetLastErrorCode(OSPDevice object) +OSPRAY_CATCH_BEGIN +{ + auto *device = (Device *)object; + return device->lastErrorCode; +} +OSPRAY_CATCH_END(OSP_NO_ERROR) + +extern "C" const char* ospDeviceGetLastErrorMsg(OSPDevice object) +OSPRAY_CATCH_BEGIN +{ + auto *device = (Device *)object; + return device->lastErrorMsg.c_str(); +} +OSPRAY_CATCH_END(nullptr) extern "C" void ospSetString(OSPObject _object, const char *id, const char *s) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setString(_object,id,s); + currentDevice().setString(_object, id, s); } +OSPRAY_CATCH_END() extern "C" void ospSetf(OSPObject _object, const char *id, float x) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setFloat(_object,id,x); + currentDevice().setFloat(_object, id, x); } +OSPRAY_CATCH_END() extern "C" void ospSet1f(OSPObject _object, const char *id, float x) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setFloat(_object,id,x); + currentDevice().setFloat(_object, id, x); } +OSPRAY_CATCH_END() + extern "C" void ospSet1i(OSPObject _object, const char *id, int32_t x) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setInt(_object,id,x); + currentDevice().setInt(_object, id, x); } +OSPRAY_CATCH_END() extern "C" void ospSeti(OSPObject _object, const char *id, int x) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setInt(_object,id,x); + currentDevice().setInt(_object, id, x); } +OSPRAY_CATCH_END() -/*! Copy data into the given volume. */ -extern "C" int ospSetRegion(OSPVolume object, void *source, +extern "C" OSPError ospSetRegion(OSPVolume object, void *source, const osp::vec3i &index, const osp::vec3i &count) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - return(ospray::api::Device::current->setRegion(object, source, - (vec3i&)index, - (vec3i&)count)); + return currentDevice().setRegion(object, source, (vec3i&)index, (vec3i&)count) + ? OSP_NO_ERROR : OSP_UNKNOWN_ERROR; } +OSPRAY_CATCH_END(OSP_UNKNOWN_ERROR) -/*! add a vec2f parameter to an object */ -extern "C" void ospSetVec2f(OSPObject _object, const char *id, const osp::vec2f &v) +extern "C" void ospSetVec2f(OSPObject _object, + const char *id, + const osp::vec2f &v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2f(_object, id, (const vec2f &)v); + currentDevice().setVec2f(_object, id, (const vec2f &)v); } +OSPRAY_CATCH_END() -/*! add a vec2i parameter to an object */ -extern "C" void ospSetVec2i(OSPObject _object, const char *id, const osp::vec2i &v) +extern "C" void ospSetVec2i(OSPObject _object, + const char *id, + const osp::vec2i &v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2i(_object, id, (const vec2i &)v); + currentDevice().setVec2i(_object, id, (const vec2i &)v); } +OSPRAY_CATCH_END() -/*! add a vec3f parameter to another object */ -extern "C" void ospSetVec3f(OSPObject _object, const char *id, const osp::vec3f &v) +extern "C" void ospSetVec3f(OSPObject _object, + const char *id, + const osp::vec3f &v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3f(_object,id,(const vec3f &)v); + currentDevice().setVec3f(_object, id, (const vec3f &)v); } +OSPRAY_CATCH_END() -/*! add a vec4f parameter to another object */ -extern "C" void ospSetVec4f(OSPObject _object, const char *id, const osp::vec4f &v) +extern "C" void ospSetVec4f(OSPObject _object, + const char *id, + const osp::vec4f &v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec4f(_object,id,(const vec4f &)v); + currentDevice().setVec4f(_object, id, (const vec4f &)v); } +OSPRAY_CATCH_END() -/*! add a vec3i parameter to another object */ -extern "C" void ospSetVec3i(OSPObject _object, const char *id, const osp::vec3i &v) +extern "C" void ospSetVec3i(OSPObject _object, + const char *id, + const osp::vec3i &v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3i(_object,id,(const vec3i &)v); + currentDevice().setVec3i(_object, id, (const vec3i &)v); } +OSPRAY_CATCH_END() -/*! add a vec2f parameter to another object */ extern "C" void ospSet2f(OSPObject _object, const char *id, float x, float y) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2f(_object,id,ospray::vec2f(x,y)); + currentDevice().setVec2f(_object, id, ospray::vec2f(x, y)); } +OSPRAY_CATCH_END() -/*! add a vec2f parameter to another object */ extern "C" void ospSet2fv(OSPObject _object, const char *id, const float *xy) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2f(_object,id,vec2f(xy[0],xy[1])); + currentDevice().setVec2f(_object, id, vec2f(xy[0], xy[1])); } +OSPRAY_CATCH_END() -/*! add a vec2i parameter to another object */ extern "C" void ospSet2i(OSPObject _object, const char *id, int x, int y) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2i(_object,id,vec2i(x,y)); + currentDevice().setVec2i(_object, id, vec2i(x,y)); } +OSPRAY_CATCH_END() -/*! add a vec2i parameter to another object */ extern "C" void ospSet2iv(OSPObject _object, const char *id, const int *xy) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec2i(_object,id,vec2i(xy[0],xy[1])); + currentDevice().setVec2i(_object, id, vec2i(xy[0], xy[1])); } +OSPRAY_CATCH_END() -/*! add a vec3f parameter to another object */ extern "C" void ospSet3f(OSPObject _object, const char *id, float x, float y, float z) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3f(_object,id,vec3f(x,y,z)); + currentDevice().setVec3f(_object,id,vec3f(x,y,z)); } +OSPRAY_CATCH_END() -/*! add a vec3f parameter to another object */ extern "C" void ospSet3fv(OSPObject _object, const char *id, const float *xyz) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3f(_object,id,vec3f(xyz[0],xyz[1],xyz[2])); + currentDevice().setVec3f(_object,id,vec3f(xyz[0],xyz[1],xyz[2])); } +OSPRAY_CATCH_END() -/*! add a vec3i parameter to another object */ extern "C" void ospSet3i(OSPObject _object, const char *id, int x, int y, int z) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3i(_object,id,vec3i(x,y,z)); + currentDevice().setVec3i(_object, id, vec3i(x, y, z)); } +OSPRAY_CATCH_END() -/*! add a vec3i parameter to another object */ extern "C" void ospSet3iv(OSPObject _object, const char *id, const int *xyz) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec3i(_object,id,vec3i(xyz[0],xyz[1],xyz[2])); + currentDevice().setVec3i(_object, id, vec3i(xyz[0], xyz[1], xyz[2])); } +OSPRAY_CATCH_END() -/*! add a vec4f parameter to another object */ -extern "C" void ospSet4f(OSPObject _object, const char *id, float x, float y, float z, float w) +extern "C" void ospSet4f(OSPObject _object, const char *id, + float x, float y, float z, float w) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec4f(_object,id,vec4f(x,y,z,w)); + currentDevice().setVec4f(_object, id, vec4f(x, y, z, w)); } +OSPRAY_CATCH_END() -/*! add a vec4f parameter to another object */ extern "C" void ospSet4fv(OSPObject _object, const char *id, const float *xyzw) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVec4f(_object,id,vec4f(xyzw[0],xyzw[1],xyzw[2],xyzw[3])); + currentDevice().setVec4f(_object, id, + vec4f(xyzw[0], xyzw[1], xyzw[2], xyzw[3])); } +OSPRAY_CATCH_END() -/*! add a void pointer to another object */ extern "C" void ospSetVoidPtr(OSPObject _object, const char *id, void *v) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->setVoidPtr(_object,id,v); + currentDevice().setVoidPtr(_object, id, v); } +OSPRAY_CATCH_END() extern "C" void ospRemoveParam(OSPObject _object, const char *id) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - ospray::api::Device::current->removeParam(_object, id); + currentDevice().removeParam(_object, id); } +OSPRAY_CATCH_END() extern "C" void ospRelease(OSPObject _object) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); if (!_object) return; - ospray::api::Device::current->release(_object); + currentDevice().release(_object); } +OSPRAY_CATCH_END() -//! assign given material to given geometry extern "C" void ospSetMaterial(OSPGeometry geometry, OSPMaterial material) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(geometry,"nullptr geometry passed to ospSetMaterial"); - ospray::api::Device::current->setMaterial(geometry,material); + currentDevice().setMaterial(geometry, material); } +OSPRAY_CATCH_END() -/*! \brief create a new instance geometry that instantiates another - model. the resulting geometry still has to be added to another - model via ospAddGeometry */ extern "C" OSPGeometry ospNewInstance(OSPModel modelToInstantiate, const osp::affine3f &xfm) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); - // return ospray::api::Device::current->newInstance(modelToInstantiate,xfm); OSPGeometry geom = ospNewGeometry("instance"); ospSet3fv(geom,"xfm.l.vx",&xfm.l.vx.x); ospSet3fv(geom,"xfm.l.vy",&xfm.l.vy.x); @@ -713,22 +851,25 @@ extern "C" OSPGeometry ospNewInstance(OSPModel modelToInstantiate, ospSetObject(geom,"model",modelToInstantiate); return geom; } +OSPRAY_CATCH_END(nullptr) extern "C" void ospPick(OSPPickResult *result, OSPRenderer renderer, const osp::vec2f &screenPos) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(renderer, "nullptr renderer passed to ospPick"); if (!result) return; - *result = ospray::api::Device::current->pick(renderer, - (const vec2f&)screenPos); + *result = currentDevice().pick(renderer, (const vec2f&)screenPos); } +OSPRAY_CATCH_END() extern "C" void ospSampleVolume(float **results, OSPVolume volume, const osp::vec3f &worldCoordinates, const size_t count) +OSPRAY_CATCH_BEGIN { ASSERT_DEVICE(); Assert2(volume, "nullptr volume passed to ospSampleVolume"); @@ -738,7 +879,7 @@ extern "C" void ospSampleVolume(float **results, return; } - ospray::api::Device::current->sampleVolume(results, volume, - (vec3f*)&worldCoordinates, count); + currentDevice().sampleVolume(results, volume, + (vec3f*)&worldCoordinates, count); } - +OSPRAY_CATCH_END() diff --git a/ospray/api/Device.cpp b/ospray/api/Device.cpp index c187eef3ee..7eacef3a14 100644 --- a/ospray/api/Device.cpp +++ b/ospray/api/Device.cpp @@ -1,4 +1,4 @@ -// ======================================================================== // +// ======================================================================== // // Copyright 2009-2017 Intel Corporation // // // // Licensed under the Apache License, Version 2.0 (the "License"); // @@ -28,8 +28,6 @@ namespace ospray { - void error_handler(const RTCError code, const char *str); - namespace api { Ref Device::current = nullptr; @@ -52,14 +50,28 @@ namespace ospray { int cpuFeatures = ospcommon::getCPUFeatures(); if ((cpuFeatures & ospcommon::CPU_FEATURE_SSE41) == 0) { - throw std::runtime_error("Error. OSPRay only runs on CPUs that support" - " at least SSE4.1."); + handleError(OSP_UNSUPPORTED_CPU, + "OSPRay only runs on CPUs that support at least SSE4.1"); + return; } auto OSPRAY_DEBUG = getEnvVar("OSPRAY_DEBUG"); debugMode = OSPRAY_DEBUG.first ? OSPRAY_DEBUG.second : getParam1i("debug", 0); + auto OSPRAY_TRACE_API = getEnvVar("OSPRAY_TRACE_API"); + bool traceAPI = OSPRAY_TRACE_API.first ? OSPRAY_TRACE_API.second : + getParam1i("traceApi", 0); + if (traceAPI) { + auto streamPtr = + std::make_shared("ospray_api_trace.txt"); + + trace_fcn = [=](const char *message) { + auto &stream = *streamPtr; + stream << message << std::endl; + }; + } + auto OSPRAY_LOG_LEVEL = getEnvVar("OSPRAY_LOG_LEVEL"); logLevel = OSPRAY_LOG_LEVEL.first ? OSPRAY_LOG_LEVEL.second : getParam1i("logLevel", 0); @@ -72,9 +84,9 @@ namespace ospray { if (OSPRAY_LOG_OUTPUT.first) { auto &dst = OSPRAY_LOG_OUTPUT.second; if (dst == "cout") - error_fcn = [](const char *msg){ std::cout << msg; }; + msg_fcn = [](const char *msg){ std::cout << msg; }; else if (dst == "cerr") - error_fcn = [](const char *msg){ std::cerr << msg; }; + msg_fcn = [](const char *msg){ std::cerr << msg; }; } if (debugMode) { @@ -82,7 +94,15 @@ namespace ospray { numThreads = 1; } - initTaskingSystem(numThreads); + auto OSPRAY_SET_AFFINITY = getEnvVar("OSPRAY_SET_AFFINITY"); + if (OSPRAY_SET_AFFINITY.first) { + threadAffinity = OSPRAY_SET_AFFINITY.second == 0 ? DEAFFINITIZE : + AFFINITIZE; + } + + threadAffinity = getParam1i("setAffinity", threadAffinity); + + tasking::initTaskingSystem(numThreads); committed = true; } @@ -92,5 +112,32 @@ namespace ospray { return committed; } + bool deviceIsSet() + { + return Device::current.ptr != nullptr; + } + + Device ¤tDevice() + { + return *Device::current; + } + + std::string generateEmbreeDeviceCfg(const Device &device) + { + std::stringstream embreeConfig; + + if (device.debugMode) + embreeConfig << " threads=1,verbose=2"; + else if(device.numThreads > 0) + embreeConfig << " threads=" << device.numThreads; + + if (device.threadAffinity == api::Device::AFFINITIZE) + embreeConfig << ",set_affinity=1"; + else if (device.threadAffinity == api::Device::DEAFFINITIZE) + embreeConfig << ",set_affinity=0"; + + return embreeConfig.str(); + } + } // ::ospray::api } // ::ospray diff --git a/ospray/api/Device.h b/ospray/api/Device.h index 26a6f7ed96..1a093d4362 100644 --- a/ospray/api/Device.h +++ b/ospray/api/Device.h @@ -19,7 +19,6 @@ // ospray #include "common/OSPCommon.h" #include "common/Managed.h" -#include "ospray/ospray.h" // embree #include "embree2/rtcore.h" // std @@ -141,7 +140,8 @@ namespace ospray { virtual OSPTransferFunction newTransferFunction(const char *type) = 0; /*! have given renderer create a new material */ - virtual OSPMaterial newMaterial(OSPRenderer _renderer, const char *type) = 0; + virtual OSPMaterial newMaterial(OSPRenderer _renderer, + const char *type) = 0; /*! create a new Texture2D object */ virtual OSPTexture2D newTexture2D(const vec2i &size, @@ -203,12 +203,6 @@ namespace ospray { NOT_IMPLEMENTED; } - /*! switch API mode for distributed API extensions */ - virtual void apiMode(OSPDApiMode) - { - NOT_IMPLEMENTED; - } - virtual void sampleVolume(float **results, OSPVolume volume, const vec3f *worldCoordinates, @@ -229,18 +223,39 @@ namespace ospray { int numThreads {-1}; /*! whether we're running in debug mode (cmdline: --osp:debug) */ bool debugMode {false}; + + enum OSP_THREAD_AFFINITY {AUTO_DETECT, AFFINITIZE, DEAFFINITIZE}; + int threadAffinity {AUTO_DETECT}; /*! logging level (cmdline: --osp:loglevel \) */ // NOTE(jda) - Keep logLevel static because the device factory function // needs to have a valid value for the initial Device creation static uint32_t logLevel; - std::function error_fcn{[](const char*){}}; + std::function + msg_fcn { [](const char*){} }; + + std::function + error_fcn { [](OSPError, const char*){} }; + + std::function + trace_fcn { [](const char*){} }; + + OSPError lastErrorCode = OSP_NO_ERROR; + std::string lastErrorMsg = "no error";// no braced initializer for MSVC12 private: bool committed {false}; }; + // Shorthand functions to query current API device // + + OSPRAY_SDK_INTERFACE bool deviceIsSet(); + OSPRAY_SDK_INTERFACE Device& currentDevice(); + + OSPRAY_SDK_INTERFACE + std::string generateEmbreeDeviceCfg(const Device &device); + /*! \brief registers a internal ospray:: renderer under the externally accessible name "external_name" diff --git a/ospray/api/LocalDevice.cpp b/ospray/api/LocalDevice.cpp index b81866a264..678a7f4745 100644 --- a/ospray/api/LocalDevice.cpp +++ b/ospray/api/LocalDevice.cpp @@ -39,9 +39,7 @@ namespace ospray { void embreeErrorFunc(const RTCError code, const char* str) { - std::stringstream msg; - msg << "#osp: embree internal error " << code << " : " << str << '\n'; - postErrorMsg(msg.str()); + postStatusMsg() << "#osp: embree internal error " << code << " : " << str; throw std::runtime_error("embree internal error '" +std::string(str)+"'"); } @@ -54,21 +52,14 @@ namespace ospray { // ospray::init() because in mpi-mode the latter is also called // in the host-stubs, where it shouldn't. // ------------------------------------------------------- - std::stringstream embreeConfig; - if (debugMode) - embreeConfig << " threads=1,verbose=2"; - else if(numThreads > 0) - embreeConfig << " threads=" << numThreads; - embreeDevice = rtcNewDevice(embreeConfig.str().c_str()); + embreeDevice = rtcNewDevice(generateEmbreeDeviceCfg(*this).c_str()); rtcDeviceSetErrorFunction(embreeDevice, embreeErrorFunc); RTCError erc = rtcDeviceGetError(embreeDevice); if (erc != RTC_NO_ERROR) { // why did the error function not get called !? - std::stringstream msg; - msg << "#osp:init: embree internal error number " << erc << '\n'; - postErrorMsg(msg.str()); + postStatusMsg() << "#osp:init: embree internal error number " << erc; assert(erc == RTC_NO_ERROR); } @@ -149,11 +140,6 @@ namespace ospray { ManagedObject *object = (ManagedObject *)_object; Assert2(object,"null object in LocalDevice::commit()"); object->commit(); - - // hack, to stay compatible with earlier version - Model *model = dynamic_cast(object); - if (model) - model->finalize(); } /*! add a new geometry to a model */ @@ -287,7 +273,7 @@ namespace ospray { { Volume *volume = (Volume *) handle; Assert(volume != nullptr && "invalid volume object handle"); - return(volume->setRegion(source, index, count)); + return volume->setRegion(source, index, count); } /*! assign (named) vec2f parameter to an object */ @@ -368,7 +354,7 @@ namespace ospray { OSPPixelOp LocalDevice::newPixelOp(const char *type) { Assert(type != nullptr && "invalid render type identifier"); - PixelOp *pixelOp = PixelOp::createPixelOp(type); + PixelOp *pixelOp = PixelOp::createInstance(type); if (!pixelOp) { if (debugMode) { throw std::runtime_error("unknown pixelOp type '" + @@ -395,7 +381,7 @@ namespace ospray { OSPRenderer LocalDevice::newRenderer(const char *type) { Assert(type != nullptr && "invalid render type identifier"); - Renderer *renderer = Renderer::createRenderer(type); + Renderer *renderer = Renderer::createInstance(type); if (!renderer) { if (debugMode) { throw std::runtime_error("unknown renderer type '" + @@ -412,7 +398,7 @@ namespace ospray { OSPGeometry LocalDevice::newGeometry(const char *type) { Assert(type != nullptr && "invalid render type identifier"); - Geometry *geometry = Geometry::createGeometry(type); + Geometry *geometry = Geometry::createInstance(type); if (!geometry) return nullptr; geometry->refInc(); return (OSPGeometry)geometry; @@ -451,7 +437,7 @@ namespace ospray { OSPCamera LocalDevice::newCamera(const char *type) { Assert(type != nullptr && "invalid camera type identifier"); - Camera *camera = Camera::createCamera(type); + Camera *camera = Camera::createInstance(type); if (!camera) { if (debugMode) { throw std::runtime_error("unknown camera type '" @@ -550,11 +536,10 @@ namespace ospray { try { return renderer->renderFrame(fb, fbChannelFlags); } catch (const std::runtime_error &e) { - std::string msg = "=================================================\n"; - msg += "# >>> ospray fatal error <<< \n"; - msg += e.what() + '\n'; - msg += "=================================================\n"; - postErrorMsg(msg); + postStatusMsg() << "================================================\n" + << "# >>> ospray fatal error <<< \n" + << std::string(e.what()) + '\n' + << "================================================"; exit(1); } } diff --git a/ospray/camera/Camera.cpp b/ospray/camera/Camera.cpp index 931e9459fa..e9028acede 100644 --- a/ospray/camera/Camera.cpp +++ b/ospray/camera/Camera.cpp @@ -17,15 +17,11 @@ // ospray #include "Camera.h" #include "common/Util.h" -#include "common/Library.h" -// ispc-side stuff #include "Camera_ispc.h" -// stl -#include namespace ospray { - Camera *Camera::createCamera(const char *type) + Camera *Camera::createInstance(const char *type) { return createInstanceHelper(type); } diff --git a/ospray/camera/Camera.h b/ospray/camera/Camera.h index 5cd114bc78..e720a4491d 100644 --- a/ospray/camera/Camera.h +++ b/ospray/camera/Camera.h @@ -32,7 +32,7 @@ namespace ospray { virtual void commit() override; - static Camera *createCamera(const char *identifier); + static Camera *createInstance(const char *identifier); // Data members // diff --git a/ospray/camera/OrthographicCamera.cpp b/ospray/camera/OrthographicCamera.cpp index 734b4d00a2..9583d02a64 100644 --- a/ospray/camera/OrthographicCamera.cpp +++ b/ospray/camera/OrthographicCamera.cpp @@ -15,8 +15,6 @@ // ======================================================================== // #include "OrthographicCamera.h" -#include -// ispc-side stuff #include "OrthographicCamera_ispc.h" namespace ospray { @@ -53,7 +51,7 @@ namespace ospray { vec3f pos_00 = pos - 0.5f * pos_du - 0.5f * pos_dv; - ispc::OrthographicCamera_set(getIE(), /*! return the ISPC equivalent of this class */ + ispc::OrthographicCamera_set(getIE(), (const ispc::vec3f&)dir, (const ispc::vec3f&)pos_00, (const ispc::vec3f&)pos_du, diff --git a/ospray/camera/PanoramicCamera.cpp b/ospray/camera/PanoramicCamera.cpp index 22af7999e5..cc443b44a2 100644 --- a/ospray/camera/PanoramicCamera.cpp +++ b/ospray/camera/PanoramicCamera.cpp @@ -15,15 +15,8 @@ // ======================================================================== // #include "PanoramicCamera.h" -#include -// ispc-side stuff #include "PanoramicCamera_ispc.h" -#ifdef _WIN32 -# define _USE_MATH_DEFINES -# include // M_PI -#endif - namespace ospray { PanoramicCamera::PanoramicCamera() diff --git a/ospray/camera/PerspectiveCamera.cpp b/ospray/camera/PerspectiveCamera.cpp index 1cbd692075..fa5691445f 100644 --- a/ospray/camera/PerspectiveCamera.cpp +++ b/ospray/camera/PerspectiveCamera.cpp @@ -15,15 +15,8 @@ // ======================================================================== // #include "PerspectiveCamera.h" -#include -// ispc-side stuff #include "PerspectiveCamera_ispc.h" -#ifdef _WIN32 -# define _USE_MATH_DEFINES -# include // M_PI -#endif - namespace ospray { PerspectiveCamera::PerspectiveCamera() diff --git a/ospray/common/Data.cpp b/ospray/common/Data.cpp index 04f9ccd68a..de8501af8b 100644 --- a/ospray/common/Data.cpp +++ b/ospray/common/Data.cpp @@ -17,8 +17,6 @@ // ospray #include "Data.h" #include "ospray/ospray.h" -// stl -#include namespace ospray { @@ -28,11 +26,6 @@ namespace ospray { flags(flags), type(type) { - /* two notes here: - a) i'm using embree's 'new' to enforce alignment - b) i'm adding 16 bytes to size to enforce 4-float padding (which embree - requires in some buffers - */ if (flags & OSP_DATA_SHARED_BUFFER) { Assert2(init != NULL, "shared buffer is NULL"); data = init; @@ -45,19 +38,20 @@ namespace ospray { } managedObjectType = OSP_DATA; - - // std::cout << "checksum when creating data array" << std::endl; - // PRINT(numBytes); - // PRINT((int*)computeCheckSum(init,numBytes)); } Data::~Data() { if (type == OSP_OBJECT) { Data **child = (Data **)data; - for (uint32_t i = 0; i < numItems; i++) if (child[i]) child[i]->refDec(); + for (uint32_t i = 0; i < numItems; i++) { + if (child[i]) + child[i]->refDec(); + } } - if (!(flags & OSP_DATA_SHARED_BUFFER)) alignedFree(data); + + if (!(flags & OSP_DATA_SHARED_BUFFER)) + alignedFree(data); } /*! commit this object - for this object type, make sure that all diff --git a/ospray/common/Managed.cpp b/ospray/common/Managed.cpp index adae04f561..63a84673a9 100644 --- a/ospray/common/Managed.cpp +++ b/ospray/common/Managed.cpp @@ -15,7 +15,6 @@ // ======================================================================== // #include "Managed.h" -#include "Data.h" #include "OSPCommon_ispc.h" namespace ospray { @@ -182,8 +181,8 @@ namespace ospray { void ManagedObject::emitMessage(const std::string &kind, const std::string &message) const { - std::string msg = " " + toString() + " " + kind + ": " + message + ".\n"; - postErrorMsg(msg); + postStatusMsg() << " " << toString() + << " " << kind << ": " << message + '.'; } void ManagedObject::exitOnCondition(bool condition, diff --git a/ospray/common/Material.cpp b/ospray/common/Material.cpp index 87dfd80a7f..382b67db64 100644 --- a/ospray/common/Material.cpp +++ b/ospray/common/Material.cpp @@ -16,10 +16,7 @@ // ospray #include "Material.h" -#include "common/Library.h" #include "common/Util.h" -// stl -#include namespace ospray { diff --git a/ospray/common/Model.cpp b/ospray/common/Model.cpp index ead6f88d6b..dbd184ba04 100644 --- a/ospray/common/Model.cpp +++ b/ospray/common/Model.cpp @@ -17,10 +17,6 @@ // ospray #include "api/Device.h" #include "Model.h" -// embree -#include "embree2/rtcore.h" -#include "embree2/rtcore_scene.h" -#include "embree2/rtcore_geometry.h" // ispc exports #include "Model_ispc.h" @@ -42,13 +38,12 @@ namespace ospray { return "ospray::Model"; } - void Model::finalize() + void Model::commit() { - std::stringstream msg; - msg << "=======================================================\n"; - msg << "Finalizing model, has " << geometry.size() - << " geometries and " << volume.size() << " volumes" << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) + << "=======================================================\n" + << "Finalizing model, has " << geometry.size() + << " geometries and " << volume.size() << " volumes"; RTCDevice embreeDevice = (RTCDevice)ospray_getEmbreeDevice(); @@ -58,11 +53,9 @@ namespace ospray { bounds = empty; for (size_t i = 0; i < geometry.size(); i++) { - - std::stringstream msg; - msg << "=======================================================\n" - << "Finalizing geometry " << i << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) + << "=======================================================\n" + << "Finalizing geometry " << i; geometry[i]->finalize(this); diff --git a/ospray/common/Model.h b/ospray/common/Model.h index 9a43df5a6d..95b66d09b2 100644 --- a/ospray/common/Model.h +++ b/ospray/common/Model.h @@ -20,13 +20,11 @@ #include "geometry/Geometry.h" #include "volume/Volume.h" -// stl stuff +// stl #include -// embree stuff +// embree #include "embree2/rtcore.h" -#include "embree2/rtcore_scene.h" -#include "embree2/rtcore_geometry.h" namespace ospray { @@ -44,7 +42,7 @@ namespace ospray { //! \brief common function to help printf-debugging virtual std::string toString() const override; - virtual void finalize(); + virtual void commit() override; // Data members // diff --git a/ospray/common/OSPCommon.cpp b/ospray/common/OSPCommon.cpp index 9f3c945a8b..16be2bdae8 100644 --- a/ospray/common/OSPCommon.cpp +++ b/ospray/common/OSPCommon.cpp @@ -16,8 +16,8 @@ #include "OSPCommon.h" #include "api/Device.h" -// embree -#include "embree2/rtcore.h" + +#include namespace ospray { @@ -33,11 +33,11 @@ namespace ospray { return ospcommon::alignedFree(ptr); } - WarnOnce::WarnOnce(const std::string &s) + WarnOnce::WarnOnce(const std::string &s, uint32_t postAtLogLevel) : s(s) { - std::string msg = "Warning: " + s + " (only reporting first occurrence)\n"; - postErrorMsg(msg); + postStatusMsg(postAtLogLevel) << "Warning: " << s + << " (only reporting first occurrence)"; } /*! for debugging. compute a checksum for given area range... */ @@ -75,7 +75,7 @@ namespace ospray { if (parm == "--osp:debug") { device->findParam("debug", true)->set(true); // per default enable logging to cout; may be overridden later - device->error_fcn = [](const char *msg){ std::cout << msg; }; + device->msg_fcn = [](const char *msg){ std::cout << msg; }; removeArgs(ac,av,i,1); } else if (parm == "--osp:verbose") { device->findParam("logLevel", true)->set(1); @@ -90,16 +90,19 @@ namespace ospray { std::string dst = av[i+1]; if (dst == "cout") - device->error_fcn = [](const char *msg){ std::cout << msg; }; + device->msg_fcn = [](const char *msg){ std::cout << msg; }; else if (dst == "cerr") - device->error_fcn = [](const char *msg){ std::cerr << msg; }; + device->msg_fcn = [](const char *msg){ std::cerr << msg; }; else - postErrorMsg("You must use 'cout' or 'cerr' for --osp:logoutput!"); + postStatusMsg("You must use 'cout' or 'cerr' for --osp:logoutput!"); removeArgs(ac,av,i,2); } else if (parm == "--osp:numthreads" || parm == "--osp:num-threads") { device->findParam("numThreads", true)->set(atoi(av[i+1])); removeArgs(ac,av,i,2); + } else if (parm == "--osp:setaffinity" || parm == "--osp:affinity") { + device->findParam("setAffinity", true)->set(atoi(av[i+1])); + removeArgs(ac,av,i,2); } else { ++i; } @@ -107,25 +110,6 @@ namespace ospray { } } - void error_handler(const RTCError code, const char *str) - { - printf("Embree: "); - switch (code) { - case RTC_UNKNOWN_ERROR : printf("RTC_UNKNOWN_ERROR"); break; - case RTC_INVALID_ARGUMENT : printf("RTC_INVALID_ARGUMENT"); break; - case RTC_INVALID_OPERATION: printf("RTC_INVALID_OPERATION"); break; - case RTC_OUT_OF_MEMORY : printf("RTC_OUT_OF_MEMORY"); break; - case RTC_UNSUPPORTED_CPU : printf("RTC_UNSUPPORTED_CPU"); break; - default : printf("invalid error code"); break; - } - if (str) { - printf(" ("); - while (*str) putchar(*str++); - printf(")\n"); - } - abort(); - } - size_t sizeOf(const OSPDataType type) { switch (type) { case OSP_VOID_PTR: @@ -182,8 +166,9 @@ namespace ospray { throw std::runtime_error(error.str()); } - OSPDataType typeForString(const char *string) { - if (string == NULL) return(OSP_UNKNOWN); + OSPDataType typeForString(const char *string) + { + if (string == nullptr) return(OSP_UNKNOWN); if (strcmp(string, "char" ) == 0) return(OSP_CHAR); if (strcmp(string, "double") == 0) return(OSP_DOUBLE); if (strcmp(string, "float" ) == 0) return(OSP_FLOAT); @@ -264,7 +249,8 @@ namespace ospray { throw std::runtime_error(error.str()); } - size_t sizeOf(const OSPTextureFormat type) { + size_t sizeOf(const OSPTextureFormat type) + { switch (type) { case OSP_TEXTURE_RGBA8: case OSP_TEXTURE_SRGBA: return sizeof(uint32); @@ -288,7 +274,7 @@ namespace ospray { return ospray::api::Device::current->logLevel; } - int loadLocalModule(const std::string &name) + OSPError loadLocalModule(const std::string &name) { std::string libName = "ospray_module_" + name; loadLibrary(libName); @@ -302,28 +288,82 @@ namespace ospray { void (*initMethod)() = (void(*)())initSym; - //NOTE(jda) - don't use magic numbers! if (!initMethod) - return 2; + return OSP_INVALID_ARGUMENT; try { initMethod(); } catch (...) { - return 3; + return OSP_UNKNOWN_ERROR; + } + + return OSP_NO_ERROR; + } + + void postStatusMsg(const std::stringstream &msg, uint32_t postAtLogLevel) + { + postStatusMsg(msg.str(), postAtLogLevel); + } + + void postStatusMsg(const std::string &msg, uint32_t postAtLogLevel) + { + if (logLevel() >= postAtLogLevel && ospray::api::Device::current.ptr) + ospray::api::Device::current->msg_fcn((msg + '\n').c_str()); + } + + StatusMsgStream::StatusMsgStream(uint32_t postAtLogLevel) + : logLevel(postAtLogLevel) + { + } + + StatusMsgStream::~StatusMsgStream() + { + if (!msg.str().empty()) { + postStatusMsg(msg, logLevel); } + } - return 0; + StatusMsgStream postStatusMsg(uint32_t postAtLogLevel) + { + return StatusMsgStream(postAtLogLevel); } - void postErrorMsg(const std::stringstream &msg, uint32_t postAtLogLevel) + void handleError(OSPError e, const std::string &message) { - postErrorMsg(msg.str(), postAtLogLevel); + if (api::deviceIsSet()) { + auto &device = api::currentDevice(); + + device.lastErrorCode = e; + device.lastErrorMsg = message; + + device.error_fcn(e, message.c_str()); + } else { + // NOTE: No device, but something should still get printed for the user to + // debug the calling application. + std::cerr << "#osp: INITIALIZATION ERROR --> " << message << std::endl; + } + } + + void postTraceMsg(const std::string &message) + { + if (api::deviceIsSet()) { + auto &device = api::currentDevice(); + device.trace_fcn(message.c_str()); + } } - void postErrorMsg(const std::string &msg, uint32_t postAtLogLevel) + size_t translatedHash(size_t v) { - if (logLevel() >= postAtLogLevel) - ospray::api::Device::current->error_fcn(msg.c_str()); + static std::map id_translation; + + auto itr = id_translation.find(v); + if (itr == id_translation.end()) { + static size_t newIndex = 0; + id_translation[v] = newIndex; + return newIndex++; + } else { + return id_translation[v]; + } } } // ::ospray diff --git a/ospray/common/OSPCommon.h b/ospray/common/OSPCommon.h index 64d9fa0f72..82b727de60 100644 --- a/ospray/common/OSPCommon.h +++ b/ospray/common/OSPCommon.h @@ -16,10 +16,6 @@ #pragma once -/*! \file OSPCommon.h Defines common types and classes that _every_ - ospray file should know about */ - -// include cmake config first #include "OSPConfig.h" #ifdef _WIN32 @@ -40,47 +36,18 @@ typedef int ssize_t; # include "unistd.h" #endif -#if 1 +// ospcommon #include "ospcommon/AffineSpace.h" #include "ospcommon/RefCount.h" #include "ospcommon/malloc.h" -#else -// embree -#include "common/math/vec2.h" -#include "common/math/vec3.h" -#include "common/math/vec4.h" -#include "common/math/bbox.h" -#include "common/math/affinespace.h" // includes "common/math/linearspace[23].h" -#include "common/sys/ref.h" -#include "common/sys/alloc.h" -#endif - -// C++11 -#include -#include -#include -#include -#include - -#if 1 -// iw: remove this eventually, and replace all occurrences with actual -// std::atomic_xyz's etc; for now this'll make it easier to try out the new c++11 types -namespace ospray { - typedef std::atomic_int AtomicInt; - typedef std::mutex Mutex; - typedef std::lock_guard LockGuard; - typedef std::condition_variable Condition; - - using namespace ospcommon; -} -#endif // ospray #include "ospray/OSPDataType.h" #include "ospray/OSPTexture.h" +#include "ospray/ospray.h" // std -#include // for int64_t etc +#include // for int64_t etc #include #ifdef _WIN32 @@ -96,40 +63,30 @@ namespace ospray { #endif #define OSPRAY_SDK_INTERFACE OSPRAY_INTERFACE -#define ALIGNED_STRUCT \ - void* operator new(size_t size) { return alignedMalloc(size); } \ - void operator delete(void* ptr) { alignedFree(ptr); } \ - void* operator new[](size_t size) { return alignedMalloc(size); } \ - void operator delete[](void* ptr) { alignedFree(ptr); } \ - -#define ALIGNED_STRUCT_(align) \ - void* operator new(size_t size) { return alignedMalloc(size,align); } \ - void operator delete(void* ptr) { alignedFree(ptr); } \ - void* operator new[](size_t size) { return alignedMalloc(size,align); } \ - void operator delete[](void* ptr) { alignedFree(ptr); } \ - //! main namespace for all things ospray (for internal code) namespace ospray { + using namespace ospcommon; + /*! basic types */ - typedef ::int64_t int64; - typedef ::uint64_t uint64; + using int64 = std::int64_t; + using uint64 = std::uint64_t; - typedef ::int32_t int32; - typedef ::uint32_t uint32; + using int32 = std::int32_t; + using uint32 = std::uint32_t; - typedef ::int16_t int16; - typedef ::uint16_t uint16; + using int16 = std::int16_t; + using uint16 = std::uint16_t; - typedef ::int8_t int8; - typedef ::uint8_t uint8; + using int8 = std::int8_t; + using uint8 = std::uint8_t; - typedef ::int64_t index_t; + using index_t = std::int64_t; void initFromCommandLine(int *ac = nullptr, const char ***av = nullptr); /*! for debugging. compute a checksum for given area range... */ - OSPRAY_INTERFACE void *computeCheckSum(const void *ptr, size_t numBytes); + OSPRAY_SDK_INTERFACE void *computeCheckSum(const void *ptr, size_t numBytes); #ifndef Assert #ifdef NDEBUG @@ -147,40 +104,148 @@ namespace ospray { #endif /*! size of OSPDataType */ - OSPRAY_INTERFACE size_t sizeOf(const OSPDataType); + OSPRAY_SDK_INTERFACE size_t sizeOf(const OSPDataType); /*! Convert a type string to an OSPDataType. */ - OSPRAY_INTERFACE OSPDataType typeForString(const char *string); + OSPRAY_SDK_INTERFACE OSPDataType typeForString(const char *string); /*! Convert a type string to an OSPDataType. */ inline OSPDataType typeForString(const std::string &s) { return typeForString(s.c_str()); } /*! Convert a type string to an OSPDataType. */ - OSPRAY_INTERFACE std::string stringForType(OSPDataType type); + OSPRAY_SDK_INTERFACE std::string stringForType(OSPDataType type); /*! size of OSPTextureFormat */ - OSPRAY_INTERFACE size_t sizeOf(const OSPTextureFormat); + OSPRAY_SDK_INTERFACE size_t sizeOf(const OSPTextureFormat); - struct OSPRAY_SDK_INTERFACE WarnOnce { - WarnOnce(const std::string &s); + OSPRAY_SDK_INTERFACE OSPError loadLocalModule(const std::string &name); + + /*! little helper class that prints out a warning string upon the + first time it is encountered. + + Usage: + + if (someThisBadHappens) { + static WarnOnce warning("something bad happened, at least once!"); + ... + } + */ + struct OSPRAY_SDK_INTERFACE WarnOnce + { + WarnOnce(const std::string &message, uint32_t postAtLogLevel = 0); private: const std::string s; }; - OSPRAY_INTERFACE uint32_t logLevel(); + // use postStatusMsg to output any information, which will OSPRay's internal + // infrastructure, optionally at a given loglevel + // a newline is added implicitely + ////////////////////////////////////////////////////////////////////////////// - OSPRAY_INTERFACE int loadLocalModule(const std::string &name); + OSPRAY_SDK_INTERFACE uint32_t logLevel(); - OSPRAY_INTERFACE void postErrorMsg(const std::stringstream &msg, - uint32_t postAtLogLevel = 0); + OSPRAY_SDK_INTERFACE void postStatusMsg(const std::stringstream &msg, + uint32_t postAtLogLevel = 0); - OSPRAY_INTERFACE void postErrorMsg(const std::string &msg, - uint32_t postAtLogLevel = 0); + OSPRAY_SDK_INTERFACE void postStatusMsg(const std::string &msg, + uint32_t postAtLogLevel = 0); -} // ::ospray + struct OSPRAY_SDK_INTERFACE StatusMsgStream + { + StatusMsgStream(uint32_t postAtLogLevel = 0); + // a "= default" move constructor is not supported by older compilers + // however, apparently it just needs to be declared, it won't be called + StatusMsgStream(StatusMsgStream &&other); + ~StatusMsgStream(); -#ifdef _WIN32 -#define __PRETTY_FUNCTION__ __FUNCSIG__ -#endif -#define NOTIMPLEMENTED throw std::runtime_error(std::string(__PRETTY_FUNCTION__)+": not implemented..."); + private: + template + friend StatusMsgStream &&operator<<(StatusMsgStream &&stream, T &&rhs); + + std::stringstream msg; + uint32_t logLevel {0}; + }; + + template + inline StatusMsgStream &&operator<<(StatusMsgStream &&stream, T &&rhs) + { + if (logLevel() >= stream.logLevel) + stream.msg << std::forward(rhs); + return std::forward(stream); + } + + OSPRAY_SDK_INTERFACE StatusMsgStream postStatusMsg(uint32_t postAtLogLevel = 0); + + OSPRAY_SDK_INTERFACE void handleError(OSPError e, const std::string &message); + + OSPRAY_SDK_INTERFACE void postTraceMsg(const std::string &message); + + // RTTI hash ID lookup helper functions /////////////////////////////////// + + OSPRAY_SDK_INTERFACE size_t translatedHash(size_t v); + + template + inline size_t typeIdOf() + { + return translatedHash(typeid(T).hash_code()); + } + + template + inline size_t typeIdOf(T *v) + { + return translatedHash(typeid(*v).hash_code()); + } + + template + inline size_t typeIdOf(const T &v) + { + return translatedHash(typeid(v).hash_code()); + } + + template + inline size_t typeIdOf(const std::unique_ptr &v) + { + return translatedHash(typeid(*v).hash_code()); + } + + template + inline size_t typeIdOf(const std::shared_ptr &v) + { + return translatedHash(typeid(*v).hash_code()); + } + + // RTTI string name lookup helper functions /////////////////////////////// + + template + inline std::string typeString() + { + return typeid(T).name(); + } + + template + inline std::string typeString(T *v) + { + return typeid(*v).name(); + } + + template + inline std::string typeString(const T &v) + { + return typeid(v).name(); + } + + template + inline std::string typeString(const std::unique_ptr &v) + { + return typeid(*v).name(); + } + + template + inline std::string typeString(const std::shared_ptr &v) + { + return typeid(*v).name(); + } + + +} // ::ospray diff --git a/ospray/common/ObjectHandle.cpp b/ospray/common/ObjectHandle.cpp index 8d6f60dc59..b45df6661b 100644 --- a/ospray/common/ObjectHandle.cpp +++ b/ospray/common/ObjectHandle.cpp @@ -20,7 +20,7 @@ namespace ospray { - std::map > objectByHandle; + std::map> objectByHandle; std::stack freedHandles; //! next unassigned ID on this node @@ -34,17 +34,29 @@ namespace ospray { freedHandles.push((int64)*this); } - ObjectHandle ObjectHandle::alloc() + ObjectHandle::ObjectHandle() { - ObjectHandle h; if (freedHandles.empty()) { - // h.i32.owner = app.rank; - h.i32.ID = nextFreeLocalID++; + i32.ID = nextFreeLocalID++; + i32.owner = 0; } else { - h = freedHandles.top(); + i64 = freedHandles.top(); freedHandles.pop(); } - return h; + } + + ObjectHandle::ObjectHandle(int64 i) : i64(i) + { + } + + ObjectHandle::ObjectHandle(const ObjectHandle &other) : i64(other.i64) + { + } + + ObjectHandle &ObjectHandle::operator=(const ObjectHandle &other) + { + i64 = other.i64; + return *this; } /*! define the given handle to refer to given object */ @@ -67,7 +79,22 @@ namespace ospray { objectByHandle.erase(it); } - bool ObjectHandle::defined() const + int32 ObjectHandle::ownerRank() const + { + return i32.owner; + } + + int32 ObjectHandle::objID() const + { + return i32.ID; + } + + ospray::ObjectHandle::operator int64() const + { + return i64; + } + + bool ObjectHandle::defined() const { auto it = objectByHandle.find(i64); return it != objectByHandle.end(); @@ -78,7 +105,14 @@ namespace ospray { if (i64 == 0) return nullptr; auto it = objectByHandle.find(i64); - Assert(it != objectByHandle.end()); + if (it == objectByHandle.end()) { +#ifndef NDEBUG + // iw - made this into a warning only; the original code had + // this throw an actual exceptoin, but that may be overkill + std::cout << "#osp: WARNING: ospray is trying to look up object handle "+std::to_string(i64)+" that isn't defined!" << std::endl; +#endif + return nullptr; + } return it->second.ptr; } diff --git a/ospray/common/ObjectHandle.h b/ospray/common/ObjectHandle.h index fee2133403..64990a5e6d 100644 --- a/ospray/common/ObjectHandle.h +++ b/ospray/common/ObjectHandle.h @@ -38,19 +38,16 @@ namespace ospray { as if they were pointers (and thus, 'null' objects are consistent between local and mpi rendering) */ - union OSPRAY_SDK_INTERFACE ObjectHandle { - static ObjectHandle alloc(); + union OSPRAY_SDK_INTERFACE ObjectHandle + { void free(); - inline ObjectHandle(const int64 i = 0) { i64 = i; } - inline ObjectHandle(const ObjectHandle &other) { i64 = other.i64; } - inline ObjectHandle &operator=(const ObjectHandle &other) - { i64 = other.i64; return *this; } + ObjectHandle(); + ObjectHandle(int64 i); + ObjectHandle(const ObjectHandle &other); + ObjectHandle &operator=(const ObjectHandle &other); - struct { int32 ID; int32 owner; } i32; - int64 i64; - - /*! look up an object by handle, and return it. must be a defiend handle */ + /*! look up an object by handle, and return it. must be a defined handle */ ManagedObject *lookup() const; /*! Return the handle associated with the given object. */ @@ -65,25 +62,32 @@ namespace ospray { /*! define the given handle to refer to given object */ void assign(const ManagedObject *object) const; - //! free the given object void freeObject() const; - /*! returns the owner's rank */ - inline int32 owner() const { return i32.owner; } - - /*! returns the local ID to reference this object on the owner rank */ - inline int32 ID() const { return i32.ID; } + int32 ownerRank() const; + int32 objID() const; /*! cast to int64 to allow fast operations with this type */ - inline operator int64() const { return i64; } + operator int64() const; + + // Data members // + struct { int32 ID; int32 owner; } i32; + int64 i64; }; OSPRAY_SDK_INTERFACE extern const ObjectHandle nullHandle; + // Inlined operator definitions ///////////////////////////////////////////// + inline bool operator==(const ObjectHandle &a, const ObjectHandle &b) - { return a.i64 == b.i64; } + { + return a.i64 == b.i64; + } + inline bool operator!=(const ObjectHandle &a, const ObjectHandle &b) - { return a.i64 != b.i64; } + { + return !(a == b); + } } // ::ospray diff --git a/ospray/common/Ray.ih b/ospray/common/Ray.ih index 233a61d27a..402dc6632f 100644 --- a/ospray/common/Ray.ih +++ b/ospray/common/Ray.ih @@ -115,6 +115,13 @@ inline void setRay(Ray &ray, const vec3f &ray_org, const vec3f &ray_dir, ray.instID = -1; } +/*! helper function that performs a ray-plane test */ +inline float intersectPlane(const Ray& ray, const uniform vec4f& plane) +{ + const uniform vec3f normal = make_vec3f(plane); + return (plane.w - dot(ray.org, normal)) * rcpf(dot(ray.dir, normal)); +} + /*! helper function that performs a ray-box test */ inline void intersectBox(const Ray& ray, const uniform box3f& box, diff --git a/ospray/common/Util.h b/ospray/common/Util.h index bde0b7580f..57574f2422 100644 --- a/ospray/common/Util.h +++ b/ospray/common/Util.h @@ -35,11 +35,9 @@ namespace ospray { // Find the creation function for the subtype if not already known. if (symbolRegistry.count(type) == 0) { - - std::stringstream msg; - msg << "#ospray: trying to look up " << type_string << " type '" - << type << "' for the first time" << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) << "#ospray: trying to look up " + << type_string << " type '" << type + << "' for the first time"; // Construct the name of the creation function to look for. std::string creationFunctionName = "ospray_create_" + type_string @@ -52,10 +50,8 @@ namespace ospray { // The named function may not be found if the requested subtype is not // known. if (!symbolRegistry[type]) { - std::stringstream msg; - msg << " WARNING: unrecognized " << type_string << " type '" - << type << "'." << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << " WARNING: unrecognized " << type_string + << " type '" << type << "'."; } } diff --git a/ospray/common/ospray.rc b/ospray/common/ospray.rc index a764e0531f..6c51a7feca 100644 --- a/ospray/common/ospray.rc +++ b/ospray/common/ospray.rc @@ -1,8 +1,8 @@ #include "ospray/version.h" 1 VERSIONINFO - FILEVERSION OSPRAY_VERSION_MAJOR,OSPRAY_VERSION_MINOR,0 - PRODUCTVERSION OSPRAY_VERSION_MAJOR,OSPRAY_VERSION_MINOR,0 + FILEVERSION OSPRAY_VERSION_MAJOR,OSPRAY_VERSION_MINOR,OSPRAY_VERSION_PATCH,0 + PRODUCTVERSION OSPRAY_VERSION_MAJOR,OSPRAY_VERSION_MINOR,OSPRAY_VERSION_PATCH,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L diff --git a/ospray/def_header.txt b/ospray/def_header.txt new file mode 100644 index 0000000000..6d90dafdc7 --- /dev/null +++ b/ospray/def_header.txt @@ -0,0 +1,4 @@ +EXPORTS +precomputedHalton +precomputedHalton_initialized +z_order diff --git a/ospray/fb/FrameBuffer.h b/ospray/fb/FrameBuffer.h index 4c8d1d2827..aa484277b2 100644 --- a/ospray/fb/FrameBuffer.h +++ b/ospray/fb/FrameBuffer.h @@ -64,7 +64,7 @@ namespace ospray { virtual int32 accumID(const vec2i &tile) = 0; virtual float tileError(const vec2i &tile) = 0; - void beginFrame(); + virtual void beginFrame(); //! returns error of frame virtual float endFrame(const float errorThreshold) = 0; diff --git a/ospray/fb/LocalFB.h b/ospray/fb/LocalFB.h index 0a3cfa4966..6f689987ef 100644 --- a/ospray/fb/LocalFB.h +++ b/ospray/fb/LocalFB.h @@ -22,7 +22,8 @@ namespace ospray { /*! local frame buffer - frame buffer that exists on local machine */ - struct OSPRAY_SDK_INTERFACE LocalFrameBuffer : public FrameBuffer { + struct OSPRAY_SDK_INTERFACE LocalFrameBuffer : public FrameBuffer + { void *colorBuffer; /*!< format depends on FrameBuffer::colorBufferFormat, may be NULL */ diff --git a/ospray/fb/LocalFB.ispc b/ospray/fb/LocalFB.ispc index faeb51548a..0b51d7462e 100644 --- a/ospray/fb/LocalFB.ispc +++ b/ospray/fb/LocalFB.ispc @@ -94,7 +94,7 @@ export uniform float LocalFrameBuffer_accumulateTile(void *uniform _fb, uint32 pixelID = iiy*fb->super.size.x+iix; - /*! todo: rather than gathering, replace this code with + /* TODO: rather than gathering, replace this code with 'load4f's and swizzles */ varying vec4f acc = make_vec4f(0.f); if (tile.accumID > 0) @@ -110,20 +110,21 @@ export uniform float LocalFrameBuffer_accumulateTile(void *uniform _fb, // variance buffer accumulates every other frame if (variance && (tile.accumID & 1) == 1) { - varying vec3f vari = make_vec3f(0.f); + varying vec4f vari = make_vec4f(0.f); if (tile.accumID > 1) - vari = make_vec3f(variance[pixelID]); + vari = variance[pixelID]; unmasked { vari.x += varyTile->r[chunkID]; vari.y += varyTile->g[chunkID]; vari.z += varyTile->b[chunkID]; + vari.w += varyTile->a[chunkID]; } - variance[pixelID] = make_vec4f(vari); + variance[pixelID] = vari; - const vec3f acc3 = make_vec3f(acc); - const float den2 = reduce_add(acc3); + // invert alpha (bright alpha is more important) + const float den2 = reduce_add(make_vec3f(acc)) + (1.f-acc.w); if (den2 > 0.0f) { - const vec3f diff = absf(acc3 - accHalfScale * vari); + const vec4f diff = absf(acc - accHalfScale * vari); err += reduce_add(diff) * rsqrtf(den2); } } diff --git a/ospray/fb/PixelOp.cpp b/ospray/fb/PixelOp.cpp index c570ad864c..2a775d1882 100644 --- a/ospray/fb/PixelOp.cpp +++ b/ospray/fb/PixelOp.cpp @@ -16,21 +16,11 @@ // ospray #include "PixelOp.h" -#include "common/Library.h" #include "common/Util.h" -// stl -#include namespace ospray { - PixelOp::Instance *PixelOp::createInstance(FrameBuffer *fb, - PixelOp::Instance *prev) - { - UNUSED(fb, prev); - return nullptr; - } - - PixelOp *PixelOp::createPixelOp(const char *type) + PixelOp *PixelOp::createInstance(const char *type) { return createInstanceHelper(type); } diff --git a/ospray/fb/PixelOp.h b/ospray/fb/PixelOp.h index 83e48ab083..3d69f7270f 100644 --- a/ospray/fb/PixelOp.h +++ b/ospray/fb/PixelOp.h @@ -43,7 +43,7 @@ namespace ospray { { struct OSPRAY_SDK_INTERFACE Instance : public RefCount { - virtual ~Instance() = default; + virtual ~Instance() {} // "= default;" causes linker problems with icc15 /*! gets called every time the frame buffer got 'commit'ted */ virtual void commitNotify() {} /*! gets called once at the beginning of the frame */ @@ -80,15 +80,9 @@ namespace ospray { virtual ~PixelOp() = default; //! \brief create an instance of this pixel op - virtual Instance *createInstance(FrameBuffer *fb, PixelOp::Instance *prev); + virtual Instance *createInstance(FrameBuffer *fb, PixelOp::Instance *prev) = 0; - /*! \brief creates an abstract renderer class of given type - - The respective renderer type must be a registered renderer type - in either ospray proper or any already loaded module. For - renderer types specified in special modules, make sure to call - ospLoadModule first. */ - static PixelOp *createPixelOp(const char *identifier); + static PixelOp *createInstance(const char *identifier); }; /*! \brief registers a internal ospray:: renderer under diff --git a/ospray/fb/Tile.h b/ospray/fb/Tile.h index e97662e789..5031e2bd5f 100644 --- a/ospray/fb/Tile.h +++ b/ospray/fb/Tile.h @@ -17,7 +17,6 @@ #pragma once #include "common/OSPCommon.h" -#include "render/util.h" namespace ospray { @@ -37,10 +36,8 @@ namespace ospray { agree on which fields will be set. Similarly, the frame buffer may actually use uchars, but the tile will always store floats. */ - struct OSPRAY_SDK_INTERFACE __aligned(64) Tile { - // make sure this tile is 64-byte aligned when alloc'ed - ALIGNED_STRUCT; - + struct OSPRAY_SDK_INTERFACE __aligned(64) Tile + { // 'red' component; in float. float r[TILE_SIZE*TILE_SIZE]; // 'green' component; in float. @@ -58,7 +55,7 @@ namespace ospray { int32 children; int32 accumID; //!< how often has been accumulated into this tile - Tile() {} + Tile() = default; Tile(const vec2i &tile, const vec2i &fbsize, const int32 accumId) : fbSize(fbsize), rcp_fbSize(rcp(vec2f(fbsize))), diff --git a/ospray/geometry/Cylinders.cpp b/ospray/geometry/Cylinders.cpp index 133b1bbef6..fa70ce55ca 100644 --- a/ospray/geometry/Cylinders.cpp +++ b/ospray/geometry/Cylinders.cpp @@ -48,16 +48,15 @@ namespace ospray { cylinderData = getParamData("cylinders"); materialList = getParamData("materialList"); colorData = getParamData("color"); + texcoordData = getParamData("texcoord"); if (cylinderData.ptr == nullptr || bytesPerCylinder == 0) { throw std::runtime_error("#ospray:geometry/cylinders: no 'cylinders'" " data specified"); } numCylinders = cylinderData->numBytes / bytesPerCylinder; - std::stringstream msg; - msg << "#osp: creating 'cylinders' geometry, #cylinders = " - << numCylinders << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) << "#osp: creating 'cylinders' geometry, #cylinders = " + << numCylinders; if (_materialList) { free(_materialList); @@ -84,9 +83,13 @@ namespace ospray { bounds.extend(box3f(v1 - r, v1 + r)); } + auto colComps = colorData && colorData->type == OSP_FLOAT3 ? 3 : 4; ispc::CylindersGeometry_set(getIE(),model->getIE(), cylinderData->data,_materialList, - colorData?(ispc::vec4f*)colorData->data:nullptr, + texcoordData ? texcoordData->data : nullptr, + colorData ? colorData->data : nullptr, + colComps * sizeof(float), + colorData && colorData->type == OSP_FLOAT4, numCylinders,bytesPerCylinder, radius,materialID, offset_v0,offset_v1, diff --git a/ospray/geometry/Cylinders.h b/ospray/geometry/Cylinders.h index bf4feafa84..a483c23c5a 100644 --- a/ospray/geometry/Cylinders.h +++ b/ospray/geometry/Cylinders.h @@ -77,11 +77,13 @@ namespace ospray { int64 offset_radius; int64 offset_materialID; int64 offset_colorID; + float epsilon; //epsilon for intersections Ref cylinderData; Ref materialList; void *_materialList {nullptr}; - Ref colorData; /*!< cylinder color array (vec3fa) */ + Ref colorData; /*!< cylinder color array */ + Ref texcoordData; }; /*! @} */ diff --git a/ospray/geometry/Cylinders.ispc b/ospray/geometry/Cylinders.ispc index 76bbfd2e9e..edcac1b8f9 100644 --- a/ospray/geometry/Cylinders.ispc +++ b/ospray/geometry/Cylinders.ispc @@ -25,12 +25,15 @@ #include "embree2/rtcore_scene.isph" #include "embree2/rtcore_geometry_user.isph" +struct CylinderTexCoord { + vec2f v0; + vec2f v1; +}; struct Cylinders { - uniform Geometry super; //!< inherited geometry fields + Geometry super; //!< inherited geometry fields - uniform uint8 *uniform data; - uniform Material *uniform *materialList; - uniform vec4f *uniform color; + uint8 *data; + Material **materialList; float radius; int materialID; @@ -40,9 +43,14 @@ struct Cylinders { int offset_materialID; int offset_colorID; int32 bytesPerCylinder; -}; + float epsilon; -typedef uniform float uniform_float; + uint8 *color; + int color_stride; + bool has_alpha; // 4th color component is valid + + CylinderTexCoord *texcoord; +}; unmasked void Cylinders_bounds(Cylinders *uniform self, uniform size_t primID, @@ -101,10 +109,11 @@ void Cylinders_intersect(Cylinders *uniform self, const float t_out= (- b + srad) *rcpf(2.f*a); bool hit = false; - if (t_in >= tAB0 && t_in <= tAB1) { + + if (t_in >= (tAB0) && t_in <= (tAB1)) { hit = true; ray.t = t_in; - } else if (t_out >= tAB0 && t_out <= tAB1) { + } else if (t_out >= (tAB0+self->epsilon) && t_out <= (tAB1)) { hit = true; ray.t = t_out; } @@ -113,10 +122,11 @@ void Cylinders_intersect(Cylinders *uniform self, ray.primID = primID; ray.geomID = self->super.geomID; // cannot easily be moved to postIntersect - // we need hit in object space, in postIntersect it is in world-space + // we need hit in object-space, in postIntersect it is in world-space const vec3f P = ray.t*ray.dir - A; const vec3f V = cross(P,AB); ray.Ng = cross(AB,V); + ray.u = (ray.t-tA)*rcp(tB-tA); } } @@ -131,15 +141,23 @@ static void Cylinders_postIntersect(Geometry *uniform _self, dg.Ng = dg.Ns = ray.Ng; if ((flags & DG_COLOR) && self->color) { + uint32 colorID = 0; if (self->offset_colorID >= 0) { uniform uint8 *cylinderPtr = self->data + self->bytesPerCylinder*ray.primID; - uint32 colorID = *((uniform uint32 *varying)(cylinderPtr+self->offset_colorID)); - dg.color = self->color[colorID]; - } else { - dg.color = self->color[ray.primID]; - } + colorID = *((uniform uint32 *varying)(cylinderPtr+self->offset_colorID)); + } else + colorID = ray.primID; + dg.color = *((vec4f *)(self->color + colorID*self->color_stride)); + if (!self->has_alpha) + dg.color.w = 1.f; } + if (flags & DG_TEXCOORD && self->texcoord) { + CylinderTexCoord tc = self->texcoord[ray.primID]; + dg.st = lerp(ray.u, tc.v0, tc.v1); + } else + dg.st = make_vec2f(0.0f); + if ((flags & DG_MATERIALID) && (self->offset_materialID >= 0)) { uniform uint8 *cylinderPtr = self->data + self->bytesPerCylinder*ray.primID; dg.materialID = *((uniform uint32 *varying)(cylinderPtr+self->offset_materialID)); @@ -224,20 +242,24 @@ export void *uniform Cylinders_create(void *uniform cppEquivalent) return geom; } -export void CylindersGeometry_set(void *uniform _self, - void *uniform _model, - void *uniform data, - void *uniform materialList, - uniform vec4f *uniform color, - int uniform numCylinders, - int uniform bytesPerCylinder, - float uniform radius, - int uniform materialID, - int uniform offset_v0, - int uniform offset_v1, - int uniform offset_radius, - int uniform offset_materialID, - int uniform offset_colorID) +export void CylindersGeometry_set(void *uniform _self + , void *uniform _model + , void *uniform data + , void *uniform materialList + , void *uniform texcoord + , void *uniform color + , uniform int color_stride + , uniform bool has_alpha + , uniform int numCylinders + , uniform int bytesPerCylinder + , uniform float radius + , uniform int materialID + , uniform int offset_v0 + , uniform int offset_v1 + , uniform int offset_radius + , uniform int offset_materialID + , uniform int offset_colorID + ) { Cylinders *uniform self = (Cylinders *uniform)_self; Model *uniform model = (Model *uniform)_model; @@ -250,9 +272,12 @@ export void CylindersGeometry_set(void *uniform _self, self->super.getAreas = Cylinders_getAreas; self->super.sampleArea = Cylinders_sampleArea; self->materialList = (Material **)materialList; - self->color = color; + self->texcoord = (CylinderTexCoord *uniform)texcoord; + self->color = (uint8 *uniform)color; + self->color_stride = color_stride; + self->has_alpha = has_alpha; self->radius = radius; - self->data = (uniform uint8 *uniform)data; + self->data = (uint8 *uniform)data; self->materialID = materialID; self->bytesPerCylinder = bytesPerCylinder; @@ -262,6 +287,10 @@ export void CylindersGeometry_set(void *uniform _self, self->offset_materialID = offset_materialID; self->offset_colorID = offset_colorID; + self->epsilon = log(self->radius); + if (self->epsilon < 0.f) + self->epsilon = -1.f/self->epsilon; + rtcSetUserData(model->embreeSceneHandle, geomID, self); rtcSetBoundsFunction(model->embreeSceneHandle,geomID, (uniform RTCBoundsFunc)&Cylinders_bounds); diff --git a/ospray/geometry/Geometry.cpp b/ospray/geometry/Geometry.cpp index d67e984ca7..247163c87b 100644 --- a/ospray/geometry/Geometry.cpp +++ b/ospray/geometry/Geometry.cpp @@ -17,31 +17,33 @@ // ospray #include "Geometry.h" #include "common/Util.h" -#include "common/Library.h" -// stl -#include // ISPC exports #include "Geometry_ispc.h" namespace ospray { - Geometry::Geometry() { managedObjectType = OSP_GEOMETRY; } + Geometry::Geometry() + { + managedObjectType = OSP_GEOMETRY; + } void Geometry::setMaterial(Material *mat) { if (!mat) { - std::stringstream msg; - msg << "#osp: warning - tried to set NULL material; ignoring" - << "#osp: warning. (note this means that object may not get any " - << "material at all!)" << std::endl; - postErrorMsg(msg.str()); + postStatusMsg() << "#osp: warning - tried to set NULL material; ignoring" + << "#osp: warning. (note this means that object may not " + << " get any material at all!)"; return; } + material = mat; - if (!getIE()) - postErrorMsg("#osp: warning - geometry does not have an ispc equivalent!\n"); + + if (!getIE()) { + postStatusMsg("#osp: warning: geometry does not have an " + "ispc equivalent!"); + } else { - ispc::Geometry_setMaterial(this->getIE(),mat?mat->getIE():NULL); + ispc::Geometry_setMaterial(this->getIE(), mat ? mat->getIE() : nullptr); } } @@ -59,7 +61,7 @@ namespace ospray { { } - Geometry *Geometry::createGeometry(const char *type) + Geometry *Geometry::createInstance(const char *type) { return createInstanceHelper(type); } diff --git a/ospray/geometry/Geometry.h b/ospray/geometry/Geometry.h index 4e0c3dac04..13fc92f021 100644 --- a/ospray/geometry/Geometry.h +++ b/ospray/geometry/Geometry.h @@ -63,7 +63,7 @@ namespace ospray { in either ospray proper or any already loaded module. For geometry types specified in special modules, make sure to call ospLoadModule first. */ - static Geometry *createGeometry(const char *type); + static Geometry *createInstance(const char *type); box3f bounds {empty}; diff --git a/ospray/geometry/Instance.cpp b/ospray/geometry/Instance.cpp index 72f843380c..9f8037167e 100644 --- a/ospray/geometry/Instance.cpp +++ b/ospray/geometry/Instance.cpp @@ -41,11 +41,11 @@ namespace ospray { xfm.l.vz = getParam3f("xfm.l.vz",vec3f(0.f,0.f,1.f)); xfm.p = getParam3f("xfm.p",vec3f(0.f,0.f,0.f)); - instancedScene = (Model *)getParamObject("model",NULL); + instancedScene = (Model *)getParamObject("model", nullptr); assert(instancedScene); if (!instancedScene->embreeSceneHandle) { - instancedScene->finalize(); + instancedScene->commit(); } embreeGeomID = rtcNewInstance(model->embreeSceneHandle, diff --git a/ospray/geometry/Slices.ispc b/ospray/geometry/Slices.ispc index 2fd090bdd5..b4d337aa1a 100644 --- a/ospray/geometry/Slices.ispc +++ b/ospray/geometry/Slices.ispc @@ -43,7 +43,7 @@ void Slices_intersect(uniform Slices *uniform slices, varying Ray &ray, uniform size_t primID) { - const float tIntersect = -(dot(ray.org, make_vec3f(slices->planes[primID])) + slices->planes[primID].w) * rcpf(dot(ray.dir, make_vec3f(slices->planes[primID]))); + const float tIntersect = intersectPlane(ray, slices->planes[primID]); float tBox0, tBox1; intersectBox(ray, slices->volume->boundingBox, tBox0, tBox1); diff --git a/ospray/geometry/Spheres.cpp b/ospray/geometry/Spheres.cpp index 88320bf956..b3aa877f75 100644 --- a/ospray/geometry/Spheres.cpp +++ b/ospray/geometry/Spheres.cpp @@ -56,7 +56,9 @@ namespace ospray { materialList = getParamData("materialList"); colorData = getParamData("color"); colorOffset = getParam1i("color_offset",0); - colorStride = getParam1i("color_stride",4*sizeof(float)); + auto colComps = colorData && colorData->type == OSP_FLOAT3 ? 3 : 4; + colorStride = getParam1i("color_stride", colComps * sizeof(float)); + texcoordData = getParamData("texcoord"); if (sphereData.ptr == nullptr) { throw std::runtime_error("#ospray:geometry/spheres: no 'spheres' data " @@ -64,10 +66,8 @@ namespace ospray { } numSpheres = sphereData->numBytes / bytesPerSphere; - std::stringstream msg; - msg << "#osp: creating 'spheres' geometry, #spheres = " - << numSpheres << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) << "#osp: creating 'spheres' geometry, #spheres = " + << numSpheres; if (numSpheres >= (1ULL << 30)) { throw std::runtime_error("#ospray::Spheres: too many spheres in this " @@ -104,8 +104,10 @@ namespace ospray { ispc::SpheresGeometry_set(getIE(),model->getIE(), sphereData->data,_materialList, - colorData?(unsigned char*)colorData->data:nullptr, + texcoordData ? (ispc::vec2f *)texcoordData->data : nullptr, + colorData ? colorData->data : nullptr, colorOffset, colorStride, + colorData && colorData->type == OSP_FLOAT4, numSpheres,bytesPerSphere, radius,materialID, offset_center,offset_radius, diff --git a/ospray/geometry/Spheres.h b/ospray/geometry/Spheres.h index c2408ed653..31f913f08c 100644 --- a/ospray/geometry/Spheres.h +++ b/ospray/geometry/Spheres.h @@ -80,6 +80,8 @@ namespace ospray { /*! data array from which we read the per-sphere color data; if NULL we do not have per-sphere data */ Ref colorData; + + Ref texcoordData; /*! stride in colorData array for accessing i'th sphere's color. color of sphere i will be read as 3 floats from @@ -91,6 +93,8 @@ namespace ospray { 'colorOffset+i*colorStride */ size_t colorOffset; + + float epsilon; //epsilon for intersections }; /*! @} */ diff --git a/ospray/geometry/Spheres.ispc b/ospray/geometry/Spheres.ispc index 0653159779..6cfcd9e39c 100644 --- a/ospray/geometry/Spheres.ispc +++ b/ospray/geometry/Spheres.ispc @@ -31,25 +31,29 @@ struct Spheres { Geometry super; /*! data array that contains the sphere data (possibly but not - necessarily including the color, which could be in color_data); - all of offset and stride values point into here. */ + necessarily including the color, which could be in color); + most offset and stride values point into here. */ uint8 *data; + int32 stride; + int offset_center; + int offset_radius; + int offset_materialID; + int offset_colorID; + float radius; + float epsilon; + /*! list of multiple materials, in case of per-sphere materials */ Material **materialList; - float radius; int materialID; - int offset_center; - int offset_radius; - int offset_materialID; - int offset_colorID; - uint8 *color_data; + uint8 *color; int color_stride; int color_offset; - - int32 bytesPerSphere; + bool has_alpha; // 4th color component is valid + + vec2f *texcoord; }; static void Spheres_postIntersect(uniform Geometry *uniform geometry, @@ -62,18 +66,24 @@ static void Spheres_postIntersect(uniform Geometry *uniform geometry, dg.Ng = dg.Ns = ray.Ng; - if ((flags & DG_COLOR) && self->color_data) { + if ((flags & DG_COLOR) && self->color) { uint32 colorID = 0; if (self->offset_colorID >= 0) { uniform uint8 *varying spherePtr = - self->data + self->bytesPerSphere*ray.primID; + self->data + self->stride*ray.primID; colorID = *((uniform uint32 *varying)(spherePtr+self->offset_colorID)); - } else { + } else colorID = ray.primID; - } uint32 colorAddr = self->color_offset+colorID*self->color_stride; - dg.color = *((vec4f *)(self->color_data+colorAddr)); + dg.color = *((vec4f *)(self->color+colorAddr)); + if (!self->has_alpha) + dg.color.w = 1.f; } + + if (flags & DG_TEXCOORD && self->texcoord) + dg.st = self->texcoord[ray.primID]; + else + dg.st = make_vec2f(0.0f); if (flags & DG_MATERIALID) { if (self->offset_materialID >= 0) { @@ -85,9 +95,9 @@ static void Spheres_postIntersect(uniform Geometry *uniform geometry, uniform uint8 *uniform pagePtr = self->data + (((int64)primPage) * primsPerPage - * self->bytesPerSphere); + * self->stride); uniform uint8 *varying spherePtr = pagePtr - + self->bytesPerSphere*localPrimID; + + self->stride*localPrimID; dg.materialID = *((uniform uint32 *varying)(spherePtr+self->offset_materialID)); if (self->materialList) { @@ -96,7 +106,7 @@ static void Spheres_postIntersect(uniform Geometry *uniform geometry, } } else { uniform uint8 *varying spherePtr = self->data - + self->bytesPerSphere*ray.primID; + + self->stride*ray.primID; dg.materialID = *((uniform uint32 *varying)(spherePtr+self->offset_materialID)); if (self->materialList) { @@ -117,7 +127,7 @@ unmasked void Spheres_bounds(uniform Spheres *uniform self, uniform box3fa &bbox) { uniform uint8 *uniform spherePtr = self->data - + self->bytesPerSphere*((uniform int64)primID); + + self->stride*((uniform int64)primID); uniform bool offr = self->offset_radius >= 0; uniform float radius = offr ? *((uniform float *uniform)(spherePtr+self->offset_radius)) : @@ -132,7 +142,7 @@ void Spheres_intersect(uniform Spheres *uniform self, uniform size_t primID) { uniform uint8 *uniform spherePtr = - self->data + self->bytesPerSphere*((uniform int64)primID); + self->data + self->stride*((uniform int64)primID); uniform float radius = self->radius; if (self->offset_radius >= 0) { radius = *((uniform float *uniform)(spherePtr+self->offset_radius)); @@ -157,7 +167,7 @@ void Spheres_intersect(uniform Spheres *uniform self, if (t_in > ray.t0 && t_in < ray.t) { hit = true; ray.t = t_in; - } else if (t_out > ray.t0 && t_out < ray.t) { + } else if (t_out > (ray.t0 + self->epsilon) && t_out < ray.t) { hit = true; ray.t = t_out; } @@ -190,14 +200,14 @@ SampleAreaRes Spheres_sampleArea( uint8 *uniform pagePtr = self->data + (((int64)primPage) * primsPerPage - * self->bytesPerSphere); - uint8 *spherePtr = pagePtr + self->bytesPerSphere*localPrimID; + * self->stride); + uint8 *spherePtr = pagePtr + self->stride*localPrimID; center = *((vec3f*)(spherePtr+self->offset_center)); if (self->offset_radius >= 0) radius = *((float *)(spherePtr+self->offset_radius)); } } else { - uint8 *spherePtr = self->data + self->bytesPerSphere*primID; + uint8 *spherePtr = self->data + self->stride*primID; center = *((vec3f*)(spherePtr+self->offset_center)); if (self->offset_radius >= 0) radius = *((float *)(spherePtr+self->offset_radius)); @@ -240,7 +250,7 @@ void Spheres_getAreas( area[i] = sphereArea; } else { const uniform uint8 *uniform radiusPtr = self->data + self->offset_radius; - const uniform int64 stride = self->bytesPerSphere; + const uniform int64 stride = self->stride; // TODO vectorize this loop, with foreach or ProgramCount & ProgramIndex for(uniform int32 i = 0; i < spheres; i++, radiusPtr += stride) { const uniform float radius = *((float *uniform)radiusPtr); @@ -259,21 +269,24 @@ export void *uniform Spheres_create(void *uniform cppEquivalent) return self; } -export void SpheresGeometry_set(void *uniform _self, - void *uniform _model, - void *uniform data, - void *uniform materialList, - uint8 *uniform color_data, - int uniform color_offset, - int uniform color_stride, - int uniform numSpheres, - int uniform bytesPerSphere, - float uniform radius, - int uniform materialID, - int uniform offset_center, - int uniform offset_radius, - int uniform offset_materialID, - int uniform offset_colorID) +export void SpheresGeometry_set(void *uniform _self + , void *uniform _model + , void *uniform data + , void *uniform materialList + , vec2f *uniform texcoord + , void *uniform color + , uniform int color_offset + , uniform int color_stride + , uniform bool has_alpha + , uniform int numSpheres + , uniform int bytesPerSphere + , uniform float radius + , uniform int materialID + , uniform int offset_center + , uniform int offset_radius + , uniform int offset_materialID + , uniform int offset_colorID + ) { uniform Spheres *uniform self = (uniform Spheres *uniform)_self; uniform Model *uniform model = (uniform Model *uniform)_model; @@ -286,19 +299,25 @@ export void SpheresGeometry_set(void *uniform _self, self->super.getAreas = Spheres_getAreas; self->super.sampleArea = Spheres_sampleArea; self->materialList = (Material **)materialList; - self->color_data = color_data; + self->texcoord = texcoord; + self->color = (uint8 *uniform)color; self->color_stride = color_stride; self->color_offset = color_offset; + self->has_alpha = has_alpha; self->radius = radius; - self->data = (uniform uint8 *uniform)data; + self->data = (uint8 *uniform)data; self->materialID = materialID; - self->bytesPerSphere = bytesPerSphere; + self->stride = bytesPerSphere; self->offset_center = offset_center; self->offset_radius = offset_radius; self->offset_materialID = offset_materialID; self->offset_colorID = offset_colorID; + self->epsilon = log(self->radius); + if (self->epsilon < 0.f) + self->epsilon = -1.f/self->epsilon; + rtcSetUserData(model->embreeSceneHandle,geomID,self); rtcSetBoundsFunction(model->embreeSceneHandle,geomID, (uniform RTCBoundsFunc)&Spheres_bounds); diff --git a/ospray/geometry/StreamLines.cpp b/ospray/geometry/StreamLines.cpp index 240b090dc3..590e98a666 100644 --- a/ospray/geometry/StreamLines.cpp +++ b/ospray/geometry/StreamLines.cpp @@ -52,12 +52,10 @@ namespace ospray { numVertices = vertexData->numItems; color = colorData ? (const vec4f*)colorData->data : nullptr; - std::stringstream msg; - msg << "#osp: creating streamlines geometry, " - << "#verts=" << numVertices << ", " - << "#segments=" << numSegments << ", " - << "radius=" << radius << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) << "#osp: creating streamlines geometry, " + << "#verts=" << numVertices << ", " + << "#segments=" << numSegments << ", " + << "radius=" << radius; bounds = empty; if (vertex) { diff --git a/ospray/geometry/TriangleMesh.cpp b/ospray/geometry/TriangleMesh.cpp index eb7730a854..8febd47e49 100644 --- a/ospray/geometry/TriangleMesh.cpp +++ b/ospray/geometry/TriangleMesh.cpp @@ -18,10 +18,6 @@ #include "TriangleMesh.h" #include "common/Model.h" #include "../include/ospray/ospray.h" -// embree -#include "embree2/rtcore.h" -#include "embree2/rtcore_scene.h" -#include "embree2/rtcore_geometry.h" // ispc exports #include "TriangleMesh_ispc.h" #include @@ -52,11 +48,12 @@ namespace ospray { static int numPrints = 0; numPrints++; if (numPrints == 5) { - postErrorMsg("(all future printouts for triangle mesh creation " - "will be omitted)\n", 2); + postStatusMsg(2) << "(all future printouts for triangle mesh creation " + << "will be omitted)"; } - if (numPrints < 5) postErrorMsg("ospray: finalizing trianglemesh ...\n", 2); + if (numPrints < 5) + postStatusMsg(2) << "ospray: finalizing trianglemesh ..."; Assert(model && "invalid model pointer"); @@ -133,6 +130,7 @@ namespace ospray { default: throw std::runtime_error("unsupported trianglemesh.vertex data type"); } + if (normalData) switch (normalData->type) { case OSP_FLOAT3: numCompsInNor = 3; break; case OSP_FLOAT: @@ -157,11 +155,9 @@ namespace ospray { bounds.extend(*(vec3f*)(vertex + i)); if (numPrints < 5) { - std::stringstream msg; - msg << " created triangle mesh (" << numTris << " tris " - << ", " << numVerts << " vertices)" << std::endl; - msg << " mesh bounds " << bounds << std::endl; - postErrorMsg(msg, 2); + postStatusMsg(2) << " created triangle mesh (" << numTris << " tris " + << ", " << numVerts << " vertices)\n" + << " mesh bounds " << bounds; } ispc::TriangleMesh_set(getIE(),model->getIE(),eMesh, diff --git a/ospray/geometry/TriangleMesh.ispc b/ospray/geometry/TriangleMesh.ispc index 147e124898..2d4f5358a3 100644 --- a/ospray/geometry/TriangleMesh.ispc +++ b/ospray/geometry/TriangleMesh.ispc @@ -53,7 +53,6 @@ inline vec3##S gather_vec3##S(const uniform bool huge, \ const varying int index) \ { \ vec3##S v; \ - \ if (huge) { \ const int index_lo = index & ((1<.so - /*! returns 0 if the module could be loaded, else it returns an error code > 0 */ - OSPRAY_INTERFACE int32_t ospLoadModule(const char *pluginName); + //! returns OSPError value to report any errors during initialization + OSPRAY_INTERFACE OSPError ospLoadModule(const char *pluginName); //! use renderer to render a frame. /*! What input to use for rendering the frame is encoded in the @@ -463,7 +447,7 @@ extern "C" { [regionCoords...regionCoord+regionSize]. */ #ifdef __cplusplus - OSPRAY_INTERFACE int ospSetRegion(/*! the object we're writing this block of pixels into */ + OSPRAY_INTERFACE OSPError ospSetRegion(/*! the object we're writing this block of pixels into */ OSPVolume, /* points to the first voxel to be copies. The voxels at 'source' MUST have dimensions @@ -478,7 +462,7 @@ extern "C" { be the same as the dimensions of source[][][] */ const osp::vec3i ®ionSize); #else - OSPRAY_INTERFACE int ospSetRegion(OSPVolume, + OSPRAY_INTERFACE OSPError ospSetRegion(OSPVolume, void *source, const osp_vec3i *regionCoords, const osp_vec3i *regionSize); diff --git a/ospray/include/ospray/ospray_cpp/Device.h b/ospray/include/ospray/ospray_cpp/Device.h index cc848a18cf..515088616e 100644 --- a/ospray/include/ospray/ospray_cpp/Device.h +++ b/ospray/include/ospray/ospray_cpp/Device.h @@ -28,9 +28,9 @@ class Device { public: - Device(const std::string &type); + Device(const std::string &type = "default"); Device(const Device ©); - Device(OSPDevice existing = nullptr); + Device(OSPDevice existing); void set(const std::string &name, const std::string &v) const; void set(const std::string &name, int v) const; @@ -39,51 +39,58 @@ class Device void setCurrent() const; + OSPDevice handle() const; + private: - OSPDevice handle; + OSPDevice ospHandle; }; // Inlined function definitions /////////////////////////////////////////////// inline Device::Device(const std::string &type) { - OSPDevice c = ospCreateDevice(type.c_str()); + OSPDevice c = ospNewDevice(type.c_str()); if (c) { - handle = c; + ospHandle = c; } else { throw std::runtime_error("Failed to create OSPDevice!"); } } inline Device::Device(const Device ©) : - handle(copy.handle) + ospHandle(copy.ospHandle) { } inline Device::Device(OSPDevice existing) : - handle(existing) + ospHandle(existing) { } inline void Device::set(const std::string &name, const std::string &v) const { - ospDeviceSetString(handle, name.c_str(), v.c_str()); + ospDeviceSetString(ospHandle, name.c_str(), v.c_str()); } inline void Device::set(const std::string &name, int v) const { - ospDeviceSet1i(handle, name.c_str(), v); + ospDeviceSet1i(ospHandle, name.c_str(), v); } inline void Device::commit() const { - ospDeviceCommit(handle); + ospDeviceCommit(ospHandle); } inline void Device::setCurrent() const { - ospSetCurrentDevice(handle); + ospSetCurrentDevice(ospHandle); +} + +inline OSPDevice Device::handle() const +{ + return ospHandle; } }// namespace cpp diff --git a/ospray/include/ospray/ospray_cpp/FrameBuffer.h b/ospray/include/ospray/ospray_cpp/FrameBuffer.h index 47ac0da767..c59f448fdb 100644 --- a/ospray/include/ospray/ospray_cpp/FrameBuffer.h +++ b/ospray/include/ospray/ospray_cpp/FrameBuffer.h @@ -20,117 +20,117 @@ #include namespace ospray { -namespace cpp { - -class FrameBuffer : public ManagedObject_T -{ -public: - - FrameBuffer() = default;//NOTE(jda) - this does *not* create the underlying - // OSP object - FrameBuffer(const osp::vec2i &size, - OSPFrameBufferFormat format = OSP_FB_SRGBA, - int channels = OSP_FB_COLOR); - FrameBuffer(const FrameBuffer ©); - FrameBuffer(FrameBuffer &&move); - FrameBuffer(OSPFrameBuffer existing); - - FrameBuffer& operator=(const FrameBuffer ©); - FrameBuffer& operator=( FrameBuffer &&move); - - ~FrameBuffer(); - - void setPixelOp(PixelOp &p) const; - void setPixelOp(OSPPixelOp p) const; - - const void *map(OSPFrameBufferChannel channel) const; - void unmap(void *ptr) const; - void clear(uint32_t channel) const; - -private: - - void free() const; - - bool owner = true; -}; - -// Inlined function definitions /////////////////////////////////////////////// - -inline FrameBuffer::FrameBuffer(const osp::vec2i &size, - OSPFrameBufferFormat format, - int channels) -{ - ospObject = ospNewFrameBuffer(size, format, channels); -} - -inline FrameBuffer::FrameBuffer(const FrameBuffer ©) : - ManagedObject_T(copy.handle()), - owner(false) -{ -} - -inline FrameBuffer::FrameBuffer(FrameBuffer &&move) : - ManagedObject_T(move.handle()) -{ - move.ospObject = nullptr; -} - -inline FrameBuffer::FrameBuffer(OSPFrameBuffer existing) : - ManagedObject_T(existing) -{ -} - -inline FrameBuffer& FrameBuffer::operator=(const FrameBuffer ©) -{ - free(); - ospObject = copy.ospObject; - return *this; -} - -inline FrameBuffer& FrameBuffer::operator=(FrameBuffer &&move) -{ - free(); - ospObject = move.ospObject; - move.ospObject = nullptr; - return *this; -} - -inline FrameBuffer::~FrameBuffer() -{ - free(); -} - -inline void FrameBuffer::setPixelOp(PixelOp &p) const -{ - setPixelOp(p.handle()); -} - -inline void FrameBuffer::setPixelOp(OSPPixelOp p) const -{ - ospSetPixelOp(handle(), p); -} - -inline const void *FrameBuffer::map(OSPFrameBufferChannel channel) const -{ - return ospMapFrameBuffer(handle(), channel); -} - -inline void FrameBuffer::unmap(void *ptr) const -{ - ospUnmapFrameBuffer(ptr, handle()); -} - -inline void FrameBuffer::clear(uint32_t channel) const -{ - ospFrameBufferClear(handle(), channel); -} - -inline void FrameBuffer::free() const -{ - if (owner && handle()) { - ospFreeFrameBuffer(handle()); - } -} - -}// namespace cpp + namespace cpp { + + class FrameBuffer : public ManagedObject_T + { + public: + + FrameBuffer() = default;//NOTE(jda) - this does *not* create the underlying + // OSP object + FrameBuffer(const ospcommon::vec2i &size, + OSPFrameBufferFormat format = OSP_FB_SRGBA, + int channels = OSP_FB_COLOR); + FrameBuffer(const FrameBuffer ©); + FrameBuffer(FrameBuffer &&move); + FrameBuffer(OSPFrameBuffer existing); + + FrameBuffer& operator=(const FrameBuffer ©); + FrameBuffer& operator=( FrameBuffer &&move); + + ~FrameBuffer(); + + void setPixelOp(PixelOp &p) const; + void setPixelOp(OSPPixelOp p) const; + + const void *map(OSPFrameBufferChannel channel) const; + void unmap(void *ptr) const; + void clear(uint32_t channel) const; + + private: + + void free() const; + + bool owner = true; + }; + + // Inlined function definitions /////////////////////////////////////////////// + + inline FrameBuffer::FrameBuffer(const ospcommon::vec2i &size, + OSPFrameBufferFormat format, + int channels) + { + ospObject = ospNewFrameBuffer((const osp::vec2i&)size, format, channels); + } + + inline FrameBuffer::FrameBuffer(const FrameBuffer ©) : + ManagedObject_T(copy.handle()), + owner(false) + { + } + + inline FrameBuffer::FrameBuffer(FrameBuffer &&move) : + ManagedObject_T(move.handle()) + { + move.ospObject = nullptr; + } + + inline FrameBuffer::FrameBuffer(OSPFrameBuffer existing) : + ManagedObject_T(existing) + { + } + + inline FrameBuffer& FrameBuffer::operator=(const FrameBuffer ©) + { + free(); + ospObject = copy.ospObject; + return *this; + } + + inline FrameBuffer& FrameBuffer::operator=(FrameBuffer &&move) + { + free(); + ospObject = move.ospObject; + move.ospObject = nullptr; + return *this; + } + + inline FrameBuffer::~FrameBuffer() + { + free(); + } + + inline void FrameBuffer::setPixelOp(PixelOp &p) const + { + setPixelOp(p.handle()); + } + + inline void FrameBuffer::setPixelOp(OSPPixelOp p) const + { + ospSetPixelOp(handle(), p); + } + + inline const void *FrameBuffer::map(OSPFrameBufferChannel channel) const + { + return ospMapFrameBuffer(handle(), channel); + } + + inline void FrameBuffer::unmap(void *ptr) const + { + ospUnmapFrameBuffer(ptr, handle()); + } + + inline void FrameBuffer::clear(uint32_t channel) const + { + ospFrameBufferClear(handle(), channel); + } + + inline void FrameBuffer::free() const + { + if (owner && handle()) { + ospFreeFrameBuffer(handle()); + } + } + + }// namespace cpp }// namespace ospray diff --git a/ospray/include/ospray/ospray_cpp/Geometry.h b/ospray/include/ospray/ospray_cpp/Geometry.h index c00e2a4edc..1306b5b9fd 100644 --- a/ospray/include/ospray/ospray_cpp/Geometry.h +++ b/ospray/include/ospray/ospray_cpp/Geometry.h @@ -20,51 +20,51 @@ #include namespace ospray { -namespace cpp { + namespace cpp { -class Geometry : public ManagedObject_T -{ -public: + class Geometry : public ManagedObject_T + { + public: - Geometry(const std::string &type); - Geometry(const Geometry ©); - Geometry(OSPGeometry existing); + Geometry(const std::string &type); + Geometry(const Geometry ©); + Geometry(OSPGeometry existing); - void setMaterial(Material &m) const; - void setMaterial(OSPMaterial m) const; -}; + void setMaterial(Material &m) const; + void setMaterial(OSPMaterial m) const; + }; -// Inlined function definitions /////////////////////////////////////////////// + // Inlined function definitions /////////////////////////////////////////////// -inline Geometry::Geometry(const std::string &type) -{ - OSPGeometry c = ospNewGeometry(type.c_str()); - if (c) { - ospObject = c; - } else { - throw std::runtime_error("Failed to create OSPGeometry!"); - } -} + inline Geometry::Geometry(const std::string &type) + { + OSPGeometry c = ospNewGeometry(type.c_str()); + if (c) { + ospObject = c; + } else { + throw std::runtime_error("Failed to create OSPGeometry!"); + } + } -inline Geometry::Geometry(const Geometry ©) : - ManagedObject_T(copy.handle()) -{ -} + inline Geometry::Geometry(const Geometry ©) : + ManagedObject_T(copy.handle()) + { + } -inline Geometry::Geometry(OSPGeometry existing) : - ManagedObject_T(existing) -{ -} + inline Geometry::Geometry(OSPGeometry existing) : + ManagedObject_T(existing) + { + } -inline void Geometry::setMaterial(Material &m) const -{ - setMaterial(m.handle()); -} + inline void Geometry::setMaterial(Material &m) const + { + setMaterial(m.handle()); + } -inline void Geometry::setMaterial(OSPMaterial m) const -{ - ospSetMaterial(handle(), m); -} + inline void Geometry::setMaterial(OSPMaterial m) const + { + ospSetMaterial(handle(), m); + } -}// namespace cpp + }// namespace cpp }// namespace ospray diff --git a/ospray/include/ospray/ospray_cpp/ManagedObject.h b/ospray/include/ospray/ospray_cpp/ManagedObject.h index e794d06829..6fec0649f9 100644 --- a/ospray/include/ospray/ospray_cpp/ManagedObject.h +++ b/ospray/include/ospray/ospray_cpp/ManagedObject.h @@ -21,6 +21,7 @@ #include #include "ospcommon/vec.h" +#include "ospcommon/AffineSpace.h" namespace ospray { namespace cpp { @@ -42,11 +43,13 @@ namespace ospray { virtual void set(const std::string &name, float v) const = 0; virtual void set(const std::string &name, float v1, float v2) const = 0; virtual void set(const std::string &name, float v1, float v2, float v3) const = 0; + virtual void set(const std::string &name, float v1, float v2, float v3, float v4) const = 0; // double virtual void set(const std::string &name, double v) const = 0; virtual void set(const std::string &name, double v1, double v2) const = 0; virtual void set(const std::string &name, double v1, double v2, double v3) const = 0; + virtual void set(const std::string &name, double v1, double v2, double v3, double v4) const = 0; // ospcommon::vec2 virtual void set(const std::string &name, const ospcommon::vec2i &v) const = 0; @@ -102,10 +105,12 @@ namespace ospray { void set(const std::string &name, float v) const override; void set(const std::string &name, float v1, float v2) const override; void set(const std::string &name, float v1, float v2, float v3) const override; + void set(const std::string &name, float v1, float v2, float v3, float v4) const override; void set(const std::string &name, double v) const override; void set(const std::string &name, double v1, double v2) const override; void set(const std::string &name, double v1, double v2, double v3) const override; + void set(const std::string &name, double v1, double v2, double v3, double v4) const override; void set(const std::string &name, const ospcommon::vec2i &v) const override; void set(const std::string &name, const ospcommon::vec2f &v) const override; @@ -132,6 +137,10 @@ namespace ospray { //! Get the underlying specific OSP* handle OSP_TYPE handle() const; + //! return whether the given object is valid, or NULL + inline operator bool () const { return handle() != nullptr; } + + protected: OSP_TYPE ospObject; @@ -204,6 +213,14 @@ namespace ospray { ospSet3f(ospObject, name.c_str(), v1, v2, v3); } + template + inline void ManagedObject_T::set(const std::string &name, + float v1, float v2, + float v3, float v4) const + { + ospSet4f(ospObject, name.c_str(), v1, v2, v3, v4); + } + template inline void ManagedObject_T::set(const std::string &name, double v) const { @@ -224,6 +241,14 @@ namespace ospray { ospSet3f(ospObject, name.c_str(), v1, v2, v3); } + template + inline void ManagedObject_T::set(const std::string &name, + double v1, double v2, + double v3, double v4) const + { + ospSet4f(ospObject, name.c_str(), v1, v2, v3, v4); + } + template inline void ManagedObject_T::set(const std::string &name, const ospcommon::vec2i &v) const diff --git a/ospray/include/ospray/ospray_cpp/Model.h b/ospray/include/ospray/ospray_cpp/Model.h index ec6d15e1d0..08c0bdbdb9 100644 --- a/ospray/include/ospray/ospray_cpp/Model.h +++ b/ospray/include/ospray/ospray_cpp/Model.h @@ -21,90 +21,98 @@ #include namespace ospray { -namespace cpp { - -class Model : public ManagedObject_T -{ -public: - - Model(); - Model(const Model ©); - Model(OSPModel existing); - - void addGeometry(Geometry &v) const; - void addGeometry(OSPGeometry v) const; - - void removeGeometry(Geometry &v) const; - void removeGeometry(OSPGeometry v) const; - - void addVolume(Volume &v) const; - void addVolume(OSPVolume v) const; - - void removeVolume(Volume &v) const; - void removeVolume(OSPVolume v) const; -}; - -// Inlined function definitions /////////////////////////////////////////////// - -inline Model::Model() -{ - OSPModel c = ospNewModel(); - if (c) { - ospObject = c; - } else { - throw std::runtime_error("Failed to create OSPModel!"); - } -} - -inline Model::Model(const Model ©) : - ManagedObject_T(copy.handle()) -{ -} - -inline Model::Model(OSPModel existing) : - ManagedObject_T(existing) -{ -} - -inline void Model::addGeometry(Geometry &v) const -{ - addGeometry(v.handle()); -} - -inline void Model::addGeometry(OSPGeometry v) const -{ - ospAddGeometry(handle(), v); -} - -inline void Model::removeGeometry(Geometry &v) const -{ - removeGeometry(v.handle()); -} - -inline void Model::removeGeometry(OSPGeometry v) const -{ - ospRemoveGeometry(handle(), v); -} - -inline void Model::addVolume(Volume &v) const -{ - addVolume(v.handle()); -} - -inline void Model::addVolume(OSPVolume v) const -{ - ospAddVolume(handle(), v); -} - -inline void Model::removeVolume(Volume &v) const -{ - removeVolume(v.handle()); -} - -inline void Model::removeVolume(OSPVolume v) const -{ - ospRemoveVolume(handle(), v); -} - -}// namespace cpp + namespace cpp { + + class Model : public ManagedObject_T + { + public: + + Model(); + Model(const Model ©); + Model(OSPModel existing); + + void addGeometry(Geometry &v) const; + void addGeometry(OSPGeometry v) const; + + void removeGeometry(Geometry &v) const; + void removeGeometry(OSPGeometry v) const; + + void addVolume(Volume &v) const; + void addVolume(OSPVolume v) const; + + void removeVolume(Volume &v) const; + void removeVolume(OSPVolume v) const; + + Geometry createInstance(const ospcommon::affine3f &transform) const; + }; + + // Inlined function definitions /////////////////////////////////////////////// + + inline Model::Model() + { + OSPModel c = ospNewModel(); + if (c) { + ospObject = c; + } else { + throw std::runtime_error("Failed to create OSPModel!"); + } + } + + inline Model::Model(const Model ©) : + ManagedObject_T(copy.handle()) + { + } + + inline Model::Model(OSPModel existing) : + ManagedObject_T(existing) + { + } + + inline void Model::addGeometry(Geometry &v) const + { + addGeometry(v.handle()); + } + + inline void Model::addGeometry(OSPGeometry v) const + { + ospAddGeometry(handle(), v); + } + + inline void Model::removeGeometry(Geometry &v) const + { + removeGeometry(v.handle()); + } + + inline void Model::removeGeometry(OSPGeometry v) const + { + ospRemoveGeometry(handle(), v); + } + + inline void Model::addVolume(Volume &v) const + { + addVolume(v.handle()); + } + + inline void Model::addVolume(OSPVolume v) const + { + ospAddVolume(handle(), v); + } + + inline void Model::removeVolume(Volume &v) const + { + removeVolume(v.handle()); + } + + inline void Model::removeVolume(OSPVolume v) const + { + ospRemoveVolume(handle(), v); + } + + inline Geometry + Model::createInstance(const ospcommon::affine3f &transform) const + { + return ospNewInstance(handle(), (const osp::affine3f&)transform); + } + + }// namespace cpp }// namespace ospray diff --git a/ospray/include/ospray/ospray_cpp/Renderer.h b/ospray/include/ospray/ospray_cpp/Renderer.h index 266185250d..38cbf8cecb 100644 --- a/ospray/include/ospray/ospray_cpp/Renderer.h +++ b/ospray/include/ospray/ospray_cpp/Renderer.h @@ -22,74 +22,74 @@ #include namespace ospray { -namespace cpp { + namespace cpp { -class Renderer : public ManagedObject_T -{ -public: + class Renderer : public ManagedObject_T + { + public: - Renderer(const std::string &type); - Renderer(const Renderer ©); - Renderer(OSPRenderer existing = nullptr); + Renderer(const std::string &type); + Renderer(const Renderer ©); + Renderer(OSPRenderer existing = nullptr); - Material newMaterial(const std::string &type) const; - Light newLight(const std::string &type) const; + Material newMaterial(const std::string &type) const; + Light newLight(const std::string &type) const; - float renderFrame(const FrameBuffer &fb, uint32_t channels) const; + float renderFrame(const FrameBuffer &fb, uint32_t channels) const; - OSPPickResult pick(const ospcommon::vec2f &screenPos) const; -}; + OSPPickResult pick(const ospcommon::vec2f &screenPos) const; + }; -// Inlined function definitions /////////////////////////////////////////////// + // Inlined function definitions /////////////////////////////////////////////// -inline Renderer::Renderer(const std::string &type) -{ - OSPRenderer c = ospNewRenderer(type.c_str()); - if (c) { - ospObject = c; - } else { - throw std::runtime_error("Failed to create OSPRenderer!"); - } -} + inline Renderer::Renderer(const std::string &type) + { + OSPRenderer c = ospNewRenderer(type.c_str()); + if (c) { + ospObject = c; + } else { + throw std::runtime_error("Failed to create OSPRenderer!"); + } + } -inline Renderer::Renderer(const Renderer ©) : - ManagedObject_T(copy.handle()) -{ -} + inline Renderer::Renderer(const Renderer ©) : + ManagedObject_T(copy.handle()) + { + } -inline Renderer::Renderer(OSPRenderer existing) : - ManagedObject_T(existing) -{ -} + inline Renderer::Renderer(OSPRenderer existing) : + ManagedObject_T(existing) + { + } -inline Material Renderer::newMaterial(const std::string &type) const -{ - auto mat = Material(ospNewMaterial(handle(), type.c_str())); + inline Material Renderer::newMaterial(const std::string &type) const + { + auto mat = Material(ospNewMaterial(handle(), type.c_str())); - if (!mat.handle()) { - throw std::runtime_error("Failed to create OSPMaterial!"); - } + if (!mat.handle()) { + throw std::runtime_error("Failed to create OSPMaterial!"); + } - return mat; -} + return mat; + } -inline Light Renderer::newLight(const std::string &type) const -{ - return Light(ospNewLight(handle(), type.c_str())); -} + inline Light Renderer::newLight(const std::string &type) const + { + return Light(ospNewLight(handle(), type.c_str())); + } -inline float Renderer::renderFrame(const FrameBuffer &fb, uint32_t channels) const -{ - return ospRenderFrame(fb.handle(), handle(), channels); -} + inline float Renderer::renderFrame(const FrameBuffer &fb, uint32_t channels) const + { + return ospRenderFrame(fb.handle(), handle(), channels); + } -inline OSPPickResult Renderer::pick(const ospcommon::vec2f &screenPos) const -{ - OSPPickResult result; - ospPick(&result, handle(), (const osp::vec2f&)screenPos); - return result; -} + inline OSPPickResult Renderer::pick(const ospcommon::vec2f &screenPos) const + { + OSPPickResult result; + ospPick(&result, handle(), (const osp::vec2f&)screenPos); + return result; + } -}// namespace cpp + }// namespace cpp }// namespace ospray diff --git a/ospray/include/ospray/ospray_cpp/Volume.h b/ospray/include/ospray/ospray_cpp/Volume.h index 948500826a..17542886aa 100644 --- a/ospray/include/ospray/ospray_cpp/Volume.h +++ b/ospray/include/ospray/ospray_cpp/Volume.h @@ -67,6 +67,7 @@ inline void Volume::setRegion(void *source, const ospcommon::vec3i ®ionCoords, const ospcommon::vec3i ®ionSize) const { + // TODO return error code ospSetRegion(handle(), source, (const osp::vec3i&)regionCoords, diff --git a/ospray/ispc_symbols.txt b/ospray/ispc_symbols.txt new file mode 100644 index 0000000000..8389d8ee1e --- /dev/null +++ b/ospray/ispc_symbols.txt @@ -0,0 +1,25 @@ +BlockBrickedVolume_Constructor___un_3C_s_5B_unBlockBrickedVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_, +Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_, +Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_, +Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, +Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_, +FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_un_3C_unv_3E_, +FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuuni, +Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C_unv_3E_un_3C____un_3C_s_5B_unGeometry_5D__3E_un_3C_s_5B_unModel_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_un_3C_s_5B_unModel_5D__3E_uniun_3C_s_5B_unMaterial_5D__3E_, +GridAccelerator_createInstance___un_3C_unv_3E_, +GridAccelerator_destroy___un_3C_s_5B_unGridAccelerator_5D__3E_, +GridAccelerator_intersect___un_3C_s_5B_unGridAccelerator_5D__3E_unfREFs_5B_vyRay_5D_, +GridAccelerator_intersectIsosurface___un_3C_s_5B_unGridAccelerator_5D__3E_unfun_3C_unf_3E_uniREFs_5B_vyRay_5D_, +Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyf, +Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_, +Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_un_3C_unv_3E_un_3C_unv_3E_Cuni, +SharedStructuredVolume_Constructor___un_3C_s_5B_unSharedStructuredVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_un_3C_Cunv_3E_, +StructuredVolume_Constructor___un_3C_s_5B_unStructuredVolume_5D__3E_un_3C_unv_3E_REFs_5B__c_unvec3i_5D_, +Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_un_3C_unv_3E_, +delete_uniform___un_3C_unv_3E_, +delete_uniform_, +error_handler___Cvyenum_5B_RTCError_5D_vy_3C_Cunt_3E_, +precomputedHalton_create___, +precomputedZOrder_create___, +print_box___REFs_5B__c_unbox3f_5D_, +print_box___REFs_5B__c_unbox3fa_5D_, diff --git a/ospray/lights/AmbientLight.cpp b/ospray/lights/AmbientLight.cpp index 8aac1672da..e4578bafe5 100644 --- a/ospray/lights/AmbientLight.cpp +++ b/ospray/lights/AmbientLight.cpp @@ -29,7 +29,8 @@ namespace ospray { return "ospray::AmbientLight"; } - void AmbientLight::commit() { + void AmbientLight::commit() + { Light::commit(); color = getParam3f("color", vec3f(1.f)); @@ -47,4 +48,4 @@ namespace ospray { OSP_REGISTER_LIGHT(AmbientLight, AmbientLight); OSP_REGISTER_LIGHT(AmbientLight, ambient); -}// ::ospray +} // ::ospray diff --git a/ospray/lights/AmbientLight.h b/ospray/lights/AmbientLight.h index 8c4110403d..0c24d13bbe 100644 --- a/ospray/lights/AmbientLight.h +++ b/ospray/lights/AmbientLight.h @@ -35,4 +35,4 @@ namespace ospray { float intensity {1.f};//!< Amount of light emitted }; -} +} // ::ospray diff --git a/ospray/lights/DirectionalLight.cpp b/ospray/lights/DirectionalLight.cpp index 05d39c4470..ca44c414e3 100644 --- a/ospray/lights/DirectionalLight.cpp +++ b/ospray/lights/DirectionalLight.cpp @@ -29,7 +29,6 @@ namespace ospray { return "ospray::DirectionalLight"; } - //! Commit parameters understood by the DirectionalLight void DirectionalLight::commit() { Light::commit(); @@ -53,4 +52,4 @@ namespace ospray { OSP_REGISTER_LIGHT(DirectionalLight, distant); OSP_REGISTER_LIGHT(DirectionalLight, directional); -}// ::ospray +} // ::ospray diff --git a/ospray/lights/DirectionalLight.h b/ospray/lights/DirectionalLight.h index bb70eacd13..c2c9e62cf3 100644 --- a/ospray/lights/DirectionalLight.h +++ b/ospray/lights/DirectionalLight.h @@ -26,8 +26,8 @@ namespace ospray { { DirectionalLight(); virtual ~DirectionalLight() = default; - virtual std::string toString() const; - virtual void commit(); + virtual std::string toString() const override; + virtual void commit() override; private: vec3f direction {0.f, 0.f, 1.f};//!< Direction of the emitted rays @@ -36,4 +36,4 @@ namespace ospray { float angularDiameter {0.f}; //!< Apparent size of the distant light, in degree (e.g. 0.53 for the sun) }; -}// ::ospray +} // ::ospray diff --git a/ospray/lights/HDRILight.cpp b/ospray/lights/HDRILight.cpp index 0533642ab3..13c2654065 100644 --- a/ospray/lights/HDRILight.cpp +++ b/ospray/lights/HDRILight.cpp @@ -48,13 +48,12 @@ namespace ospray { frame.vy = normalize(cross(frame.vx, up)); frame.vz = cross(frame.vx, frame.vy); - ispc::HDRILight_set( - getIE(), - (const ispc::LinearSpace3f&)frame, - map ? map->getIE() : nullptr, - intensity); + ispc::HDRILight_set(getIE(), + (const ispc::LinearSpace3f&)frame, + map ? map->getIE() : nullptr, + intensity); } OSP_REGISTER_LIGHT(HDRILight, hdri); -// OSP_REGISTER_LIGHT(HDRILight, HDRILight); -} + +} // ::ospray diff --git a/ospray/lights/HDRILight.h b/ospray/lights/HDRILight.h index 4d723f2470..fc6c5bff1c 100644 --- a/ospray/lights/HDRILight.h +++ b/ospray/lights/HDRILight.h @@ -41,5 +41,5 @@ namespace ospray { float intensity {1.f}; //!< Amount of light emitted }; -} +} // ::ospray diff --git a/ospray/lights/Light.cpp b/ospray/lights/Light.cpp index aff36b05cc..a26758d127 100644 --- a/ospray/lights/Light.cpp +++ b/ospray/lights/Light.cpp @@ -16,12 +16,8 @@ //ospray #include "Light.h" -#include "common/Library.h" #include "common/Util.h" -// ispc exports #include "Light_ispc.h" -//system -#include namespace ospray { @@ -37,7 +33,6 @@ namespace ospray { return "ospray::Light"; } - //! Create a new Light object of given type Light *Light::createLight(const char *type) { return createInstanceHelper(type); diff --git a/ospray/lights/Light.h b/ospray/lights/Light.h index 834f0cfcf6..690e77dde6 100644 --- a/ospray/lights/Light.h +++ b/ospray/lights/Light.h @@ -40,4 +40,4 @@ namespace ospray { #define OSP_REGISTER_LIGHT(InternalClass, external_name) \ OSP_REGISTER_OBJECT(Light, light, InternalClass, external_name) -} +} // ::ospray diff --git a/ospray/lights/QuadLight.cpp b/ospray/lights/QuadLight.cpp index 475576fd73..ea4d430321 100644 --- a/ospray/lights/QuadLight.cpp +++ b/ospray/lights/QuadLight.cpp @@ -17,11 +17,6 @@ #include "QuadLight.h" #include "QuadLight_ispc.h" -#ifdef _WIN32 -# define _USE_MATH_DEFINES -# include // M_PI -#endif - namespace ospray { QuadLight::QuadLight() diff --git a/ospray/lights/SpotLight.cpp b/ospray/lights/SpotLight.cpp index 3f54b8bce2..fbc39f50c1 100644 --- a/ospray/lights/SpotLight.cpp +++ b/ospray/lights/SpotLight.cpp @@ -17,12 +17,6 @@ #include "SpotLight.h" #include "SpotLight_ispc.h" -#ifdef _WIN32 -# define _USE_MATH_DEFINES -# include // M_PI -#endif - - namespace ospray { SpotLight::SpotLight() @@ -35,7 +29,6 @@ namespace ospray { return "ospray::SpotLight"; } - //!< Copy understood parameters into class members void SpotLight::commit() { Light::commit(); diff --git a/ospray/math/vec.ih b/ospray/math/vec.ih index c09adba249..cbe36f49ad 100644 --- a/ospray/math/vec.ih +++ b/ospray/math/vec.ih @@ -469,26 +469,6 @@ __define_binary_operator( operator/, / ); #undef __define_binary_operator -inline float reduce_mul(const vec3f v) -{ return v.x * v.y * v.z; } -inline uniform float reduce_mul(const uniform vec3f v) -{ return v.x * v.y * v.z; } - -inline float reduce_max(const vec3f v) -{ return max(max(v.x,v.y),v.z); } - -inline float reduce_add(const vec3f v) -{ return v.x+v.y+v.z; } - -inline uniform float reduce_add(const uniform vec3f v) -{ return v.x+v.y+v.z; } - -inline float reduce_avg(const vec3f v) -{ return (v.x+v.y+v.z)*(1.0f/3.0f); } - -inline float luminance(const vec3f& c) -{ return 0.212671f*c.x + 0.715160f*c.y + 0.072169f*c.z; } - inline uniform bool eq(const uniform vec2f a, const uniform vec2f b) { return a.x==b.x && a.y==b.y; } inline bool eq(const vec2f a, const vec2f b) @@ -809,16 +789,17 @@ inline vec3i clamp(const vec3i &a, const uniform vec3i &b, const uniform vec3i & inline uniform vec3f nextafter(const uniform vec3i &a, const uniform vec3i &b) { return(make_vec3f(nextafter(a.x, b.x), nextafter(a.y, b.y), nextafter(a.z, b.z))); } -inline varying float reduce_min(const varying vec3f &a) + +inline varying int reduce_min(const varying vec3i &a) { return min(min(a.x, a.y), a.z); } -inline uniform float reduce_min(const uniform vec3f &a) +inline uniform int reduce_min(const uniform vec3i &a) { return min(min(a.x, a.y), a.z); } -inline uniform float reduce_min(const uniform vec3i &a) +inline varying float reduce_min(const varying vec3f &a) { return min(min(a.x, a.y), a.z); } -inline varying float reduce_min(const varying vec3i &a) +inline uniform float reduce_min(const uniform vec3f &a) { return min(min(a.x, a.y), a.z); } inline varying float reduce_min(const varying vec4f &a) @@ -827,10 +808,11 @@ inline varying float reduce_min(const varying vec4f &a) inline uniform float reduce_min(const uniform vec4f &a) { return min(min(a.x, a.y), min(a.z, a.w)); } -inline uniform float reduce_max(const uniform vec3i &a) + +inline varying int reduce_max(const varying vec3i &a) { return max(max(a.x, a.y), a.z); } -inline varying float reduce_max(const varying vec3i &a) +inline uniform int reduce_max(const uniform vec3i &a) { return max(max(a.x, a.y), a.z); } inline varying float reduce_max(const varying vec3f &a) @@ -845,15 +827,40 @@ inline varying float reduce_max(const varying vec4f &a) inline uniform float reduce_max(const uniform vec4f &a) { return max(max(a.x, a.y), max(a.z, a.w)); } + +inline varying float reduce_mul(const varying vec3f &a) +{ return a.x * a.y * a.z; } + +inline uniform float reduce_mul(const uniform vec3f &a) +{ return a.x * a.y * a.z; } + + +inline varying float reduce_add(const varying vec3f &a) +{ return a.x + a.y + a.z; } + inline uniform float reduce_add(const uniform vec3f &a) -{ return a.x+a.y+a.z; } +{ return a.x + a.y + a.z; } + +inline varying float reduce_add(const varying vec4f &a) +{ return (a.x + a.y) + (a.z + a.w); } + +inline uniform float reduce_add(const uniform vec4f &a) +{ return (a.x + a.y) + (a.z + a.w); } + + +inline varying float reduce_avg(const varying vec3f &a) +{ return reduce_add(a)*(1.0f/3.0f); } inline uniform float reduce_avg(const uniform vec3f &a) { return reduce_add(a)*(1.0f/3.0f); } -inline uniform float luminance(const uniform vec3f& c) +inline varying float luminance(const varying vec3f &c) { return 0.212671f*c.x + 0.715160f*c.y + 0.072169f*c.z; } +inline uniform float luminance(const uniform vec3f &c) +{ return 0.212671f*c.x + 0.715160f*c.y + 0.072169f*c.z; } + + inline varying vec3f pow(const varying vec3f &a, const varying float b) { return make_vec3f(pow(a.x, b), pow(a.y, b), pow(a.z, b)); } diff --git a/ospray/ospray.def b/ospray/ospray.def deleted file mode 100644 index c6eed5b70e..0000000000 --- a/ospray/ospray.def +++ /dev/null @@ -1,79 +0,0 @@ -EXPORTS -BlockBrickedVolume_Constructor___un_3C_s_5B_unBlockBrickedVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_avx -BlockBrickedVolume_Constructor___un_3C_s_5B_unBlockBrickedVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_avx2 -BlockBrickedVolume_Constructor___un_3C_s_5B_unBlockBrickedVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_sse4 -Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_avx -Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_avx2 -Distribution2D_create___s_5B__c_unvec2i_5D_un_3C_unf_3E_sse4 -Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_avx -Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_avx2 -Distribution2D_destroy___un_3C_s_5B_unDistribution2D_5D__3E_sse4 -Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_avx -Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_avx2 -Distribution2D_pdf___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_sse4 -Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_avx -Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_avx2 -Distribution2D_sample___un_3C_s_5B__c_unDistribution2D_5D__3E_REFs_5B__c_vyvec2f_5D_sse4 -FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_un_3C_unv_3E_avx -FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_un_3C_unv_3E_avx2 -FrameBuffer_Constructor___un_3C_s_5B_unFrameBuffer_5D__3E_un_3C_unv_3E_sse4 -FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuuniavx -FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuuniavx2 -FrameBuffer_set___un_3C_s_5B_unFrameBuffer_5D__3E_CunuCunuunisse4 -Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C_unv_3E_un_3C____un_3C_s_5B_unGeometry_5D__3E_un_3C_s_5B_unModel_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_un_3C_s_5B_unModel_5D__3E_uniun_3C_s_5B_unMaterial_5D__3E_avx -Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C_unv_3E_un_3C____un_3C_s_5B_unGeometry_5D__3E_un_3C_s_5B_unModel_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_un_3C_s_5B_unModel_5D__3E_uniun_3C_s_5B_unMaterial_5D__3E_avx2 -Geometry_Constructor___un_3C_s_5B_unGeometry_5D__3E_un_3C_unv_3E_un_3C____un_3C_s_5B_unGeometry_5D__3E_un_3C_s_5B_unModel_5D__3E_REFs_5B_vyDifferentialGeometry_5D_REFs_5B__c_vyRay_5D_unI_3E_un_3C_s_5B_unModel_5D__3E_uniun_3C_s_5B_unMaterial_5D__3E_sse4 -GridAccelerator_createInstance___un_3C_unv_3E_avx -GridAccelerator_createInstance___un_3C_unv_3E_avx2 -GridAccelerator_createInstance___un_3C_unv_3E_sse4 -GridAccelerator_destroy___un_3C_s_5B_unGridAccelerator_5D__3E_avx -GridAccelerator_destroy___un_3C_s_5B_unGridAccelerator_5D__3E_avx2 -GridAccelerator_destroy___un_3C_s_5B_unGridAccelerator_5D__3E_sse4 -GridAccelerator_intersect___un_3C_s_5B_unGridAccelerator_5D__3E_unfREFs_5B_vyRay_5D_avx -GridAccelerator_intersect___un_3C_s_5B_unGridAccelerator_5D__3E_unfREFs_5B_vyRay_5D_avx2 -GridAccelerator_intersect___un_3C_s_5B_unGridAccelerator_5D__3E_unfREFs_5B_vyRay_5D_sse4 -GridAccelerator_intersectIsosurface___un_3C_s_5B_unGridAccelerator_5D__3E_unfun_3C_unf_3E_uniREFs_5B_vyRay_5D_avx -GridAccelerator_intersectIsosurface___un_3C_s_5B_unGridAccelerator_5D__3E_unfun_3C_unf_3E_uniREFs_5B_vyRay_5D_avx2 -GridAccelerator_intersectIsosurface___un_3C_s_5B_unGridAccelerator_5D__3E_unfun_3C_unf_3E_uniREFs_5B_vyRay_5D_sse4 -Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyfavx -Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyfavx2 -Light_eval___un_3C_s_5B__c_unLight_5D__3E_REFs_5B__c_vyDifferentialGeometry_5D_REFs_5B__c_vyvec3f_5D_CvyfCvyfsse4 -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_avx -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_avx2 -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_sse4 -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_un_3C_unv_3E_un_3C_unv_3E_Cuniavx -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_un_3C_unv_3E_un_3C_unv_3E_Cuniavx2 -Renderer_Constructor___un_3C_s_5B_unRenderer_5D__3E_un_3C_unv_3E_un_3C_unv_3E_un_3C_unv_3E_Cunisse4 -SharedStructuredVolume_Constructor___un_3C_s_5B_unSharedStructuredVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_un_3C_Cunv_3E_avx -SharedStructuredVolume_Constructor___un_3C_s_5B_unSharedStructuredVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_un_3C_Cunv_3E_avx2 -SharedStructuredVolume_Constructor___un_3C_s_5B_unSharedStructuredVolume_5D__3E_un_3C_unv_3E_CuniREFs_5B__c_unvec3i_5D_un_3C_Cunv_3E_sse4 -StructuredVolume_Constructor___un_3C_s_5B_unStructuredVolume_5D__3E_un_3C_unv_3E_REFs_5B__c_unvec3i_5D_avx -StructuredVolume_Constructor___un_3C_s_5B_unStructuredVolume_5D__3E_un_3C_unv_3E_REFs_5B__c_unvec3i_5D_avx2 -StructuredVolume_Constructor___un_3C_s_5B_unStructuredVolume_5D__3E_un_3C_unv_3E_REFs_5B__c_unvec3i_5D_sse4 -Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_un_3C_unv_3E_avx -Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_un_3C_unv_3E_avx2 -Volume_Constructor___un_3C_s_5B_unVolume_5D__3E_un_3C_unv_3E_sse4 -delete_uniform___un_3C_unv_3E_avx -delete_uniform___un_3C_unv_3E_avx2 -delete_uniform___un_3C_unv_3E_sse4 -delete_uniform_avx -delete_uniform_avx2 -delete_uniform_sse4 -error_handler___Cvyenum_5B_RTCError_5D_vy_3C_Cunt_3E_avx -error_handler___Cvyenum_5B_RTCError_5D_vy_3C_Cunt_3E_avx2 -error_handler___Cvyenum_5B_RTCError_5D_vy_3C_Cunt_3E_sse4 -precomputedHalton_create___avx -precomputedHalton_create___avx2 -precomputedHalton_create___sse4 -precomputedZOrder_create___avx -precomputedZOrder_create___avx2 -precomputedZOrder_create___sse4 -print_box___REFs_5B__c_unbox3f_5D_avx -print_box___REFs_5B__c_unbox3f_5D_avx2 -print_box___REFs_5B__c_unbox3f_5D_sse4 -print_box___REFs_5B__c_unbox3fa_5D_avx -print_box___REFs_5B__c_unbox3fa_5D_avx2 -print_box___REFs_5B__c_unbox3fa_5D_sse4 -precomputedHalton -precomputedHalton_initialized -z_order diff --git a/ospray/render/LoadBalancer.cpp b/ospray/render/LoadBalancer.cpp index a2e01734f0..40b28e38f2 100644 --- a/ospray/render/LoadBalancer.cpp +++ b/ospray/render/LoadBalancer.cpp @@ -33,7 +33,7 @@ namespace ospray { void *perFrameData = renderer->beginFrame(fb); - parallel_for(fb->getTotalTiles(), [&](int taskIndex) { + tasking::parallel_for(fb->getTotalTiles(), [&](int taskIndex) { const size_t numTiles_x = fb->getNumTiles().x; const size_t tile_y = taskIndex / numTiles_x; const size_t tile_x = taskIndex - tile_y*numTiles_x; @@ -45,7 +45,7 @@ namespace ospray { Tile __aligned(64) tile(tileID, fb->size, accumID); - parallel_for(numJobs(renderer->spp, accumID), [&](int tIdx) { + tasking::parallel_for(numJobs(renderer->spp, accumID), [&](int tIdx) { renderer->renderTile(perFrameData, tile, tIdx); }); diff --git a/ospray/render/Renderer.cpp b/ospray/render/Renderer.cpp index 9de2186102..59c1c2d54d 100644 --- a/ospray/render/Renderer.cpp +++ b/ospray/render/Renderer.cpp @@ -34,21 +34,20 @@ namespace ospray { epsilon = getParam1f("epsilon", 1e-6f); spp = getParam1i("spp", 1); errorThreshold = getParam1f("varianceThreshold", 0.f); - backgroundEnabled = getParam1i("backgroundEnabled", 1); maxDepthTexture = (Texture2D*)getParamObject("maxDepthTexture", nullptr); model = (Model*)getParamObject("model", getParamObject("world")); if (maxDepthTexture) { if (maxDepthTexture->type != OSP_TEXTURE_R32F || !(maxDepthTexture->flags & OSP_TEXTURE_FILTER_NEAREST)) { - static WarnOnce warning("expected maxDepthTexture provided to the " - "renderer to be type OSP_TEXTURE_R32F and have " + static WarnOnce warning("maxDepthTexture provided to the renderer " + "needs to be of type OSP_TEXTURE_R32F and have " "the OSP_TEXTURE_FILTER_NEAREST flag"); } } - vec3f bgColor; - bgColor = getParam3f("bgColor", vec3f(1.f)); + vec3f bgColor3 = getParam3f("bgColor", vec3f(getParam1f("bgColor", 0.f))); + bgColor = getParam4f("bgColor", vec4f(bgColor3, 0.f)); if (getIE()) { ManagedObject* camera = getParamObject("camera"); @@ -58,18 +57,18 @@ namespace ospray { epsilon *= diameter; } - ispc::Renderer_set(getIE(), - model ? model->getIE() : nullptr, - camera ? camera->getIE() : nullptr, - epsilon, - spp, - backgroundEnabled, - (ispc::vec3f&)bgColor, - maxDepthTexture ? maxDepthTexture->getIE() : nullptr); + ispc::Renderer_set(getIE() + , model ? model->getIE() : nullptr + , camera ? camera->getIE() : nullptr + , epsilon + , spp + , (ispc::vec4f&)bgColor + , maxDepthTexture ? maxDepthTexture->getIE() : nullptr + ); } } - Renderer *Renderer::createRenderer(const char *type) + Renderer *Renderer::createInstance(const char *type) { return createInstanceHelper(type); } @@ -93,10 +92,7 @@ namespace ospray { float Renderer::renderFrame(FrameBuffer *fb, const uint32 channelFlags) { - // double T0 = getSysTime(); return TiledLoadBalancer::instance->renderFrame(this,fb,channelFlags); - // double T1 = getSysTime(); - // printf("time per frame %lf ms\n",(T1-T0)*1e3f); } OSPPickResult Renderer::pick(const vec2f &screenPos) diff --git a/ospray/render/Renderer.h b/ospray/render/Renderer.h index cc696f07f0..c3d5a5bf3e 100644 --- a/ospray/render/Renderer.h +++ b/ospray/render/Renderer.h @@ -45,7 +45,7 @@ namespace ospray { in either ospray proper or any already loaded module. For renderer types specified in special modules, make sure to call ospLoadModule first. */ - static Renderer *createRenderer(const char *identifier); + static Renderer *createInstance(const char *identifier); virtual void commit() override; virtual std::string toString() const override; @@ -91,12 +91,12 @@ namespace ospray { /*! adaptive accumulation: variance-based error to reach */ float errorThreshold {0.f}; - /*! \brief whether the background should be rendered (e.g. for compositing the background may be disabled) */ - bool backgroundEnabled {true}; + /*! \brief the background color */ + vec4f bgColor {0.f}; /*! \brief maximum depth texture provided as an optional parameter to the renderer, used for early ray termination - The texture format should be OSP_FLOAT and texture filtering + The texture format should be OSP_TEXTURE_R32F and texture filtering should be set to nearest-neighbor interpolation: (OSP_TEXTURE_FILTER_NEAREST). */ Ref maxDepthTexture; diff --git a/ospray/render/Renderer.ih b/ospray/render/Renderer.ih index a425f8f2b3..58760496bf 100644 --- a/ospray/render/Renderer.ih +++ b/ospray/render/Renderer.ih @@ -53,9 +53,6 @@ struct ScreenSample { typedef void (*Renderer_RenderSampleFct)(uniform Renderer *uniform self, void *uniform perFrameData, varying ScreenSample &retValue); -typedef void (*Renderer_ToneMapFct)(uniform Renderer *uniform self, - varying vec3f &color, - const varying vec2i &pixelID); typedef unmasked void (*Renderer_RenderTileFct)(uniform Renderer *uniform self, void *uniform perFrameData, uniform Tile &tile, @@ -67,7 +64,6 @@ typedef unmasked void (*Renderer_EndFrameFct)(uniform Renderer *uniform self, struct Renderer { Renderer_RenderSampleFct renderSample; - Renderer_ToneMapFct toneMap; Renderer_RenderTileFct renderTile; Renderer_BeginFrameFct beginFrame; Renderer_EndFrameFct endFrame; @@ -77,13 +73,12 @@ struct Renderer { FrameBuffer *fb; /*!< currently rendered frame buffer. may be NULL or invalid before start of, or after end of, a frame */ - Model *model; + Model *uniform model; Camera *camera; float epsilon; // parameter to prevent self-intersection issues, will be scaled with diameter of the scene int32 spp; // number of samples per pixel; negative values mean subsampling, i.e. render only every 2^-spp pixel in x and y for the first frame - uniform bool backgroundEnabled; // whether the background should be rendered (e.g. for compositing the background may be disabled) - uniform vec3f bgColor;// background color - uniform Texture2D *uniform maxDepthTexture; // optional maximum depth texture used for early ray termination + vec4f bgColor;// background color and alpha + Texture2D *maxDepthTexture; // optional maximum depth texture used for early ray termination }; void Renderer_Constructor(uniform Renderer *uniform self, void *uniform cppE); diff --git a/ospray/render/Renderer.ispc b/ospray/render/Renderer.ispc index 91fa03ed5f..d2eaa02c73 100644 --- a/ospray/render/Renderer.ispc +++ b/ospray/render/Renderer.ispc @@ -179,21 +179,25 @@ unmasked void Renderer_default_renderTile(uniform Renderer *uniform self, } } +export void Renderer_set(void *uniform _self + , void *uniform _model + , void *uniform _camera + , const uniform float epsilon + , const uniform int32 spp + , const uniform vec4f &bgColor + , void *uniform _maxDepthTexture + ); + void Renderer_Constructor(uniform Renderer *uniform self, void *uniform cppE) { self->cppEquivalent = cppE; - self->model = NULL; - self->camera = NULL; - self->fb = NULL; - self->spp = 1; - self->backgroundEnabled = true; - self->maxDepthTexture = NULL; self->renderSample = Renderer_default_renderSample; self->renderTile = Renderer_default_renderTile; self->beginFrame = Renderer_default_beginFrame; self->endFrame = Renderer_default_endFrame; - self->toneMap = NULL; + self->fb = NULL; + Renderer_set(self, NULL, NULL, 1e-6f, 1, make_vec4f(0.f), NULL); } void Renderer_Constructor(uniform Renderer *uniform self, @@ -208,18 +212,20 @@ void Renderer_Constructor(uniform Renderer *uniform self, self->spp = spp; } + +// Exports (called from C++) +////////////////////////////////////////////////////////////////////////////// + export void Renderer_renderTile(void *uniform _self, void *uniform perFrameData, uniform Tile &tile, uniform int jobID) { - //print("Renderer_renderTile\n"); uniform Renderer *uniform self = (uniform Renderer *uniform)_self; self->renderTile(self, perFrameData, tile, jobID); } -export void *uniform Renderer_beginFrame(void *uniform _self, - void *uniform _fb) +export void *uniform Renderer_beginFrame(void *uniform _self, void *uniform _fb) { uniform Renderer *uniform self = (uniform Renderer *uniform)_self; uniform FrameBuffer *uniform fb = (uniform FrameBuffer *uniform)_fb; @@ -227,28 +233,26 @@ export void *uniform Renderer_beginFrame(void *uniform _self, } -export void Renderer_endFrame(void *uniform _self, - void *uniform perFrameData) +export void Renderer_endFrame(void *uniform _self, void *uniform perFrameData) { uniform Renderer *uniform self = (uniform Renderer *uniform)_self; self->endFrame(self, perFrameData); } -export void Renderer_set(void *uniform _self, - void *uniform _model, - void *uniform _camera, - const uniform float epsilon, - const uniform int32 spp, - const uniform bool backgroundEnabled, - const uniform vec3f &bgColor, - void *uniform _maxDepthTexture) +export void Renderer_set(void *uniform _self + , void *uniform _model + , void *uniform _camera + , const uniform float epsilon + , const uniform int32 spp + , const uniform vec4f &bgColor + , void *uniform _maxDepthTexture + ) { uniform Renderer *uniform self = (uniform Renderer *uniform)_self; self->model = (uniform Model *uniform)_model; self->camera = (uniform Camera *uniform)_camera; self->epsilon = epsilon; self->spp = spp; - self->backgroundEnabled = backgroundEnabled; self->bgColor = bgColor; self->maxDepthTexture = (uniform Texture2D *uniform)_maxDepthTexture; diff --git a/ospray/render/pathtracer/PathTracer.cpp b/ospray/render/pathtracer/PathTracer.cpp index 0ffc693039..5a0a8c0feb 100644 --- a/ospray/render/pathtracer/PathTracer.cpp +++ b/ospray/render/pathtracer/PathTracer.cpp @@ -53,10 +53,8 @@ namespace ospray { std::map numOccurrances; const std::string T = type; if (numOccurrances[T] == 0) { - std::stringstream msg; - msg << "#osp:PT: does not know material type '" << type << "'" << - " (replacing with OBJMaterial)" << std::endl; - postErrorMsg(msg); + postStatusMsg() << "#osp:PT: does not know material type '" << type + << "'" << " (replacing with OBJMaterial)"; } numOccurrances[T]++; material = Material::createMaterial("PathTracer_OBJMaterial"); @@ -92,11 +90,10 @@ namespace ospray { if (light) lightArray.push_back(light); else { - std::stringstream msg; - msg << "#osp:pt Geometry " << geo->toString() << - " does not implement area sampling! Cannot use importance " - "sampling for that geometry with emissive material!" << std::endl; - postErrorMsg(msg, 1); + postStatusMsg(1) << "#osp:pt Geometry " << geo->toString() + << " does not implement area sampling! " + << "Cannot use importance sampling for that " + << "geometry with emissive material!"; } } } @@ -134,11 +131,19 @@ namespace ospray { const float maxRadiance = getParam1f("maxContribution", getParam1f("maxRadiance", inf)); Texture2D *backplate = (Texture2D*)getParamObject("backplate", nullptr); - - ispc::PathTracer_set(getIE(), maxDepth, minContribution, maxRadiance, - backplate ? backplate->getIE() : nullptr, - lightPtr, lightArray.size(), geometryLights, - &areaPDF[0]); + const vec4f shadowCatcherPlane = getParam4f("shadowCatcherPlane", vec4f(0.f)); + + ispc::PathTracer_set(getIE() + , maxDepth + , minContribution + , maxRadiance + , backplate ? backplate->getIE() : nullptr + , (ispc::vec4f&)shadowCatcherPlane + , lightPtr + , lightArray.size() + , geometryLights + , &areaPDF[0] + ); } OSP_REGISTER_RENDERER(PathTracer,pathtracer); diff --git a/ospray/render/pathtracer/PathTracer.ih b/ospray/render/pathtracer/PathTracer.ih index c4f2b48cf4..7a96e928e8 100644 --- a/ospray/render/pathtracer/PathTracer.ih +++ b/ospray/render/pathtracer/PathTracer.ih @@ -32,6 +32,10 @@ struct PathTracer { float minContribution; float maxRadiance; Texture2D* uniform backplate; + // coefficients of plane equation defining geometry to catch shadows for + // compositing; disabled if normal is zero-length + vec4f shadowCatcherPlane; + bool shadowCatcher; // preprocessed const uniform Light *uniform *uniform lights; uint32 numLights; diff --git a/ospray/render/pathtracer/PathTracer.ispc b/ospray/render/pathtracer/PathTracer.ispc index 2ecbc20062..43c87c20e9 100644 --- a/ospray/render/pathtracer/PathTracer.ispc +++ b/ospray/render/pathtracer/PathTracer.ispc @@ -110,13 +110,84 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, lastDg.Ns = ray.dir; lastDg.Ng = ray.dir; + float shadowCatcherDist = -inf; + if (self->shadowCatcher) + shadowCatcherDist = intersectPlane(ray, self->shadowCatcherPlane); + do { + if (shadowCatcherDist > ray.t0) // valid hit can hide other geometry + ray.t = min(shadowCatcherDist, ray.t); + traceRay(self->super.model, ray); + DifferentialGeometry dg; + // record depth of primary rays if (depth == 0) sample.z = ray.t; + + //////////////////////////////////// + // Shadow Catcher + + if (straightPath) { + // TODO use MIS as well + // consider real (flagged) geometries with material and move into + // light loop (will also handle MIS) + if (shadowCatcherDist <= ray.t && shadowCatcherDist > ray.t0) { + // "postIntersect" of shadowCatcher plane + dg.P = ray.org + shadowCatcherDist * ray.dir; + dg.Ns = dg.Ng = make_vec3f(self->shadowCatcherPlane); + if (dot(ray.dir, dg.Ng) >= 0.f) + dg.Ns = dg.Ng = neg(dg.Ng); + + vec3f unshaded = make_vec3f(0.f); // illumination without occluders + vec3f shaded = make_vec3f(0.f); // illumination including shadows + uniform int numLights = self->lights ? min(MAX_LIGHTS, self->numLights) : 0; + for (uniform int i = 0; i < numLights; i++) { + const uniform Light *uniform light = self->lights[i]; + + Light_SampleRes ls = light->sample(light, dg, RandomTEA__getFloats(rng)); + + // skip when zero contribution from light + if (reduce_max(ls.weight) <= 0.0f | ls.pdf <= PDF_CULLING) + continue; + + // evaluate a white diffuse BRDF + const float brdf = clamp(dot(ls.dir, dg.Ns));// * one_over_pi cancels anyway + + // skip when zero contribution from material + if (brdf <= 0.0f) + continue; + + // test for shadows + Ray shadow_ray; + setRay(shadow_ray, dg.P, ls.dir, + self->super.epsilon, ls.dist - self->super.epsilon); + shadow_ray.time = ray.time; + + const vec3f unshaded_light_contrib = Lw * ls.weight * brdf;// * misHeuristic(ls.pdf, brdf); + unshaded = unshaded + unshaded_light_contrib; + shaded = shaded + transparentShadow(self, unshaded_light_contrib, shadow_ray, currentMedium); + } + // order of args important to filter NaNs (in case unshaded.X is zero) + const vec3f ratio = min(Lw * shaded * rcp(unshaded), Lw); +#ifdef COLORED_SHADOW_HACK + const float rm = reduce_min(ratio); + sample.alpha = 1.0f - rm; + L = ratio - rm; +#else + // alpha blend-in black shadow + sample.alpha = 1.0f - luminance(ratio); + L = make_vec3f(0.f); +#endif + break; + } + + // update dist for potential next intersection (if transparent) + shadowCatcherDist -= ray.t; + } + const vec3f wo = neg(ray.dir); float maxLightDist; @@ -162,7 +233,6 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, //////////////////////////////////// // handle next surface interaction - DifferentialGeometry dg; postIntersect(self->super.model, dg, ray, DG_MATERIALID| DG_NS|DG_NG|DG_FACEFORWARD|DG_NORMALIZE|DG_TEXCOORD|DG_COLOR|DG_TANGENTS @@ -209,8 +279,7 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, // direct lighting including shadows and MIS if (bsdf->type & BSDF_SMOOTH) { uniform int numLights = self->lights ? min(MAX_LIGHTS, self->numLights) : 0; - for (uniform int i = 0; i < numLights; i++) - { + for (uniform int i = 0; i < numLights; i++) { const uniform Light *uniform light = self->lights[i]; Light_SampleRes ls = light->sample(light, dg, RandomTEA__getFloats(rng)); @@ -282,7 +351,7 @@ ScreenSample PathTraceIntegrator_Li(const uniform PathTracer* uniform self, sample.rgb = L; if (isnan(L.x) || isnan(L.y) || isnan(L.z)){ - sample.rgb = make_vec3f(0.0f,0.0f,0.0f); + sample.rgb = make_vec3f(0.f); sample.alpha = 1.0f; } return sample; @@ -380,15 +449,17 @@ unmasked void PathTracer_renderTile(uniform Renderer *uniform _self, // Exports (called from C++) ////////////////////////////////////////////////////////////////////////////// -export void PathTracer_set(void *uniform _self, - const uniform int32 maxDepth, - const uniform float minContribution, - const uniform float maxRadiance, - void *uniform backplate, - void **uniform lights, - const uniform uint32 numLights, - const uniform uint32 numGeoLights, - void *uniform areaPDF) +export void PathTracer_set(void *uniform _self + , const uniform int32 maxDepth + , const uniform float minContribution + , const uniform float maxRadiance + , void *uniform backplate + , const uniform vec4f &shadowCatcherPlane + , void **uniform lights + , const uniform uint32 numLights + , const uniform uint32 numGeoLights + , void *uniform areaPDF + ) { PathTracer *uniform self = (PathTracer *uniform)_self; @@ -396,6 +467,13 @@ export void PathTracer_set(void *uniform _self, self->minContribution = minContribution; self->maxRadiance = maxRadiance; self->backplate = (uniform Texture2D *uniform)backplate; + + uniform vec3f normal = make_vec3f(shadowCatcherPlane); + const uniform float l = length(normal); + self->shadowCatcher = l > 0.f; + const uniform float rl = rcp(l); + self->shadowCatcherPlane = make_vec4f(normal*rl, shadowCatcherPlane.w*rl); + self->lights = (const uniform Light *uniform *uniform)lights; self->numLights = numLights; self->numGeoLights = numGeoLights; @@ -408,7 +486,7 @@ export void* uniform PathTracer_create(void *uniform cppE) Renderer_Constructor(&self->super,cppE); self->super.renderTile = PathTracer_renderTile; - PathTracer_set(self, 20, 0.01f, inf, NULL, NULL, 0, 0, NULL); + PathTracer_set(self, 20, 0.01f, inf, NULL, make_vec4f(0.f), NULL, 0, 0, NULL); precomputeZOrder(); return self; diff --git a/ospray/render/pathtracer/bsdfs/Lambert.ih b/ospray/render/pathtracer/bsdfs/Lambert.ih index 71c0108f71..5bbfc1af4e 100644 --- a/ospray/render/pathtracer/bsdfs/Lambert.ih +++ b/ospray/render/pathtracer/bsdfs/Lambert.ih @@ -48,7 +48,8 @@ inline BSDF_SampleRes Lambert_sample(const varying BSDF* uniform super, return res; } -inline void Lambert_Constructor(varying Lambert* uniform self, const varying linear3f* uniform frame, +inline void Lambert_Constructor(varying Lambert* uniform self, + const varying linear3f* uniform frame, vec3f R) { BSDF_Constructor(&self->super, BSDF_DIFFUSE_REFLECTION, @@ -57,10 +58,12 @@ inline void Lambert_Constructor(varying Lambert* uniform self, const varying lin self->R = R; } -inline varying BSDF* uniform Lambert_create(uniform ShadingContext* uniform ctx, const varying linear3f* uniform frame, - vec3f R) +inline varying BSDF* uniform Lambert_create(uniform ShadingContext* uniform ctx, + const varying linear3f* uniform frame, + const varying vec3f &R) { - varying Lambert* uniform self = (varying Lambert* uniform)ShadingContext_alloc(ctx, sizeof(Lambert)); + varying Lambert* uniform self + = (varying Lambert* uniform)ShadingContext_alloc(ctx, sizeof(Lambert)); Lambert_Constructor(self, frame, R); return &self->super; } diff --git a/ospray/render/pathtracer/materials/Glass.cpp b/ospray/render/pathtracer/materials/Glass.cpp index 373b5c571d..df5c5b5d7a 100644 --- a/ospray/render/pathtracer/materials/Glass.cpp +++ b/ospray/render/pathtracer/materials/Glass.cpp @@ -23,10 +23,12 @@ namespace ospray { struct Glass : public ospray::Material { //! \brief common function to help printf-debugging /*! Every derived class should override this! */ - virtual std::string toString() const { return "ospray::pathtracer::Glass"; } + virtual std::string toString() const override + { return "ospray::pathtracer::Glass"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const float etaInside = getParamf("etaInside", getParamf("eta", 1.5f)); @@ -51,5 +53,6 @@ namespace ospray { }; OSP_REGISTER_MATERIAL(Glass,PathTracer_Glass); + OSP_REGISTER_MATERIAL(Glass,PathTracer_Dielectric); } } diff --git a/ospray/render/pathtracer/materials/Luminous.cpp b/ospray/render/pathtracer/materials/Luminous.cpp index 12168412bb..b5552a6641 100644 --- a/ospray/render/pathtracer/materials/Luminous.cpp +++ b/ospray/render/pathtracer/materials/Luminous.cpp @@ -19,7 +19,9 @@ namespace ospray { namespace pathtracer { - struct Luminous : public ospray::Material { + + struct Luminous : public ospray::Material + { Luminous() { ispcEquivalent = ispc::PathTracer_Luminous_create(); @@ -27,13 +29,14 @@ namespace ospray { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const + virtual std::string toString() const override { return "ospray::pathtracer::Luminous"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { const vec3f radiance = getParam3f("color", vec3f(1.f)) * getParam1f("intensity", 1.f); const float transparency = getParam1f("transparency", 0.f); diff --git a/ospray/render/pathtracer/materials/Matte.cpp b/ospray/render/pathtracer/materials/Matte.cpp index af9a75aa77..d5c06796e8 100644 --- a/ospray/render/pathtracer/materials/Matte.cpp +++ b/ospray/render/pathtracer/materials/Matte.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct Matte : public ospray::Material { + + struct Matte : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::Matte"; } + virtual std::string toString() const override + { return "ospray::pathtracer::Matte"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const vec3f reflectance = getParam3f("reflectance",vec3f(1.f)); diff --git a/ospray/render/pathtracer/materials/Metal.cpp b/ospray/render/pathtracer/materials/Metal.cpp index 7d8e9f698a..7fac75de10 100644 --- a/ospray/render/pathtracer/materials/Metal.cpp +++ b/ospray/render/pathtracer/materials/Metal.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct Metal : public ospray::Material { + + struct Metal : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::Metal"; } + virtual std::string toString() const override + { return "ospray::pathtracer::Metal"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const vec3f& reflectance diff --git a/ospray/render/pathtracer/materials/MetallicPaint.cpp b/ospray/render/pathtracer/materials/MetallicPaint.cpp index 3f6ac9f5d5..cd3b349985 100644 --- a/ospray/render/pathtracer/materials/MetallicPaint.cpp +++ b/ospray/render/pathtracer/materials/MetallicPaint.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct MetallicPaint : public ospray::Material { + + struct MetallicPaint : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::MetallicPaint"; } + virtual std::string toString() const override + { return "ospray::pathtracer::MetallicPaint"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const vec3f& shadeColor diff --git a/ospray/render/pathtracer/materials/MetallicPaint.ispc b/ospray/render/pathtracer/materials/MetallicPaint.ispc index d8f5b8507e..3af966f380 100644 --- a/ospray/render/pathtracer/materials/MetallicPaint.ispc +++ b/ospray/render/pathtracer/materials/MetallicPaint.ispc @@ -43,7 +43,10 @@ const varying BSDF* uniform MetallicPaint_getBSDF(const uniform PathTraceMateria varying LinearSpace3f* uniform shadingFrame = LinearSpace3f_create(ctx, frame(dg.Ns)); varying BSDF* uniform bsdf = MultiBSDF_create(ctx); - MultiBSDF_add(bsdf, Lambert_create(ctx, shadingFrame, self->shadeColor), luminance(self->shadeColor)); + const vec3f _shadeColor = self->shadeColor; + MultiBSDF_add(bsdf, + Lambert_create(ctx, shadingFrame, _shadeColor), + luminance(self->shadeColor)); if (self->glitterSpread != 0.0f & ne(self->glitterColor, make_vec3f(0.0f))) { diff --git a/ospray/render/pathtracer/materials/Mix.cpp b/ospray/render/pathtracer/materials/Mix.cpp index 6b08ba71bf..a7ed4e40ff 100644 --- a/ospray/render/pathtracer/materials/Mix.cpp +++ b/ospray/render/pathtracer/materials/Mix.cpp @@ -21,11 +21,14 @@ namespace ospray { namespace pathtracer { - struct MixMaterial : public ospray::Material { - virtual std::string toString() const { return "ospray::pathtracer::MixMaterial"; } + struct MixMaterial : public ospray::Material + { + virtual std::string toString() const override + { return "ospray::pathtracer::MixMaterial"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() == nullptr) ispcEquivalent = ispc::PathTracer_Mix_create(); diff --git a/ospray/render/pathtracer/materials/OBJ.cpp b/ospray/render/pathtracer/materials/OBJ.cpp index b4a77cd67d..3f5e5a6aff 100644 --- a/ospray/render/pathtracer/materials/OBJ.cpp +++ b/ospray/render/pathtracer/materials/OBJ.cpp @@ -21,13 +21,16 @@ namespace ospray { namespace pathtracer { - struct OBJMaterial : public ospray::Material { + struct OBJMaterial : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::OBJMaterial"; } + virtual std::string toString() const override + { return "ospray::pathtracer::OBJMaterial"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() == nullptr) ispcEquivalent = ispc::PathTracer_OBJ_create(); @@ -51,9 +54,9 @@ namespace ospray { const float color_total = reduce_max(Kd + Ks + Tf); if (color_total > 1.0) { - std::stringstream msg; - msg << "#osp:PT: warning: OBJ material produces energy (Kd + Ks + Tf = " << color_total << ", should be <= 1). Scaling down to 1." << std::endl; - postErrorMsg(msg); + postStatusMsg() << "#osp:PT: warning: OBJ material produces energy " + << "(Kd + Ks + Tf = " << color_total + << ", should be <= 1). Scaling down to 1."; Kd /= color_total; Ks /= color_total; Tf /= color_total; diff --git a/ospray/render/pathtracer/materials/Plastic.cpp b/ospray/render/pathtracer/materials/Plastic.cpp index b130756540..cef20784ab 100644 --- a/ospray/render/pathtracer/materials/Plastic.cpp +++ b/ospray/render/pathtracer/materials/Plastic.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct Plastic : public ospray::Material { + + struct Plastic : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::Plastic"; } + virtual std::string toString() const override + { return "ospray::pathtracer::Plastic"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const vec3f pigmentColor = getParam3f("pigmentColor",vec3f(1.f)); diff --git a/ospray/render/pathtracer/materials/Plastic.ispc b/ospray/render/pathtracer/materials/Plastic.ispc index eecf11077c..aa86f6024b 100644 --- a/ospray/render/pathtracer/materials/Plastic.ispc +++ b/ospray/render/pathtracer/materials/Plastic.ispc @@ -40,7 +40,8 @@ const varying BSDF* uniform Plastic_getBSDF(const uniform PathTraceMaterial* uni const uniform Plastic* uniform self = (const uniform Plastic* uniform)super; varying LinearSpace3f* uniform shadingFrame = LinearSpace3f_create(ctx, frame(dg.Ns)); - varying BSDF* uniform bsdf = Lambert_create(ctx, shadingFrame, self->pigmentColor); + const vec3f _pigmentColor = self->pigmentColor; + varying BSDF* uniform bsdf = Lambert_create(ctx, shadingFrame, _pigmentColor); if (self->roughness == 0.0f) bsdf = DielectricLayer_create(ctx, shadingFrame, bsdf, self->eta, make_vec3f(1.0f), 1.0f); diff --git a/ospray/render/pathtracer/materials/ThinGlass.cpp b/ospray/render/pathtracer/materials/ThinGlass.cpp index 0258d6bd4d..7c8674d4dd 100644 --- a/ospray/render/pathtracer/materials/ThinGlass.cpp +++ b/ospray/render/pathtracer/materials/ThinGlass.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct ThinGlass : public ospray::Material { + + struct ThinGlass : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::ThinGlass"; } + virtual std::string toString() const override + { return "ospray::pathtracer::ThinGlass"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; const vec3f& transmission diff --git a/ospray/render/pathtracer/materials/Velvet.cpp b/ospray/render/pathtracer/materials/Velvet.cpp index 1ee484c087..c2c303fc36 100644 --- a/ospray/render/pathtracer/materials/Velvet.cpp +++ b/ospray/render/pathtracer/materials/Velvet.cpp @@ -19,13 +19,17 @@ namespace ospray { namespace pathtracer { - struct Velvet : public ospray::Material { + + struct Velvet : public ospray::Material + { //! \brief common function to help printf-debugging /*! Every derived class should overrride this! */ - virtual std::string toString() const { return "ospray::pathtracer::Velvet"; } + virtual std::string toString() const override + { return "ospray::pathtracer::Velvet"; } //! \brief commit the material's parameters - virtual void commit() { + virtual void commit() override + { if (getIE() != nullptr) return; vec3f reflectance = getParam3f("reflectance", diff --git a/ospray/render/scivis/SciVisRenderer.cpp b/ospray/render/scivis/SciVisRenderer.cpp index 03d27e0e0f..c5a58cdba8 100644 --- a/ospray/render/scivis/SciVisRenderer.cpp +++ b/ospray/render/scivis/SciVisRenderer.cpp @@ -14,15 +14,12 @@ // limitations under the License. // // ======================================================================== // -// obj #include "SciVisRenderer.h" #include "SciVisMaterial.h" // ospray #include "common/Data.h" #include "lights/Light.h" #include "lights/AmbientLight.h" -//sys -#include // ispc exports #include "SciVisRenderer_ispc.h" diff --git a/ospray/render/scivis/SciVisRenderer.ih b/ospray/render/scivis/SciVisRenderer.ih new file mode 100644 index 0000000000..34fe72e7dd --- /dev/null +++ b/ospray/render/scivis/SciVisRenderer.ih @@ -0,0 +1,69 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "common/Model.ih" +#include "render/Renderer.ih" +#include "lights/Light.ih" + +#define ALPHA_THRESHOLD (.05f) + +struct SciVisRenderer +{ + Renderer super; + float volumeEpsilon; + + const uniform Light *uniform *uniform lights; + uint32 numLights; + bool oneSidedLighting; + + bool shadowsEnabled; + + int maxDepth; + + int aoSamples; + float aoDistance; + vec3f aoColor; + bool aoTransparencyEnabled; + + // ------------------------------------------------------------------ + // pre-computed state variables so we don't have to check certain + // stuff per pixel + // ------------------------------------------------------------------ + + /*! checks if all the values (aoSamples, aoDistance, aoColor) etc + require use of AO, and if not, skip AO computatoins */ + bool needToDoAO; +}; + +float lightAlpha(const uniform SciVisRenderer *uniform self, + Ray &ray, uniform Model *uniform model, + const float weight, + const varying float &rayOffset, + const varying vec3i &sampleID, + const uniform float quality); + +Volume * +SciVisRenderer_intersectVolumes(const uniform SciVisRenderer *uniform renderer, + varying Ray &ray, + const varying float &rayOffset); + +vec4f SciVisRenderer_computeVolumeInterval(const SciVisRenderer *uniform renderer, + Volume *uniform volume, + varying Ray &ray, float tBegin, + float tEnd, float maxOpacity, bool isShadowRay, const varying float &rayOffset, const varying vec3i &sampleID, uniform float quality); + diff --git a/ospray/render/scivis/SciVisRenderer.ispc b/ospray/render/scivis/SciVisRenderer.ispc index a45964ee60..4699f7674d 100644 --- a/ospray/render/scivis/SciVisRenderer.ispc +++ b/ospray/render/scivis/SciVisRenderer.ispc @@ -14,67 +14,21 @@ // limitations under the License. // // ======================================================================== // +#include "SciVisRenderer.ih" #include "fb/FrameBuffer.ih" #include "render/util.ih" -#include "common/Model.ih" #include "texture/Texture2D.ih" -#include "lights/Light.ih" -#include "render/Renderer.ih" #include "math/random.ih" #include "math/sampling.ih" #include "math/LinearSpace.ih" - +#include "surfaceShading.ih" #include "SciVisMaterial.ih" -#define ALPHA_THRESHOLD (.05f) - // Data types ///////////////////////////////////////////////////////////////// -struct SciVisRenderer -{ - Renderer super; - float volumeEpsilon; - - const uniform Light *uniform *uniform lights; - uint32 numLights; - bool oneSidedLighting; - - bool shadowsEnabled; - - int maxDepth; - - int aoSamples; - float aoDistance; - vec3f aoColor; - bool aoTransparencyEnabled; -}; - -struct SciVisShadingInfo -{ - float d; - vec3f Kd; - vec3f Ks; - float Ns; - vec3f shadingNormal; - - float local_opacity; -}; - -inline float lightAlpha(const uniform SciVisRenderer *uniform self, - Ray &ray, uniform Model *uniform model, - const float weight, - const varying float &rayOffset, - const varying vec3i &sampleID, - const uniform float quality); - -inline Volume * -SciVisRenderer_intersectVolumes(const uniform SciVisRenderer *uniform renderer, - varying Ray &ray, - const varying float &rayOffset); - // Function definitions /////////////////////////////////////////////////////// -inline void initShadingInfo(varying SciVisShadingInfo &info) +void initShadingInfo(varying SciVisShadingInfo &info) { info.d = 1.f; info.Ns = 0.f; @@ -86,11 +40,11 @@ inline void initShadingInfo(varying SciVisShadingInfo &info) // Material functions // -inline void shadeMaterials(const varying DifferentialGeometry &dg, - varying SciVisShadingInfo &info) +void shadeMaterials(const varying DifferentialGeometry &dg, + varying SciVisShadingInfo &info) { uniform SciVisMaterial *scivisMaterial = - (uniform SciVisMaterial *)dg.material; + (uniform SciVisMaterial *)dg.material; info.shadingNormal = dg.Ns; @@ -127,351 +81,9 @@ inline void shadeMaterials(const varying DifferentialGeometry &dg, info.Ks = info.Ks * ((info.Ns + 2.f) * one_over_two_pi); } -// AO functions // - -inline float calculateAO(const uniform SciVisRenderer *uniform self, - const varying vec3i &sampleID, - const varying DifferentialGeometry &dg, - const varying vec3f &shadingNormal) -{ - const int accumID = sampleID.z; - const int ix = sampleID.x; - const int iy = sampleID.y; - - // init TEA RNG // - RandomTEA rng_state; - varying RandomTEA* const uniform rng = &rng_state; - RandomTEA__Constructor(rng, (self->super.fb->size.x * iy) + ix, accumID); - - float occlusion = 0.f; - const linear3f localToWorld = frame(shadingNormal); - - for (uniform int i = 0; i < self->aoSamples; i++) { - const vec2f s = RandomTEA__getFloats(rng); - const vec3f local_ao_dir = cosineSampleHemisphere(s); - const vec3f ao_dir = localToWorld * local_ao_dir; - - if (dot(ao_dir, dg.Ns) < 0.05f) { // check below surface - occlusion += 1.f; - continue; - } - - Ray ao_ray; - setRay(ao_ray, dg.P + (self->super.epsilon * dg.Ns), ao_dir, - self->super.epsilon, self->aoDistance); - - if (self->aoTransparencyEnabled) { - const float rayOffset = self->super.epsilon*(1.f + s.x); - occlusion += (1.f - lightAlpha(self, ao_ray, self->super.model, 1.0f, - rayOffset, sampleID, 0.2f)); - } else if (isOccluded(self->super.model, ao_ray)) - occlusion += 1.f; - } - - // the cosTheta of cosineSampleHemispherePDF and dot(shadingNormal, ao_dir) cancel - return 1.0f - occlusion/self->aoSamples; -} - -inline void shadeAO(const uniform SciVisRenderer *uniform self, - const varying vec3i &sampleID, - const varying DifferentialGeometry &dg, - const varying SciVisShadingInfo &info, - varying vec3f &color) -{ - // Calculate AO contribution as ambient term - float ao = 1.0f; - if (self->aoSamples > 0 && reduce_max(self->aoColor) > 0.f) { - if (self->aoDistance > 0.f) - ao *= calculateAO(self, sampleID, dg, info.shadingNormal); - // Blend AO w/ diffuse term - } - color = color + (info.local_opacity * ao) * info.Kd * self->aoColor; -} - -inline void shadeLights(const uniform SciVisRenderer *uniform self, - const varying Ray &ray, - const varying DifferentialGeometry &dg, - const varying SciVisShadingInfo &info, - varying vec3f &color, - const varying float &rayOffset, - const varying vec3i &sampleID, - const uniform float quality) -{ - const vec3f R = ray.dir - ((2.f * dot(ray.dir, info.shadingNormal)) * info.shadingNormal); - const vec3f P = dg.P + self->super.epsilon * dg.Ng; - - //calculate shading for all lights - for (uniform int i = 0; self->lights && i < self->numLights; i++) { - const uniform Light *uniform l = self->lights[i]; - const vec2f s = make_vec2f(0.0f); // sample center of area lights - const Light_SampleRes light = l->sample(l, dg, s); - - if (reduce_max(light.weight) > 0.f) { // any potential contribution? - float cosNL = dot(light.dir, info.shadingNormal); - if (self->oneSidedLighting) { - if (cosNL < 0.0f) - continue; - } - else - cosNL = abs(cosNL); - const float cosLR = max(0.f, dot(light.dir, R)); - vec3f specular = info.Ks * powf(cosLR, info.Ns); - const vec3f brdf = info.Kd * cosNL + specular; - const vec3f light_contrib = info.local_opacity - * brdf * light.weight; - - if (self->shadowsEnabled) - { - const float max_contrib = reduce_max(light_contrib); - if (max_contrib > .01f) { - Ray shadowRay; - setRay(shadowRay, P, light.dir); - const float light_alpha = lightAlpha(self, shadowRay, - self->super.model, - max_contrib, - rayOffset, sampleID, quality); - color = color + light_alpha * light_contrib; - } - } else { - color = color + light_contrib; - } - } - } -} - -// Volume shading // - -inline vec4f SciVisRenderer_computeVolumeSample(SciVisRenderer *uniform renderer, - Volume *uniform volume, - varying Ray &ray, const varying float &rayOffset, - const varying vec3i &sampleID) -{ - // Sample the volume at the hit point in world coordinates. - const vec3f coordinates = ray.org + ray.t0 * ray.dir; - const float sample = volume->computeSample(volume, coordinates); - - // Look up the color associated with the volume sample. - vec3f sampleColor = - volume->transferFunction->getColorForValue(volume->transferFunction, - sample); - - // Look up the opacity associated with the volume sample. - const float sampleOpacity = - volume->transferFunction->getOpacityForValue(volume->transferFunction, - sample); - - // Compute gradient shading, if enabled. - if(volume->gradientShadingEnabled) { - - // Use volume gradient as the normal. - const vec3f gradient = - safe_normalize(volume->computeGradient(volume, coordinates)); - - // Setup differential geometry for the volume sample point. - DifferentialGeometry dg; - dg.P = coordinates; - dg.Ns = gradient; - - // Compute lighting. - vec3f shadedColor = make_vec3f(0.f); - const vec2f s = make_vec2f(0.0f); // sample center of area lights - - for (uniform uint32 i=0; inumLights; i++) { - const Light_SampleRes light = - renderer->lights[i]->sample(renderer->lights[i], dg, s); - const float cosNL = (gradient.x == 0.f - && gradient.y == 0.f - && gradient.z == 0.f) ? - 1.f : abs(dot(safe_normalize(light.dir), - gradient)); - - shadedColor = shadedColor + sampleColor * cosNL * light.weight * one_over_pi /*BRDF normalization to match surface shading*/; - } - - //sampleColor = shadedColor; - - dg.color = make_vec4f(sampleColor, 1); - dg.Ng = dg.Ns; - SciVisShadingInfo info; - initShadingInfo(info); - - //hardcode volume spec for now - info.Ks = make_vec3f(.3f,.3f,.3f); - info.Ns = 20.0f; - info.local_opacity=1.f; - dg.material = 0; - shadeMaterials(dg, info); - - vec3f litColor = make_vec3f(0.0f); - info.Ns *= 0.3f; - shadeLights(renderer, ray, dg, info, litColor, rayOffset, sampleID,0.5f); - sampleColor = litColor; - } - - // return the color contribution for this sample only (do not accumulate) - return clamp(sampleOpacity / volume->samplingRate) - * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); -} - -// Volume shading // -// quality is from 0 - 1. 1 being best. -vec4f SciVisRenderer_computeVolumeInterval(const SciVisRenderer *uniform renderer, - Volume *uniform volume, - varying Ray &ray, float tBegin, - float tEnd, float maxOpacity, bool isShadowRay, const varying float &rayOffset, const varying vec3i &sampleID, uniform float quality) -{ - vec3f singleCoordinates; - float singleMax = -1.f; - float singleMin = 0.01f; - ray.t0 = tBegin; - float tSkipped = -1; //for adaptive, skip adapting sampling rate up to this value - vec4f intervalColor = make_vec4f(0.f); - volume->intersect(volume, ray); - // const uniform float oneOverQuality = (1.f/quality); - float samplingRate = volume->samplingRate*quality; - float lastSample = -1.f; - const float adaptiveScalar = volume->adaptiveScalar*quality; - const float maxSamplingRate = volume->adaptiveMaxSamplingRate*quality; //2.f nyquist frequency - const float adaptiveBacktrack = volume->adaptiveBacktrack; - while (ray.t0 < tEnd && intervalColor.w < maxOpacity) - { - // Sample the volume at the hit point in world coordinates. - const vec3f coordinates = ray.org + ray.t0 * ray.dir; - const float sample = volume->computeSample(volume, coordinates); - if (lastSample == -1.f) - lastSample = sample; - - // Look up the color associated with the volume sample. - // Look up the opacity associated with the volume sample. - vec3f sampleColor; - float sampleOpacity; - sampleOpacity = volume->transferFunction->getIntegratedOpacityForValue(volume->transferFunction, - lastSample, sample); - if (volume->adaptiveSampling && sampleOpacity > adaptiveBacktrack && ray.t0 > tSkipped) // adaptive backtack - { - //adaptively refine sampling rate - const float newSamplingRate = min(adaptiveScalar*sampleOpacity*quality,maxSamplingRate); - if (newSamplingRate > samplingRate + 0.01f) - { - samplingRate = newSamplingRate; - const float ot0 = ray.t0; - ray.t0 = max(max(ray.t0-max(ray.time,volume->samplingStep),tSkipped), tBegin); - //Carson: TODO: skipping past ray.time shouldnt be necessary and avoided - //but currently greatly reduces artifacts from low to high opacity - //regions. issue with space skipping? - tSkipped = ot0; - volume->intersectAdaptive(volume, ray, samplingRate); - lastSample = -1; - continue; - } - tSkipped = ray.t0; - } - - if (!isShadowRay) - { - sampleColor = volume->transferFunction->getIntegratedColorForValue(volume->transferFunction, lastSample, sample); - lastSample = sample; - - // Compute gradient shading, if enabled. - if(volume->gradientShadingEnabled) - { - if (volume->singleShade && sampleOpacity*(1.0f - intervalColor.w) > singleMax && sampleOpacity > singleMin) - { - singleMax = sampleOpacity*(1.0f - intervalColor.w); - singleCoordinates = coordinates; - } - else if (!volume->singleShade) - { - // Use volume gradient as the normal. - const vec3f gradient = - safe_normalize(volume->computeGradient(volume, coordinates)); - - // Setup differential geometry for the volume sample point. - DifferentialGeometry dg; - dg.Ng = gradient; - if (dot(ray.dir,dg.Ng) >= 0.f) dg.Ng = neg(dg.Ng); //face forward - dg.Ns = dg.Ng; - dg.P = coordinates + dg.Ns*renderer->volumeEpsilon; - - dg.color = make_vec4f(sampleColor, 1); - dg.Ng = dg.Ns; - SciVisShadingInfo info; - initShadingInfo(info); - - //hardcode volume spec for now - info.Ks = volume->specular*sampleOpacity; - info.Ns = 20.0f; - info.local_opacity=sampleOpacity; - dg.material = 0; - shadeMaterials(dg, info); - - vec3f litColor = make_vec3f(0.0f); - info.Ns *= 0.3f; - shadeLights(renderer, ray, dg, info, litColor, rayOffset, sampleID, quality*0.5f); - sampleColor = litColor; - } - } - } - if (volume->adaptiveSampling) - { - // ray.time = absolute step length. convert to effective sampling rate - float dt = 1.0f/((ray.time)/volume->samplingStep); - vec4f contribution = clamp(sampleOpacity / dt) - * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); - intervalColor = intervalColor + (1.0f - intervalColor.w) * contribution; - //adaptively refine sampling rate - if (ray.t0 > tSkipped) - samplingRate = max(min(adaptiveScalar*sampleOpacity*quality,maxSamplingRate)/*nyquist frequency*/,volume->samplingRate*quality); - volume->intersectAdaptive(volume, ray, samplingRate); - } - else - { - vec4f contribution = clamp(sampleOpacity / volume->samplingRate) - * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); - intervalColor = intervalColor + (1.0f - intervalColor.w) * contribution; - volume->intersect(volume, ray); - } - } - if(!isShadowRay && volume->gradientShadingEnabled && volume->singleShade && singleMax >= singleMin) - { - // Use volume gradient as the normal. - const vec3f gradient = - safe_normalize(volume->computeGradient(volume, singleCoordinates)); - - // Setup differential geometry for the volume sample point. - DifferentialGeometry dg; - dg.Ng = gradient; - if (dot(ray.dir,dg.Ng) >= 0.f) dg.Ng = neg(dg.Ng); //face forward - dg.Ns = dg.Ng; - dg.P = singleCoordinates + dg.Ns*renderer->volumeEpsilon; - - // Compute lighting. - vec3f shadedColor = make_vec3f(0.f); - - dg.color = intervalColor; - SciVisShadingInfo info; - initShadingInfo(info); - - //hardcode volume spec for now - info.Ks = volume->specular*singleMax; - info.Ns = 30.0f; - info.local_opacity=intervalColor.w; - dg.material = 0; - shadeMaterials(dg, info); - - vec3f litColor = make_vec3f(0.0f); - shadeAO(renderer, sampleID, dg, info, litColor); - info.Ns *= 0.3f; - shadeLights(renderer, ray, dg, info, litColor, rayOffset, sampleID, quality*0.5f); - intervalColor = make_vec4f(litColor,intervalColor.w); - } - return intervalColor; -} - -inline vec4f SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, - const varying vec3i &sampleID, - varying Ray &ray, const varying float &rayOffset) + const varying vec3i &sampleID, + varying Ray &ray, const varying float &rayOffset) { vec3f color = make_vec3f(0.f); float path_opacity = 1.f; @@ -511,7 +123,7 @@ vec4f SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, if (info.local_opacity > 0.01f) { // worth shading? shadeAO(self, sampleID, dg, info, color); - shadeLights(self, ray, dg, info, color, rayOffset,sampleID, 0.5f); + integrateOverLights(self, ray, dg, info, color, rayOffset,sampleID, 0.5f); } // Kill path when reached max depth or if remaining contribution too low @@ -531,146 +143,6 @@ vec4f SciVisRenderer_computeGeometrySample(SciVisRenderer *uniform self, } } -/*! Returns the first hit volume for the provided ray and sets the ray bounds - * t0 and t, considering the provided ray offset and any clipping. If no - * volume is found, the returned volume is NULL and ray.t0 will be set to - * infinity. - */ -inline Volume * -SciVisRenderer_intersectVolumes(const uniform SciVisRenderer *uniform renderer, - varying Ray &ray, - const varying float &rayOffset) -{ - // The first intersected volume. - Volume * volume = NULL; - - // The ray with bounds for the first intersected volume. - Ray volumeRay = ray; - volumeRay.t0 = infinity; - - // Test each volume and find the first intersection. - for (uniform int32 i=0; isuper.model->volumeCount; i++) { - Volume *uniform volume_i = renderer->super.model->volumes[i]; - - // Intersect volume bounding box. - float t0, t1; - intersectBox(ray, volume_i->boundingBox, t0, t1); - - // Clip against volume clipping box (if specified). - if(ne(volume_i->volumeClippingBox.lower, - volume_i->volumeClippingBox.upper)) { - float tClip0, tClip1; - intersectBox(ray, volume_i->volumeClippingBox, tClip0, tClip1); - - t0 = max(t0, tClip0); - t1 = min(t1, tClip1); - } - - // Update intersected volume. - if (t0 < t1 && t0 < volumeRay.t0) { - volumeRay.t0 = t0; - volumeRay.t = t1; - volume = volume_i; - } - } - - // Update the provided ray. - ray = volumeRay; - - // If we intersected a volume, offset ray by a fraction of the nominal ray - // step. - if (volume) - ray.t0 += rayOffset * volume->samplingStep * rcpf(volume->samplingRate); - - // Return the first intersected volume. - return volume; -} - -// Lighting functions // - -inline float lightAlpha(const uniform SciVisRenderer *uniform self, - Ray &ray, uniform Model *uniform model, - const float weight, - const varying float &rayOffset, - const varying vec3i &sampleID, - const uniform float quality) -{ - float alpha = 1.f; - const float org_t_max = ray.t; - uniform int remaining_depth = self->maxDepth; - - while (1) { - // Get first intersected volume for each ray and set the ray bounds. - float org_t = ray.t; - float org_t0 = ray.t0; - Volume *volume = SciVisRenderer_intersectVolumes(self, ray, rayOffset); - float volumeT = ray.t; - float volumeT0 = ray.t0; - ray.t = org_t; - ray.t0 = org_t0; - - traceRay(model,ray); - - if (!(volumeT0 <= 0.f || volumeT0 <= ray.t)) - volume = NULL; - - if (!volume && ray.geomID < 0) return alpha; - - float material_opacity = 1.f; - - if (!volume) - { - DifferentialGeometry dg; - postIntersect(model, dg, ray, DG_MATERIALID | DG_TEXCOORD | DG_COLOR); - - uniform SciVisMaterial *scivisMaterial = - (uniform SciVisMaterial *)dg.material; - - if(scivisMaterial == NULL) { - material_opacity = dg.color.w; - } else { - foreach_unique( mat in scivisMaterial ) { - material_opacity = mat->d * get1f(mat->map_d, dg.st, 1.f); - if (mat->map_Kd) { - vec4f Kd_from_map = get4f(mat->map_Kd,dg.st); - material_opacity *= Kd_from_map.w; - } - } - } - } - else - { - ray.t = org_t; - ray.t0 = org_t0; - // print("found volume intersection!\n"); - // Provide ray offset for use with isosurface geometries (this value - // ignored elsewhere). - ray.time = -rayOffset * volume->samplingStep; - ray.t = volumeT; - float tBegin = max(volumeT0,0.f); - float tEnd = volumeT+self->volumeEpsilon; - foreach_unique (v in volume) - { - vec4f volumeColor = SciVisRenderer_computeVolumeInterval(self, v, ray, tBegin, tEnd, 0.98f, 1, rayOffset, sampleID, quality); - material_opacity = volumeColor.w; - } - } - - alpha = alpha * (1.f - material_opacity); - - if (alpha * weight < ALPHA_THRESHOLD) return alpha; - - if (--remaining_depth <= 0) - return alpha; - - ray.t0 = ray.t + rayOffset; - ray.t = org_t_max; - ray.primID = -1; - ray.geomID = -1; - ray.instID = -1; - } -} - /*! This function intersects the volume and geometries. */ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, varying Ray &ray, @@ -699,7 +171,7 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, // Initial trace through geometries. vec4f geometryColor = SciVisRenderer_computeGeometrySample(renderer, - sampleID, geometryRay, rayOffset); + sampleID, geometryRay, rayOffset); // Depth is the first volume bounding box or geometry hit depth = min(ray.t0, geometryRay.t); @@ -735,14 +207,10 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, float tBegin = ray.t0; float tEnd = min(geometryRay.t,ray.t); foreach_unique (v in volume) - { - //volumeColor = SciVisRenderer_computeVolumeSample(renderer, v, ray); - // Advance the ray for the next sample. - //volume->intersect(v, ray); - - //interval sampling - volumeColor = SciVisRenderer_computeVolumeInterval(renderer, v, ray, tBegin, tEnd, .98f - color.w, 0, rayOffset, sampleID, 1.f); - } + { + //interval sampling + volumeColor = SciVisRenderer_computeVolumeInterval(renderer, v, ray, tBegin, tEnd, .99f, 0, rayOffset, sampleID, 1.f); + } // Volume contribution. color = color + (1.0f - color.w) * volumeColor; @@ -768,7 +236,7 @@ void SciVisRenderer_intersect(uniform SciVisRenderer *uniform renderer, // Trace next geometry ray. geometryColor = SciVisRenderer_computeGeometrySample(renderer, - sampleID, geometryRay, rayOffset); + sampleID, geometryRay, rayOffset); } } @@ -800,9 +268,7 @@ void SciVisRenderer_renderSample(uniform Renderer *uniform _self, sample.sampleID, color, depth); // blend with background - if (renderer->super.backgroundEnabled) { - color = color + (1.0f - color.w) * make_vec4f(renderer->super.bgColor, 0.f); - } + color = color + (1.0f - color.w) * renderer->super.bgColor; // Store the result in the sample. sample.rgb = make_vec3f(color); @@ -860,6 +326,13 @@ export void SciVisRenderer_set(void *uniform _self, self->lights = (const uniform Light *uniform *uniform)lights; self->numLights = numLights; self->oneSidedLighting = oneSidedLighting; + + + // pre-computations: + self->needToDoAO + = (self->aoSamples > 0) + && (reduce_max(self->aoColor) > 0.f) + && (self->aoDistance > 0.f); } export void *uniform SciVisRenderer_create(void *uniform cppE) diff --git a/ospray/render/scivis/lightAlpha.ispc b/ospray/render/scivis/lightAlpha.ispc new file mode 100644 index 0000000000..1e91d8fe7c --- /dev/null +++ b/ospray/render/scivis/lightAlpha.ispc @@ -0,0 +1,107 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "SciVisRenderer.ih" +#include "fb/FrameBuffer.ih" +#include "render/util.ih" +#include "texture/Texture2D.ih" +#include "math/random.ih" +#include "math/sampling.ih" +#include "math/LinearSpace.ih" + +#include "SciVisMaterial.ih" + + +// Lighting functions // +float lightAlpha(const uniform SciVisRenderer *uniform self, + Ray &ray, uniform Model *uniform model, + const float weight, + const varying float &rayOffset, + const varying vec3i &sampleID, + const uniform float quality) +{ + float alpha = 1.f; + const float org_t_max = ray.t; + uniform int remaining_depth = self->maxDepth; + + while (1) { + traceRay(model,ray); + + if (ray.geomID >= 0) { // surfaces + DifferentialGeometry dg; + postIntersect(model, dg, ray, DG_MATERIALID | DG_TEXCOORD | DG_COLOR); + + uniform SciVisMaterial *scivisMaterial = + (uniform SciVisMaterial *)dg.material; + + float material_opacity = 1.f; + + if(scivisMaterial == NULL) { + material_opacity = dg.color.w; + } else { + foreach_unique( mat in scivisMaterial ) { + material_opacity = mat->d * get1f(mat->map_d, dg.st, 1.f); + if (hasAlpha(mat->map_Kd)) { + vec4f Kd_from_map = get4f(mat->map_Kd,dg.st); + material_opacity *= Kd_from_map.w; + } + } + } + + alpha = alpha * (1.f - material_opacity); + if (alpha * weight < ALPHA_THRESHOLD) + return 0.f; + } + + // volumes + // Get first intersected volume for each ray and set the ray bounds. + float org_t = ray.t; + float org_t0 = ray.t0; + Volume *volume = SciVisRenderer_intersectVolumes(self, ray, rayOffset); + float tBegin = max(org_t0, ray.t0); + float tEnd = min(org_t, ray.t + self->volumeEpsilon); + + if (volume && tBegin < tEnd) { + // print("found volume intersection!\n"); + // Provide ray offset for use with isosurface geometries (this value + // ignored elsewhere). + ray.time = -rayOffset * volume->samplingStep; + float material_opacity = 1.f; + foreach_unique (v in volume) { + vec4f volumeColor = SciVisRenderer_computeVolumeInterval(self, v, ray, tBegin, tEnd, 0.98f, true, rayOffset, sampleID, quality); + material_opacity = volumeColor.w; + } + + alpha = alpha * (1.f - material_opacity); + + if (alpha * weight < ALPHA_THRESHOLD) + return 0.f; + } + + + if (!volume && ray.geomID < 0) + return alpha; + if (--remaining_depth <= 0) + return alpha; + + ray.t0 = org_t + self->super.epsilon; + ray.t = org_t_max; + ray.primID = -1; + ray.geomID = -1; + ray.instID = -1; + } +} + diff --git a/ospray/render/scivis/surfaceShading.ih b/ospray/render/scivis/surfaceShading.ih new file mode 100644 index 0000000000..6b3af2040f --- /dev/null +++ b/ospray/render/scivis/surfaceShading.ih @@ -0,0 +1,65 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#pragma once + +#include "SciVisRenderer.ih" + +/*! a 'shadinginfo' seems to be a local (phong) shading context; it's + also used during volume shading, but otherwise - at least, together + with a DG - realizes the concept of a local diffential surface: it + has position (in DG), normal, and material parameters for a phong + lobe */ +struct SciVisShadingInfo +{ + float d; + vec3f Kd; + vec3f Ks; + float Ns; + vec3f shadingNormal; + + float local_opacity; +}; +void initShadingInfo(varying SciVisShadingInfo &info); +void shadeMaterials(const varying DifferentialGeometry &dg, + varying SciVisShadingInfo &info); + + +void shadeAO(const uniform SciVisRenderer *uniform self, + const varying vec3i &sampleID, + const varying DifferentialGeometry &dg, + const varying SciVisShadingInfo &info, + varying vec3f &color); + +/*! iterate over all light sources, and, for each, compute incident + irradiance (including shadowing, if required), shade with BRDF, + and accumulate in 'color' (?) */ +void integrateOverLights(const uniform SciVisRenderer *uniform self, + const varying Ray &ray, + const varying DifferentialGeometry &dg, + const varying SciVisShadingInfo &info, + varying vec3f &color, + const varying float &rayOffset, + const varying vec3i &sampleID, + const uniform float quality); + +/*! compute ambient-occlusoin term, using the materials' ao color + field. will internally check if ao is actually enabled or not, + and, if not, just skip these computatoins */ +float calculateAO(const uniform SciVisRenderer *uniform self, + const varying vec3i &sampleID, + const varying DifferentialGeometry &dg, + const varying vec3f &shadingNormal); diff --git a/ospray/render/scivis/surfaceShading.ispc b/ospray/render/scivis/surfaceShading.ispc new file mode 100644 index 0000000000..7940acf688 --- /dev/null +++ b/ospray/render/scivis/surfaceShading.ispc @@ -0,0 +1,128 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "surfaceShading.ih" +#include "math/random.ih" +#include "math/sampling.ih" + +// AO functions // + +float calculateAO(const uniform SciVisRenderer *uniform self, + const varying vec3i &sampleID, + const varying DifferentialGeometry &dg, + const varying vec3f &shadingNormal) +{ + const int accumID = sampleID.z; + const int ix = sampleID.x; + const int iy = sampleID.y; + + // init TEA RNG // + RandomTEA rng_state; + varying RandomTEA* const uniform rng = &rng_state; + RandomTEA__Constructor(rng, (self->super.fb->size.x * iy) + ix, accumID); + + float occlusion = 0.f; + const linear3f localToWorld = frame(shadingNormal); + + for (uniform int i = 0; i < self->aoSamples; i++) { + const vec2f s = RandomTEA__getFloats(rng); + const vec3f local_ao_dir = cosineSampleHemisphere(s); + const vec3f ao_dir = localToWorld * local_ao_dir; + + if (dot(ao_dir, dg.Ns) < 0.05f) { // check below surface + occlusion += 1.f; + continue; + } + + Ray ao_ray; + setRay(ao_ray, dg.P + (self->super.epsilon * dg.Ns), ao_dir, + self->super.epsilon, self->aoDistance); + + if (self->aoTransparencyEnabled) { + const float rayOffset = self->super.epsilon*(1.f + s.x); + occlusion += (1.f - lightAlpha(self, ao_ray, self->super.model, 1.0f, + rayOffset, sampleID, 0.2f)); + } else if (isOccluded(self->super.model, ao_ray)) + occlusion += 1.f; + } + + // the cosTheta of cosineSampleHemispherePDF and dot(shadingNormal, ao_dir) cancel + return 1.0f - occlusion/self->aoSamples; +} + + +void shadeAO(const uniform SciVisRenderer *uniform self, + const varying vec3i &sampleID, + const varying DifferentialGeometry &dg, + const varying SciVisShadingInfo &info, + varying vec3f &color) +{ + // Calculate AO contribution as ambient term + float ao = 1.0f; + if (self->needToDoAO) + ao = calculateAO(self, sampleID, dg, info.shadingNormal); + + color = color + (info.local_opacity * ao) * info.Kd * self->aoColor; +} + +void integrateOverLights(const uniform SciVisRenderer *uniform self, + const varying Ray &ray, + const varying DifferentialGeometry &dg, + const varying SciVisShadingInfo &info, + varying vec3f &color, + const varying float &rayOffset, + const varying vec3i &sampleID, + const uniform float quality) +{ + const vec3f R = ray.dir - ((2.f * dot(ray.dir, info.shadingNormal)) * info.shadingNormal); + const vec3f P = dg.P + self->super.epsilon * dg.Ng; + + //calculate shading for all lights + for (uniform int i = 0; self->lights && i < self->numLights; i++) { + const uniform Light *uniform l = self->lights[i]; + const vec2f s = make_vec2f(0.0f); // sample center of area lights + const Light_SampleRes light = l->sample(l, dg, s); + + if (reduce_max(light.weight) > 0.f) { // any potential contribution? + float cosNL = dot(light.dir, info.shadingNormal); + if (self->oneSidedLighting) { + if (cosNL < 0.0f) + continue; + } else + cosNL = abs(cosNL); + const float cosLR = max(0.f, dot(light.dir, R)); + vec3f specular = info.Ks * powf(cosLR, info.Ns); + const vec3f brdf = info.Kd * cosNL + specular; + const vec3f light_contrib = info.local_opacity * brdf * light.weight; + + if (self->shadowsEnabled) { + const float max_contrib = reduce_max(light_contrib); + if (max_contrib > .01f) { + Ray shadowRay; + setRay(shadowRay, P, light.dir); + const float light_alpha = lightAlpha(self, shadowRay, + self->super.model, + max_contrib, + rayOffset, sampleID, quality); + color = color + light_alpha * light_contrib; + } + } else { + color = color + light_contrib; + } + } + } +} + diff --git a/ospray/render/scivis/volumeIntegration.ispc b/ospray/render/scivis/volumeIntegration.ispc new file mode 100644 index 0000000000..2b25b0aadb --- /dev/null +++ b/ospray/render/scivis/volumeIntegration.ispc @@ -0,0 +1,313 @@ +// ======================================================================== // +// Copyright 2009-2017 Intel Corporation // +// // +// Licensed under the Apache License, Version 2.0 (the "License"); // +// you may not use this file except in compliance with the License. // +// You may obtain a copy of the License at // +// // +// http://www.apache.org/licenses/LICENSE-2.0 // +// // +// Unless required by applicable law or agreed to in writing, software // +// distributed under the License is distributed on an "AS IS" BASIS, // +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // +// See the License for the specific language governing permissions and // +// limitations under the License. // +// ======================================================================== // + +#include "SciVisRenderer.ih" +// #include "fb/FrameBuffer.ih" +// #include "render/util.ih" +// #include "texture/Texture2D.ih" +// #include "math/random.ih" +// #include "math/sampling.ih" +// #include "math/LinearSpace.ih" +// #include "surfaceShading.ih" +// #include "SciVisMaterial.ih" +#include "surfaceShading.ih" + +vec4f SciVisRenderer_computeVolumeSample(SciVisRenderer *uniform renderer, + Volume *uniform volume, + varying Ray &ray, const varying float &rayOffset, + const varying vec3i &sampleID) +{ + // Sample the volume at the hit point in world coordinates. + const vec3f coordinates = ray.org + ray.t0 * ray.dir; + const float sample = volume->computeSample(volume, coordinates); + + // Look up the color associated with the volume sample. + vec3f sampleColor = + volume->transferFunction->getColorForValue(volume->transferFunction, + sample); + + // Look up the opacity associated with the volume sample. + const float sampleOpacity = + volume->transferFunction->getOpacityForValue(volume->transferFunction, + sample); + + // Compute gradient shading, if enabled. + if(volume->gradientShadingEnabled) { + + // Use volume gradient as the normal. + const vec3f gradient = + safe_normalize(volume->computeGradient(volume, coordinates)); + + // Setup differential geometry for the volume sample point. + DifferentialGeometry dg; + dg.P = coordinates; + dg.Ns = gradient; + + // Compute lighting. + vec3f shadedColor = make_vec3f(0.f); + const vec2f s = make_vec2f(0.0f); // sample center of area lights + + for (uniform uint32 i=0; inumLights; i++) { + const Light_SampleRes light = + renderer->lights[i]->sample(renderer->lights[i], dg, s); + const float cosNL = (gradient.x == 0.f + && gradient.y == 0.f + && gradient.z == 0.f) ? + 1.f : abs(dot(safe_normalize(light.dir), + gradient)); + + shadedColor = shadedColor + sampleColor * cosNL * light.weight * one_over_pi /*BRDF normalization to match surface shading*/; + } + + dg.color = make_vec4f(sampleColor, 1); + dg.Ng = dg.Ns; + SciVisShadingInfo info; + initShadingInfo(info); + + //hardcode volume spec for now + info.Ks = make_vec3f(.3f,.3f,.3f); + info.Ns = 20.0f; + info.local_opacity=1.f; + dg.material = 0; + shadeMaterials(dg, info); + + vec3f litColor = make_vec3f(0.0f); + info.Ns *= 0.3f; + integrateOverLights(renderer, ray, dg, info, litColor, rayOffset, sampleID,0.5f); + sampleColor = litColor; + } + + // return the color contribution for this sample only (do not accumulate) + return clamp(sampleOpacity / volume->samplingRate) + * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); +} + +// Volume shading // +// quality is from 0 - 1. 1 being best. +vec4f SciVisRenderer_computeVolumeInterval(const SciVisRenderer *uniform renderer, + Volume *uniform volume, + varying Ray &ray, float tBegin, + float tEnd, float maxOpacity, bool isShadowRay, const varying float &rayOffset, const varying vec3i &sampleID, uniform float quality) +{ + vec3f singleCoordinates; + float singleMax = -1.f; + float singleMin = 0.01f; + float samplingRate = volume->samplingRate*quality; + float lastSample = -1.f; + const float adaptiveScalar = volume->adaptiveScalar*quality; + const float maxSamplingRate = volume->adaptiveMaxSamplingRate*quality; //2.f nyquist frequency + const float adaptiveBacktrack = volume->adaptiveBacktrack; + ray.t0 = tBegin; + //ray.time = ray.t0-tBegin; + float tSkipped = -1f; //for adaptive, skip adapting sampling rate up to this value + vec4f intervalColor = make_vec4f(0.f); + //TODO: initially sampling by max samplingRate produced artifacts, not sure why. + if (volume->adaptiveSampling) + volume->intersectAdaptive(volume, ray, samplingRate); + else + volume->intersect(volume,ray); + tBegin = tBegin+renderer->volumeEpsilon; + while (ray.t0 < tEnd && intervalColor.w < maxOpacity) + { + // Sample the volume at the hit point in world coordinates. + const vec3f coordinates = ray.org + ray.t0 * ray.dir; + const float sample = volume->computeSample(volume, coordinates); + if (lastSample == -1.f) + lastSample = sample; + + // Look up the color associated with the volume sample. + // Look up the opacity associated with the volume sample. + vec3f sampleColor; + float sampleOpacity; + sampleOpacity = volume->transferFunction->getIntegratedOpacityForValue(volume->transferFunction, + lastSample, sample); + if (volume->adaptiveSampling && sampleOpacity > adaptiveBacktrack && ray.t0 > tSkipped) // adaptive backtack + { + //adaptively refine sampling rate + const float newSamplingRate = min(adaptiveScalar*sampleOpacity*quality,maxSamplingRate); + if (newSamplingRate > samplingRate + 0.01f) + { + samplingRate = newSamplingRate; + const float ot0 = ray.t0; + ray.t0 = max(max(ray.t0-max(ray.time,volume->samplingStep),tSkipped), tBegin); + //Carson: TODO: skipping past ray.time shouldnt be necessary and avoided + //but currently greatly reduces artifacts from low to high opacity + //regions. issue with space skipping? + tSkipped = ot0; + volume->intersectAdaptive(volume, ray, samplingRate); + lastSample = -1; + continue; + } + tSkipped = ray.t0; + } + + if (!isShadowRay) + { + sampleColor = volume->transferFunction->getIntegratedColorForValue(volume->transferFunction, lastSample, sample); + lastSample = sample; + + // Compute gradient shading, if enabled. + if(volume->gradientShadingEnabled) + { + if (volume->singleShade && sampleOpacity*(1.0f - intervalColor.w) > singleMax && sampleOpacity > singleMin) + { + singleMax = sampleOpacity*(1.0f - intervalColor.w); + singleCoordinates = coordinates; + } + else if (!volume->singleShade) + { + // Use volume gradient as the normal. + const vec3f gradient = + safe_normalize(volume->computeGradient(volume, coordinates)); + + // Setup differential geometry for the volume sample point. + DifferentialGeometry dg; + dg.Ng = gradient; + if (dot(ray.dir,dg.Ng) >= 0.f) dg.Ng = neg(dg.Ng); //face forward + dg.Ns = dg.Ng; + dg.P = coordinates + dg.Ns*renderer->volumeEpsilon; + + dg.color = make_vec4f(sampleColor, 1); + dg.Ng = dg.Ns; + SciVisShadingInfo info; + initShadingInfo(info); + + //hardcode volume spec for now + info.Ks = volume->specular*sampleOpacity; + info.Ns = 20.0f; + info.local_opacity=sampleOpacity; + dg.material = 0; + shadeMaterials(dg, info); + + vec3f litColor = make_vec3f(0.0f); + info.Ns *= 0.3f; + integrateOverLights(renderer, ray, dg, info, litColor, rayOffset, sampleID, quality*0.5f); + sampleColor = litColor; + } + } + } + if (volume->adaptiveSampling) + { + // ray.time = absolute step length. convert to effective sampling rate + float dt = 1.0f/((ray.time)/volume->samplingStep); + vec4f contribution = clamp(sampleOpacity / dt) + * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); + intervalColor = intervalColor + (1.0f - intervalColor.w) * contribution; + //adaptively refine sampling rate + if (ray.t0 > tSkipped) + samplingRate = max(min(adaptiveScalar*sampleOpacity*quality,maxSamplingRate)/*nyquist frequency*/,volume->samplingRate*quality); + volume->intersectAdaptive(volume, ray, samplingRate); + } + else + { + vec4f contribution = clamp(sampleOpacity / volume->samplingRate) + * make_vec4f(sampleColor.x, sampleColor.y, sampleColor.z, 1.0f); + intervalColor = intervalColor + (1.0f - intervalColor.w) * contribution; + volume->intersect(volume, ray); + } + } + if(!isShadowRay && volume->gradientShadingEnabled && volume->singleShade && singleMax >= singleMin) + { + // Use volume gradient as the normal. + const vec3f gradient = + safe_normalize(volume->computeGradient(volume, singleCoordinates)); + + // Setup differential geometry for the volume sample point. + DifferentialGeometry dg; + dg.Ng = gradient; + if (dot(ray.dir,dg.Ng) >= 0.f) dg.Ng = neg(dg.Ng); //face forward + dg.Ns = dg.Ng; + dg.P = singleCoordinates + dg.Ns*renderer->volumeEpsilon; + + // Compute lighting. + vec3f shadedColor = make_vec3f(0.f); + + dg.color = intervalColor; + SciVisShadingInfo info; + initShadingInfo(info); + + //hardcode volume spec for now + info.Ks = volume->specular*singleMax; + info.Ns = 30.0f; + info.local_opacity=intervalColor.w; + dg.material = 0; + shadeMaterials(dg, info); + + vec3f litColor = make_vec3f(0.0f); + shadeAO(renderer, sampleID, dg, info, litColor); + info.Ns *= 0.3f; + integrateOverLights(renderer, ray, dg, info, litColor, rayOffset, sampleID, quality*0.5f); + intervalColor = make_vec4f(litColor,intervalColor.w); + } + return intervalColor; +} + + +/*! Returns the first hit volume for the provided ray and sets the ray bounds + * t0 and t, considering the provided ray offset and any clipping. If no + * volume is found, the returned volume is NULL and ray.t0 will be set to + * infinity. + */ +Volume * +SciVisRenderer_intersectVolumes(const uniform SciVisRenderer *uniform renderer, + varying Ray &ray, + const varying float &rayOffset) +{ + // The first intersected volume. + Volume * volume = NULL; + + // The ray with bounds for the first intersected volume. + Ray volumeRay = ray; + volumeRay.t0 = infinity; + + // Test each volume and find the first intersection. + for (uniform int32 i=0; isuper.model->volumeCount; i++) { + Volume *uniform volume_i = renderer->super.model->volumes[i]; + + // Intersect volume bounding box. + float t0, t1; + intersectBox(ray, volume_i->boundingBox, t0, t1); + + // Clip against volume clipping box (if specified). + if(ne(volume_i->volumeClippingBox.lower, + volume_i->volumeClippingBox.upper)) { + float tClip0, tClip1; + intersectBox(ray, volume_i->volumeClippingBox, tClip0, tClip1); + + t0 = max(t0, tClip0); + t1 = min(t1, tClip1); + } + + // Update intersected volume. + if (t0 < t1 && t0 < volumeRay.t0) { + volumeRay.t0 = t0; + volumeRay.t = t1; + volume = volume_i; + } + } + + // Update the provided ray. + ray = volumeRay; + + // If we intersected a volume, offset ray by a fraction of the nominal ray + // step. + if (volume) + ray.t0 += rayOffset * volume->samplingStep * rcpf(volume->samplingRate); + + // Return the first intersected volume. + return volume; +} + diff --git a/ospray/render/simpleAO/SimpleAO.ispc b/ospray/render/simpleAO/SimpleAO.ispc index 1af67a7cbc..e1b08925cd 100644 --- a/ospray/render/simpleAO/SimpleAO.ispc +++ b/ospray/render/simpleAO/SimpleAO.ispc @@ -90,10 +90,9 @@ inline void shade_ao(uniform SimpleAO *uniform self, const uniform float rot_x, const uniform float rot_y) { - if (ray.geomID < 0) { - color = self->super.backgroundEnabled ? self->super.bgColor : - make_vec3f(0.f); - alpha = self->super.backgroundEnabled ? 1.f : 0.f; + if (noHit(ray)) { + color = make_vec3f(self->super.bgColor); + alpha = self->super.bgColor.w; return; } @@ -139,7 +138,7 @@ inline void shade_ao(uniform SimpleAO *uniform self, hits++; } float diffuse = absf(dot(N,ray.dir)); - color = superColor * make_vec3f(diffuse * (1.0f - (float)hits/sampleCnt)); + color = superColor * make_vec3f(diffuse * (1.0f - hits/(float)sampleCnt)); alpha = 1.f; return; } diff --git a/ospray/render/simpleAO/SimpleAOMaterial.h b/ospray/render/simpleAO/SimpleAOMaterial.h index ef46ce87b1..6431c08a4e 100644 --- a/ospray/render/simpleAO/SimpleAOMaterial.h +++ b/ospray/render/simpleAO/SimpleAOMaterial.h @@ -33,7 +33,7 @@ namespace ospray { /*! \brief commit the object's outstanding changes * (such as changed parameters etc) */ - virtual void commit(); + virtual void commit() override; // ------------------------------------------------------- // member variables diff --git a/ospray/texture/Texture2D.cpp b/ospray/texture/Texture2D.cpp index 7c879668cb..a433047712 100644 --- a/ospray/texture/Texture2D.cpp +++ b/ospray/texture/Texture2D.cpp @@ -22,7 +22,7 @@ namespace ospray { Texture2D::~Texture2D() { if (!(flags & OSP_TEXTURE_SHARED_BUFFER)) - delete[] (unsigned char *)data; + delete [] (unsigned char *)data; } std::string Texture2D::toString() const diff --git a/ospray/texture/Texture2D.ih b/ospray/texture/Texture2D.ih index 446e05249b..a89b2ad25d 100644 --- a/ospray/texture/Texture2D.ih +++ b/ospray/texture/Texture2D.ih @@ -43,6 +43,11 @@ inline vec2f clamp2edge(const uniform Texture2D *uniform self, const vec2f p) return clamp(p, self->halfTexel, 1.0f - self->halfTexel); } +inline uniform bool hasAlpha(const uniform Texture2D *uniform self) +{ + return (self == NULL) ? false : self->hasAlpha; +} + /*! helper function that returns the sampled value for the first channel of the given texture diff --git a/ospray/texture/TextureParam.ih b/ospray/texture/TextureParam.ih index 11621873e5..c69fec2787 100644 --- a/ospray/texture/TextureParam.ih +++ b/ospray/texture/TextureParam.ih @@ -43,7 +43,7 @@ inline uniform bool valid(const uniform TextureParam uniform &tex) inline uniform bool hasAlpha(const uniform TextureParam uniform &tex) { - return valid(tex) ? tex.map->hasAlpha : false; + return hasAlpha(tex.map); } inline float get1f(const uniform TextureParam uniform &tex, @@ -56,7 +56,7 @@ inline float get1f(const uniform TextureParam uniform &tex, const varying vec2f uv, const varying float defaultValue) { - if (tex.map == NULL) + if (!valid(tex)) return defaultValue; return get1f(tex.map, tex.xform * uv); } @@ -71,7 +71,7 @@ inline vec3f get3f(const uniform TextureParam uniform &tex, const varying vec2f uv, const varying vec3f defaultValue) { - if (tex.map == NULL) + if (!valid(tex)) return defaultValue; return get3f(tex.map, tex.xform * uv); } @@ -86,7 +86,7 @@ inline vec4f get4f(const uniform TextureParam uniform &tex, const varying vec2f uv, const varying vec4f defaultValue) { - if (tex.map == NULL) + if (!valid(tex)) return defaultValue; return get4f(tex.map, tex.xform * uv); } diff --git a/ospray/transferFunction/LinearTransferFunction.cpp b/ospray/transferFunction/LinearTransferFunction.cpp index 366336e608..312f7e62e3 100644 --- a/ospray/transferFunction/LinearTransferFunction.cpp +++ b/ospray/transferFunction/LinearTransferFunction.cpp @@ -14,8 +14,6 @@ // limitations under the License. // // ======================================================================== // -#include "common/Data.h" -#include "common/OSPCommon.h" #include "transferFunction/LinearTransferFunction.h" #include "LinearTransferFunction_ispc.h" diff --git a/ospray/volume/BlockBrickedVolume.cpp b/ospray/volume/BlockBrickedVolume.cpp index 44c88ad695..87aade26ab 100644 --- a/ospray/volume/BlockBrickedVolume.cpp +++ b/ospray/volume/BlockBrickedVolume.cpp @@ -77,7 +77,7 @@ namespace ospray { finalRegionSize, finalRegionCoords); // Copy voxel data into the volume. const size_t NTASKS = finalRegionSize.y * finalRegionSize.z; - parallel_for(NTASKS, [&](size_t taskIndex) { + tasking::parallel_for(NTASKS, [&](size_t taskIndex) { ispc::BlockBrickedVolume_setRegion(ispcEquivalent, finalSource, (const ispc::vec3i&)finalRegionCoords, diff --git a/ospray/volume/GhostBlockBrickedVolume.cpp b/ospray/volume/GhostBlockBrickedVolume.cpp index b102c37e7b..8ebd67c830 100644 --- a/ospray/volume/GhostBlockBrickedVolume.cpp +++ b/ospray/volume/GhostBlockBrickedVolume.cpp @@ -77,7 +77,7 @@ namespace ospray { finalRegionSize, finalRegionCoords); // Copy voxel data into the volume. const int NTASKS = finalRegionSize.y * finalRegionSize.z; - parallel_for(NTASKS, [&](int taskIndex){ + tasking::parallel_for(NTASKS, [&](int taskIndex){ ispc::GBBV_setRegion(ispcEquivalent, finalSource, (const ispc::vec3i&)finalRegionCoords, diff --git a/ospray/volume/SharedStructuredVolume.cpp b/ospray/volume/SharedStructuredVolume.cpp index 033a93acf1..e49e4c6943 100644 --- a/ospray/volume/SharedStructuredVolume.cpp +++ b/ospray/volume/SharedStructuredVolume.cpp @@ -80,7 +80,7 @@ namespace ospray { "support for volumes of voxel type '" + voxelType + "'"); } - return 0; + return true; } void SharedStructuredVolume::createEquivalentISPC() diff --git a/ospray/volume/StructuredVolume.cpp b/ospray/volume/StructuredVolume.cpp index 3092d66cb3..70da1f4cea 100644 --- a/ospray/volume/StructuredVolume.cpp +++ b/ospray/volume/StructuredVolume.cpp @@ -16,7 +16,6 @@ //ospray #include "common/Data.h" -#include "common/Library.h" #include "volume/StructuredVolume.h" #include "GridAccelerator_ispc.h" #include "StructuredVolume_ispc.h" @@ -119,7 +118,7 @@ namespace ospray { // Build volume accelerator. const int NTASKS = brickCount.x * brickCount.y * brickCount.z; - parallel_for(NTASKS, [&](int taskIndex){ + tasking::parallel_for(NTASKS, [&](int taskIndex){ ispc::GridAccelerator_buildAccelerator(ispcEquivalent, taskIndex); }); } diff --git a/ospray/volume/StructuredVolume.h b/ospray/volume/StructuredVolume.h index e940aee1c9..9c3d51a2cd 100644 --- a/ospray/volume/StructuredVolume.h +++ b/ospray/volume/StructuredVolume.h @@ -19,9 +19,6 @@ // ospray #include "ospcommon/tasking/parallel_for.h" #include "volume/Volume.h" -// stl -#include -#include namespace ospray { @@ -119,7 +116,9 @@ namespace ospray { const vec3i &scaledRegionSize) { for (int z = 0; z < scaledRegionSize.z; ++z) { - parallel_for(scaledRegionSize.x * scaledRegionSize.y, [&](int taskID) { + const auto nTasks = scaledRegionSize.x * scaledRegionSize.y; + + tasking::parallel_for(nTasks, [&](int taskID) { int x = taskID % scaledRegionSize.x; int y = taskID / scaledRegionSize.x; const int idx = diff --git a/ospray/volume/Volume.cpp b/ospray/volume/Volume.cpp index 24e794341b..efafbaf7eb 100644 --- a/ospray/volume/Volume.cpp +++ b/ospray/volume/Volume.cpp @@ -15,12 +15,11 @@ // ======================================================================== // // ospray -#include "common/Library.h" #include "common/Util.h" #include "volume/Volume.h" -#include "Volume_ispc.h" #include "transferFunction/TransferFunction.h" #include "common/Data.h" +#include "Volume_ispc.h" namespace ospray { diff --git a/scripts/build_gitlab/linux.sh b/scripts/build_gitlab/linux.sh index 167f115d44..9f31a63b2d 100755 --- a/scripts/build_gitlab/linux.sh +++ b/scripts/build_gitlab/linux.sh @@ -19,6 +19,6 @@ mkdir build cd build -cmake -DOSPRAY_BUILD_ISA=ALL "$@" .. +cmake -DOSPRAY_BUILD_ISA=ALL -DOSPRAY_MODULE_BILINEAR_PATCH=ON "$@" .. make -j`nproc` diff --git a/scripts/build_gitlab/osx.sh b/scripts/build_gitlab/osx.sh index 8881e493d3..d86aa827a5 100755 --- a/scripts/build_gitlab/osx.sh +++ b/scripts/build_gitlab/osx.sh @@ -7,6 +7,7 @@ rm -rf * # NOTE(jda) - using Internal tasking system here temporarily to avoid installing TBB cmake \ -D OSPRAY_TASKING_SYSTEM=Internal \ +-D OSPRAY_MODULE_BILINEAR_PATCH=ON \ .. make -j 4 diff --git a/scripts/build_gitlab/win.bat b/scripts/build_gitlab/win.bat index ea132c68f6..a9d333fa76 100755 --- a/scripts/build_gitlab/win.bat +++ b/scripts/build_gitlab/win.bat @@ -25,6 +25,7 @@ cmake -L ^ -T "%~2" ^ -D OSPRAY_BUILD_ISA=ALL ^ -D OSPRAY_MODULE_MPI=ON ^ +-D OSPRAY_MODULE_BILINEAR_PATCH=ON ^ -D OSPRAY_USE_EXTERNAL_EMBREE=ON ^ -D USE_IMAGE_MAGICK=OFF ^ .. diff --git a/scripts/release/linux.sh b/scripts/release/linux.sh index 0624de2acc..167a2846e6 100755 --- a/scripts/release/linux.sh +++ b/scripts/release/linux.sh @@ -53,9 +53,9 @@ ROOT_DIR=$PWD DEP_DIR=$ROOT_DIR/deps DEP_LOCATION=http://sdvis.org/ospray/download/dependencies/linux -DEP_EMBREE=embree-2.14.0.x86_64.linux +DEP_EMBREE=embree-2.16.1.x86_64.linux DEP_ISPC=ispc-v1.9.1-linux -DEP_TBB=tbb2017_20161128oss +DEP_TBB=tbb2017_20170412oss DEP_TARBALLS="$DEP_EMBREE.tar.gz $DEP_ISPC.tar.gz ${DEP_TBB}_lin.tgz" @@ -112,7 +112,6 @@ cp $DEP_DIR/OSPRay_readme_$BRANCH.pdf readme.pdf # set release and RPM settings cmake \ -D OSPRAY_BUILD_ISA=ALL \ --D OSPRAY_USE_EXTERNAL_EMBREE=ON \ -D OSPRAY_MODULE_MPI=ON \ -D TBB_ROOT=$DEP_DIR/$DEP_TBB \ -D ISPC_EXECUTABLE=$DEP_DIR/$DEP_ISPC/ispc \ diff --git a/scripts/release/macosx.sh b/scripts/release/macosx.sh index fdac8f2bfa..3c4d443d2f 100755 --- a/scripts/release/macosx.sh +++ b/scripts/release/macosx.sh @@ -31,9 +31,9 @@ ROOT_DIR=$PWD DEP_DIR=$ROOT_DIR/deps DEP_LOCATION=http://sdvis.org/ospray/download/dependencies/osx -DEP_EMBREE=embree-2.14.0.x86_64.macosx +DEP_EMBREE=embree-2.16.1.x86_64.macosx DEP_ISPC=ispc-v1.9.1-osx -DEP_TBB=tbb2017_20161128oss +DEP_TBB=tbb2017_20170412oss DEP_TARBALLS="$DEP_EMBREE.tar.gz $DEP_ISPC.tar.gz ${DEP_TBB}_osx.tgz" @@ -87,7 +87,6 @@ cp $DEP_DIR/OSPRay_readme_$BRANCH.pdf readme.pdf # set release and installer settings cmake \ -D OSPRAY_BUILD_ISA=ALL \ --D OSPRAY_USE_EXTERNAL_EMBREE=ON \ -D OSPRAY_MODULE_MPI=ON \ -D TBB_ROOT=$DEP_DIR/$DEP_TBB \ -D ISPC_EXECUTABLE=$DEP_DIR/$DEP_ISPC/ispc \ diff --git a/scripts/release/win.bat b/scripts/release/win.bat index 2206c3d8f2..e0cb79c65d 100755 --- a/scripts/release/win.bat +++ b/scripts/release/win.bat @@ -28,7 +28,6 @@ cmake -L ^ -G "Visual Studio 12 2013 Win64" ^ -T "Intel C++ Compiler 17.0" ^ -D OSPRAY_BUILD_ISA=ALL ^ --D OSPRAY_USE_EXTERNAL_EMBREE=ON ^ -D OSPRAY_MODULE_MPI=ON ^ -D USE_IMAGE_MAGICK=OFF ^ -D OSPRAY_ZIP_MODE=OFF ^ diff --git a/scripts/release/win.sh b/scripts/release/win.sh index 4c896f2911..2dbec65d19 100755 --- a/scripts/release/win.sh +++ b/scripts/release/win.sh @@ -36,7 +36,6 @@ cmake -L \ -G "Visual Studio 12 2013 Win64" \ -T "Intel C++ Compiler 17.0" \ -D OSPRAY_BUILD_ISA=ALL \ --D OSPRAY_USE_EXTERNAL_EMBREE=ON \ -D OSPRAY_MODULE_MPI=ON \ -D USE_IMAGE_MAGICK=OFF \ -D OSPRAY_ZIP_MODE=OFF \
Special parameters understood by the SciVis renderer.
bool oneSidedLighting trueif true back-facing surfaces (wrt. light) receive no illuminationif true back-facing surfaces (wrt. light source) receive no illumination
vec3ffloat / vec3f / vec4f bgColorwhitebackground color (RGB)black, transparentbackground color and alpha (RGBA)
boolbackgroundEnabledtruewhether to color the background with bgColor
OSPTexture2D maxDepthTexture NULL