├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── helpers ├── FindUtils.cmake ├── GL │ ├── glew.h │ ├── glxew.h │ └── wglew.h ├── Helpers.cmake ├── Packages.cmake ├── camera.cpp ├── camera.h ├── directory.cpp ├── directory.h ├── file_png.cpp ├── file_png.h ├── file_tga.cpp ├── file_tga.h ├── glew.c ├── main.h ├── main_win.cpp ├── main_x11.cpp ├── nvToolsExt64_1.dll ├── nv_gui.cpp ├── nv_gui.h ├── string_helper.cpp ├── string_helper.h ├── vec.cpp └── vec.h ├── joints.cpp ├── joints.h ├── main.cpp └── quaternion.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows thumbnail cache files 2 | Thumbs.db 3 | ehthumbs.db 4 | ehthumbs_vista.db 5 | 6 | # Folder config file 7 | Desktop.ini 8 | 9 | # Recycle Bin used on file shares 10 | $RECYCLE.BIN/ 11 | 12 | # Windows Installer files 13 | *.cab 14 | *.msi 15 | *.msm 16 | *.msp 17 | 18 | # Windows shortcuts 19 | *.lnk 20 | 21 | # ========================= 22 | # Operating System Files 23 | # ========================= 24 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | set(PROJNAME Invk) 3 | Project(${PROJNAME}) 4 | Message(STATUS "-------------------------------") 5 | Message(STATUS "Processing Project ${PROJNAME}:") 6 | 7 | #################################################################################### 8 | # Bootstrap 9 | # 10 | set( BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) 11 | find_path ( HELPERS_PATH "Helpers.cmake" HINTS ${BASE_DIRECTORY}/../libraries/helpers ${BASE_DIRECTORY}/helpers ${CMAKE_HELPERS_PATH} ) 12 | if ( ${HELPERS_PATH} STREQUAL "HELPERS-NOTFOUND" ) 13 | message ( FATAL_ERROR "\n Please set the CMAKE_HELPERS_PATH to for cmake helpers." ) 14 | endif() 15 | include( ${HELPERS_PATH}/Helpers.cmake ) # Cross-Platform functions 16 | 17 | ##################################################################################### 18 | # Sample requirements 19 | 20 | set ( REQUIRE_JPG "0" ) 21 | set ( REQUIRE_PNG "0" ) 22 | set ( REQUIRE_TGA "0" ) 23 | set ( REQUIRE_VEC "1" ) 24 | set ( REQUIRE_CAM "1" ) 25 | set ( REQUIRE_GLEW "1" ) 26 | set ( REQUIRE_DIRECTORY "0" ) 27 | set ( REQUIRE_STRHELPER "0" ) 28 | set ( REQUIRE_MAIN "1" ) 29 | set ( REQUIRE_NVGUI "1" ) 30 | 31 | # add_definitions(-DBUILD_BMP) 32 | 33 | 34 | #################################################################################### 35 | # Find Sample Utils 36 | # 37 | find_package(Utils) 38 | 39 | ##################################################################################### 40 | # Require OpenGL 41 | # 42 | IF(WIN32) 43 | LIST(APPEND LIBRARIES_OPTIMIZED "opengl32.lib" ) 44 | LIST(APPEND LIBRARIES_DEBUG "opengl32.lib" ) 45 | ENDIF() 46 | 47 | ##################################################################################### 48 | # Source files for this project 49 | # 50 | file(GLOB SOURCE_FILES *.cpp *.c *.h ) 51 | 52 | 53 | ########################################################################3 54 | # Collect shaders for reference 55 | file(GLOB GLSL_FILES ${ASSET_PATH}/*.glsl ) 56 | 57 | ##################################################################################### 58 | # Executable 59 | # 60 | unset ( ALL_SOURCE_FILES ) 61 | list( APPEND ALL_SOURCE_FILES ${SOURCE_FILES} ) 62 | list( APPEND ALL_SOURCE_FILES ${COMMON_SOURCE_FILES} ) 63 | list( APPEND ALL_SOURCE_FILES ${PACKAGE_SOURCE_FILES} ) 64 | list( APPEND ALL_SOURCE_FILES ${UTIL_SOURCE_FILES} ) 65 | list( APPEND ALL_SOURCE_FILES ${CUDA_FILES} ) 66 | 67 | if ( NOT DEFINED WIN32 ) 68 | #find_library(NVTOOLSEXT nvToolsExt HINTS ${CUDA_TOOLKIT_ROOT_DIR}/lib64) 69 | #set(libdeps GL GLEW X11 ${NVTOOLSEXT}) 70 | set(libdeps GL GLEW X11 ) 71 | LIST(APPEND LIBRARIES_OPTIMIZED ${libdeps}) 72 | LIST(APPEND LIBRARIES_DEBUG ${libdeps}) 73 | ENDIF() 74 | 75 | include_directories ("${CMAKE_CURRENT_SOURCE_DIR}") 76 | add_definitions(-DGVDB_IMPORTS -DGLEW_STATIC -DGLEW_NO_GLU) 77 | add_definitions(-DASSET_PATH="${ASSET_PATH}/") 78 | add_executable (${PROJNAME} ${ALL_SOURCE_FILES} ${CUDA_FILES} ${PTX_SOURCES} ${GLSL_FILES} ) 79 | set_property ( TARGET ${PROJNAME} APPEND PROPERTY DEPENDS ${PTX_SOURCES} ) 80 | 81 | 82 | if ( MSVC ) 83 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} ) 84 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH} ) 85 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH} ) 86 | source_group(CUDA FILES ${CUDA_FILES}) 87 | source_group(PTX FILES ${PTX_FILES}) 88 | source_group(Helpers FILES ${UTIL_SOURCE_FILES}) 89 | 90 | source_group(Core FILES ${SOURCE_FILES}) 91 | endif () 92 | 93 | 94 | 95 | ##################################################################################### 96 | # Library dependencies 97 | # 98 | set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug) 99 | 100 | foreach (loop_var IN ITEMS ${LIBRARIES_OPTIMIZED} ) 101 | target_link_libraries ( ${PROJNAME} optimized ${loop_var} ) 102 | endforeach() 103 | 104 | foreach (loop_var IN ITEMS ${LIBRARIES_DEBUG} ) 105 | target_link_libraries ( ${PROJNAME} debug ${loop_var} ) 106 | endforeach() 107 | 108 | message ( STATUS "CMAKE_CURRENT_SOURCE_DIR: ${CMAKE_CURRENT_SOURCE_DIR}" ) 109 | message ( STATUS "CMAKE_CURRENT_BINARY_DIR: ${CMAKE_CURRENT_BINARY_DIR}" ) 110 | message ( STATUS "EXECUTABLE_OUTPUT_PATH: ${EXECUTABLE_OUTPUT_PATH}" ) 111 | 112 | 113 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | InvK - Inverse Kinematics Library using Quaternions 4 | ------------------------------------------------ 5 | 6 | by Rama Hoetzlein ([ramakarl.com](http://ramakarl.com)) 7 | 8 | This is a simple library that demonsrates an efficient solution to 9 | inverse kinematic chains using the Jacobian Transpose method over 10 | quaternions. The goal of this library is to enable a baseline 11 | implementation with few dependencies upon which to build larger projects. 12 | 13 | **Video demo here**:
14 | [Youtube link](https://youtu.be/j6bwD-kF_BI)
15 | 16 | **Citations**: 17 | You can cite this work as follows:
18 | Hoetzlein, Rama. "INVK - A lightweight Inverse Kinematics Library using Quaternions", May 6th, 2019. Online at: github.com/ramakarl/invk
19 |
20 | More useful reading material: 21 |
22 | Steve Rotenberg, Inverse Kinematics (part 1), UCSB. [Slides](https://cseweb.ucsd.edu/classes/wi17/cse169-a/slides/CSE169_08.pdf)
23 | Steve Rotenberg, Inverse Kinematics (part 2), UCSB. [Slides](https://cseweb.ucsd.edu/classes/wi17/cse169-a/slides/CSE169_09.pdf)
24 | Andreas Aristidou and Joan Lasenby, Inverse Kinematics: a review of existing techniques and introduction of a new fast iterative solver. [Tech Report](http://www.andreasaristidou.com/publications/papers/CUEDF-INFENG,%20TR-632.pdf)
25 | 26 | IK and Quaternions 27 | ------------------ 28 | Quaternions allow for several benefits over Euler angles. First, axis boundaries are greatly simplified as quaternions can interpolate thru two arbitrary vectors. Second, IK requires incremental changes in angles which are well suited to quaternions. Third, quaternions are more efficient to compute for certain operations. 29 | 30 | There are two drawbacks to using quaternions for inverse kinematics. Per-axis angle range limits are more easily computed with Euler angles, so there is a conversion performed in the LimitQuaternion function to handle this. Finally, care must be taken to normalize the quaternions frequently during calculations. 31 | 32 | Quaternions can represent an orientation (a local coordinate system), or they can represent a rotation (amount to rotate around a given axis), which makes it easy to compute changes in orientation. For example, a key operation during IK is to rotate a joint around its local coordinate X,Y or Z axis by an incremental angle. This is easily accomplished by observing that a joint which is oriented by a quaternion is **locally** rotated by performed a post-multiplying with a rotational quaternion. 33 |

34 | P.fromAngleAxis ( angle, Vector3DF(0, 1, 0) ); // where angle is a scalar, vec<0,1,0> = Y-axis
35 | Q = Q * P; // post-multiply to perform a rotation around the **local** Y-axis of Q.
36 | Q = P * Q; // pre-multiply to perform a rotation around the **global** Y-axis of Q.
37 |
38 | Where Q is a quaternion for local orientation, and P is a rotational quaternion. Notice the output Q is not a point but another quaternion (a new orientation). 39 | 40 | Revision History 41 | -------- 42 | May 6, 2019 - v1.0 - Support for hinge and ball joints, with joint limits. 43 | 44 | How to Build 45 | ------- 46 | * You will need to install cmake 47 | 1. Clone into a folder for invk 48 | 2. Create a build folder somewhere, eg. invk/build 49 | 3. From inside that folder: invk/build> cmake .. -DCMAKE_HELPERS_PATH=invk/helpers 50 | 4. When compile & generate succeed, run 'make' 51 | 52 | LICENSE 53 | ------- 54 | This library is licensed under the LGPLv3 license. 55 | https://www.gnu.org/licenses/lgpl-3.0.txt 56 | 57 | Short summary: 58 | - Public changes to the library itself must be back-contributed or forked as LGPL 59 | - Use in larger projects that are not LGPL are allowed 60 | 61 | Rama Hoetzlein (c) May 2019 62 | -------------------------------------------------------------------------------- /helpers/FindUtils.cmake: -------------------------------------------------------------------------------- 1 | 2 | 3 | ##################################################################################### 4 | # Find Sample Utils 5 | # 6 | unset( UTILS_FOUND CACHE) 7 | 8 | message ( STATUS "--> Find Sample Utils") 9 | 10 | set ( SAMPLE_UTIL_DIR "${CMAKE_HELPERS_PATH}" CACHE PATH "Path to sample_utils" ) 11 | 12 | set ( OK_UTIL "0" ) 13 | _FIND_FILE ( UTIL_FILES SAMPLE_UTIL_DIR "nv_gui.h" "nv_gui.h" OK_UTIL ) 14 | 15 | if ( OK_UTIL EQUAL 1 ) 16 | set ( UTILS_FOUND "YES" ) 17 | include_directories( ${SAMPLE_UTIL_DIR} ) 18 | 19 | # OpenGL always required 20 | if ( REQUIRE_OPENGL ) 21 | find_package(OpenGL) 22 | message ( STATUS " Using OpenGL") 23 | endif() 24 | 25 | #-------------------------------- Image formats 26 | if ( REQUIRE_PNG ) 27 | # Add PNG to build 28 | message ( STATUS " Using PNG") 29 | add_definitions(-DBUILD_PNG) 30 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/file_png.cpp" ) 31 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/file_png.h" ) 32 | endif() 33 | if ( REQUIRE_TGA ) 34 | # Add TGA to build 35 | message ( STATUS " Using TGA") 36 | add_definitions(-DBUILD_TGA) 37 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/file_tga.cpp" ) 38 | endif() 39 | if ( REQUIRE_JPG ) 40 | message( STATUS " Using JPG") 41 | add_definitions(-DBUILD_JPG) 42 | LIST(APPEND LIBRARIES_OPTIMIZED "${COMBINED_LIB_PATH}/libjpg_2015x64.lib" ) 43 | LIST(APPEND LIBRARIES_DEBUG "${COMBINED_LIB_PATH}/libjpg_2015x64d.lib" ) 44 | endif() 45 | #---------------------------------------- Graphics Libs 46 | if ( REQUIRE_OPTIX ) 47 | # Add OptiX to build 48 | # Collect all OptiX utility files 49 | add_definitions(-DUSE_OPTIX_UTILS) 50 | message ( STATUS " Using OPTIX_UTILS") 51 | FILE( GLOB UTIL_OPTIX_FILES "${SAMPLE_UTIL_DIR}/optix*.cpp" "${SAMPLE_UTIL_DIR}/optix*.hpp") 52 | FILE( GLOB UTIL_OPTIX_KERNELS "${SAMPLE_UTIL_DIR}/optix*.cu" "${SAMPLE_UTIL_DIR}/optix*.cuh") 53 | endif() 54 | if ( REQUIRE_GLEW ) 55 | # Add GLEW to build 56 | message ( STATUS " Using GLEW") 57 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/glew.c" ) 58 | endif() 59 | if ( REQUIRE_NVGUI ) 60 | # Add NVGUI to build 61 | message ( STATUS " Using NVGUI") 62 | add_definitions(-DUSE_NVGUI) 63 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/nv_gui.cpp" ) 64 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/nv_gui.h" ) 65 | endif() 66 | if ( REQUIRE_CAM ) 67 | # Add Camera to build 68 | message ( STATUS " Using CAM") 69 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/camera.cpp" ) 70 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/camera.h" ) 71 | endif() 72 | if ( REQUIRE_VEC ) 73 | # Add Vec to build 74 | message ( STATUS " Using VEC") 75 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/vec.cpp" ) 76 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/vec.h" ) 77 | endif() 78 | #---------------------------------------- Strings & Dirs 79 | if ( REQUIRE_STRHELPER ) 80 | message ( STATUS " Using STR_HELPER") 81 | add_definitions(-DUSE_STR_HELPER) 82 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/string_helper.cpp" ) 83 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/string_helper.h" ) 84 | endif() 85 | if ( REQUIRE_DIRECTORY ) 86 | message ( STATUS " Using DIRECTORY") 87 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/directory.cpp" ) 88 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/directory.h" ) 89 | endif() 90 | #---------------------------------------- Main 91 | if ( REQUIRE_MAIN ) 92 | # Add Main to build 93 | IF(WIN32) 94 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/main_win.cpp" ) 95 | ELSE() 96 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/main_x11.cpp" ) 97 | ENDIF() 98 | LIST( APPEND UTIL_SOURCE_FILES "${SAMPLE_UTIL_DIR}/main.h" ) 99 | endif() 100 | 101 | message ( STATUS "--> Using Sample Utils. ${SAMPLE_UTIL_DIR}\n") 102 | else() 103 | set ( UTILS_FOUND "NO" ) 104 | message ( FATAL_ERROR " 105 | Sample Utils not found. Please set the CMAKE_MODULE_PATH \ 106 | and the SAMPLE_UTIL_DIR to the location of sample_utils path, \ 107 | which contains nv_gui, file_png, file_tga, main_win, main_x11, \ 108 | and cmake helpers. \n 109 | ") 110 | endif() 111 | 112 | mark_as_advanced( UTILS_FOUND ) 113 | -------------------------------------------------------------------------------- /helpers/Helpers.cmake: -------------------------------------------------------------------------------- 1 | 2 | #################################################################################### 3 | # CMAKE HELPERS - GLOBAL START 4 | 5 | # Helpers path 6 | set ( CMAKE_HELPERS_PATH ${HELPERS_PATH} CACHE PATH "Full path to helpers." ) 7 | set ( CMAKE_MODULE_PATH ${CMAKE_HELPERS_PATH} CACHE PATH "Full path to cmake finders.") 8 | set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} CACHE PATH "Executable path" ) 9 | 10 | # Asset path 11 | if ( NOT DEFINED ASSET_PATH ) 12 | get_filename_component ( _assets "${BASE_DIRECTORY}/assets" REALPATH ) 13 | set ( ASSET_PATH ${_assets} CACHE PATH "Full path to /assets" ) 14 | endif() 15 | 16 | # Combined libraries 17 | get_filename_component ( _combined "${CMAKE_HELPERS_PATH}/../combined" REALPATH ) 18 | set ( COMBINED_ROOT_DIR ${_combined} CACHE PATH "Path to combined libraries.") 19 | set ( COMBINED_LIB_PATH "${_combined}/lib/" CACHE PATH "Combined libs.") 20 | set ( COMBINED_INCLUDE_PATH "${_combined}/include/" CACHE PATH "Combined includes.") 21 | include_directories( ${COMBINED_INCLUDE_PATH} ) 22 | 23 | set ( DEBUG_HEAP false CACHE BOOL "Enable heap checking (debug or release).") 24 | if ( ${DEBUG_HEAP} ) 25 | add_definitions( -DDEBUG_HEAP) 26 | add_definitions( -D_CRTDBG_MAP_ALLOC) 27 | endif() 28 | 29 | message ( "-------- HELPER BOOTSTRAP ----------------" ) 30 | message ( "BASE DIRECTORY: ${BASE_DIRECTORY}" ) 31 | message ( "ASSET_PATH: ${ASSET_PATH}" ) 32 | message ( "CMAKE_HELPERS_PATH: ${CMAKE_HELPERS_PATH}" ) 33 | message ( "COMBINED_ROOT_DIR: ${COMBINED_ROOT_DIR}" ) 34 | message ( "COMBINED_LIB_PATH: ${COMBINED_LIB_PATH}" ) 35 | message ( "COMBINED_INC_PATH: ${COMBINED_INCLUDE_PATH}" ) 36 | 37 | 38 | #-------------------------------------- COPY CUDA BINS 39 | # This macro copies all binaries for the CUDA library to the target executale location. 40 | # 41 | macro(_copy_cuda_bins projname ) 42 | add_custom_command( 43 | TARGET ${projname} POST_BUILD 44 | COMMAND ${CMAKE_COMMAND} -E copy ${CUDA_DLL} $ 45 | ) 46 | endmacro() 47 | 48 | 49 | #------------------------------------ CROSS-PLATFORM PTX COMPILE 50 | # 51 | # _COMPILEPTX( SOURCES file1.cu file2.cu TARGET_PATH GENERATED_FILES ptx_sources NVCC_OPTIONS -arch=sm_20) 52 | # Generates ptx files for the given source files. ptx_sources will contain the list of generated files. 53 | # 54 | FUNCTION( _COMPILEPTX ) 55 | set(options "") 56 | set(oneValueArgs TARGET_PATH GENERATED GENPATHS INCLUDE) 57 | set(multiValueArgs OPTIONS SOURCES) 58 | CMAKE_PARSE_ARGUMENTS( _COMPILEPTX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 59 | 60 | # Match the bitness of the ptx to the bitness of the application 61 | set( MACHINE "--machine=32" ) 62 | if( CMAKE_SIZEOF_VOID_P EQUAL 8) 63 | set( MACHINE "--machine=64" ) 64 | endif() 65 | unset ( PTX_FILES CACHE ) 66 | unset ( PTX_FILES_PATH CACHE ) 67 | 68 | set( USE_DEBUG_PTX OFF CACHE BOOL "Enable CUDA debugging with NSight") 69 | if ( USE_DEBUG_PTX ) 70 | set ( DEBUG_FLAGS ";-g;-G;-D_DEBUG;-DEBUG") 71 | else() 72 | set ( DEBUG_FLAGS "") 73 | endif() 74 | 75 | if ( WIN32 ) 76 | # Windows - PTX compile 77 | file ( MAKE_DIRECTORY "${_COMPILEPTX_TARGET_PATH}/Debug" ) 78 | file ( MAKE_DIRECTORY "${_COMPILEPTX_TARGET_PATH}/Release" ) 79 | string (REPLACE ";" " " _COMPILEPTX_OPTIONS "${_COMPILEPTX_OPTIONS}") 80 | separate_arguments( _OPTS WINDOWS_COMMAND "${_COMPILEPTX_OPTIONS}" ) 81 | message ( STATUS "NVCC Options: ${_COMPILEPTX_OPTIONS}" ) 82 | message ( STATUS "NVCC Include: ${_COMPILEPTX_INCLUDE}" ) 83 | 84 | set ( INCL "-I\"${_COMPILEPTX_INCLUDE}\"" ) 85 | 86 | # Custom build rule to generate ptx files from cuda files 87 | FOREACH( input ${_COMPILEPTX_SOURCES} ) 88 | get_filename_component( input_ext ${input} EXT ) # Input extension 89 | get_filename_component( input_without_ext ${input} NAME_WE ) # Input base 90 | if ( ${input_ext} STREQUAL ".cu" ) 91 | 92 | # Set output names 93 | set( output "${input_without_ext}.ptx" ) # Output name 94 | set( output_with_path "${_COMPILEPTX_TARGET_PATH}/$(Configuration)/${input_without_ext}.ptx" ) # Output with path 95 | set( output_with_quote "\"${output_with_path}\"" ) 96 | LIST( APPEND PTX_FILES ${output} ) # Append to output list 97 | LIST( APPEND PTX_FILES_PATH ${output_with_path} ) 98 | 99 | message( STATUS "NVCC Compile: ${CUDA_NVCC_EXECUTABLE} ${MACHINE} --ptx ${_COMPILEPTX_OPTIONS} ${input} ${INCL} -o ${output_with_path} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}") 100 | 101 | add_custom_command( 102 | OUTPUT ${output_with_path} 103 | MAIN_DEPENDENCY ${input} 104 | COMMAND ${CUDA_NVCC_EXECUTABLE} ${MACHINE} --ptx ${_OPTS} ${input} ${INCL} -o ${output_with_quote} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" 105 | ) 106 | endif() 107 | ENDFOREACH( ) 108 | else () 109 | # Linux - PTX compile 110 | file ( MAKE_DIRECTORY "${_COMPILEPTX_TARGET_PATH}" ) 111 | cuda_compile_ptx(PTX_FILES ${_COMPILEPTX_SOURCES} ) 112 | SET ( PTX_FILES_PATH ${PTX_FILES} ) 113 | endif() 114 | 115 | set( ${_COMPILEPTX_GENERATED} ${PTX_FILES} PARENT_SCOPE) 116 | set( ${_COMPILEPTX_GENPATHS} ${PTX_FILES_PATH} PARENT_SCOPE) 117 | 118 | ENDFUNCTION() 119 | 120 | #------------------------------------ CROSS-PLATFORM INSTALL 121 | function( _INSTALL ) 122 | set (options "") 123 | set (oneValueArgs DESTINATION SOURCE OUTPUT ) 124 | set (multiValueArgs FILES ) 125 | CMAKE_PARSE_ARGUMENTS(_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 126 | if ( _INSTALL_SOURCE ) 127 | set ( _INSTALL_SOURCE "${_INSTALL_SOURCE}/" ) 128 | endif() 129 | set ( OUT_LIST ${${_INSTALL_OUTPUT}} ) 130 | 131 | if ( WIN32 ) 132 | # Windows - copy to desintation at post-build 133 | file ( MAKE_DIRECTORY "${_INSTALL_DESTINATION}/" ) 134 | foreach (_file ${_INSTALL_FILES} ) 135 | message ( STATUS "Install: ${_INSTALL_SOURCE}${_file} -> ${_INSTALL_DESTINATION}" ) 136 | add_custom_command( 137 | TARGET ${PROJNAME} POST_BUILD 138 | COMMAND ${CMAKE_COMMAND} -E copy ${_INSTALL_SOURCE}${_file} ${_INSTALL_DESTINATION} 139 | ) 140 | list ( APPEND OUT_LIST "${_INSTALL_SOURCE}${_file}" ) 141 | endforeach() 142 | else () 143 | # Linux 144 | if ( _INSTALL_SOURCE ) 145 | foreach ( _file ${_INSTALL_FILES} ) 146 | list ( APPEND OUT_LIST "${_INSTALL_SOURCE}${_file}" ) 147 | endforeach() 148 | else() 149 | list ( APPEND OUT_LIST ${_INSTALL_FILES} ) 150 | endif() 151 | endif( ) 152 | set ( ${_INSTALL_OUTPUT} ${OUT_LIST} PARENT_SCOPE ) 153 | 154 | endfunction() 155 | 156 | #------------------------------------------------- CROSS-PLATFORM INSTALL PTX 157 | # 158 | function( _INSTALL_PTX ) 159 | set (options "") 160 | set (oneValueArgs DESTINATION OUTPUT ) 161 | set (multiValueArgs FILES ) 162 | CMAKE_PARSE_ARGUMENTS(_INSTALL_PTX "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 163 | 164 | set ( OUT_LIST ${${_INSTALL_PTX_OUTPUT}} ) 165 | 166 | unset ( PTX_FIXED ) 167 | 168 | if ( WIN32 ) 169 | 170 | foreach ( _file IN ITEMS ${_INSTALL_PTX_FILES} ) 171 | get_filename_component ( _ptxbase ${_file} NAME_WE ) 172 | get_filename_component ( _ptxpath ${_file} DIRECTORY ) 173 | get_filename_component ( _ptxparent ${_ptxpath} DIRECTORY ) # parent directory 174 | set ( _fixed "${_ptxparent}/${_ptxbase}.ptx" ) # copy to parent to remove compile time $(Configuration) path 175 | add_custom_command ( TARGET ${PROJNAME} POST_BUILD 176 | COMMAND ${CMAKE_COMMAND} -E copy ${_file} ${_fixed} 177 | ) 178 | list ( APPEND PTX_FIXED ${_file} ) # NOTE: Input of FILES must be list of ptx *with paths* 179 | list ( APPEND OUT_LIST ${_fixed} ) 180 | endforeach() 181 | 182 | else() 183 | 184 | foreach ( _file IN ITEMS ${_INSTALL_PTX_FILES} ) 185 | get_filename_component ( _ptxpath ${_file} DIRECTORY ) 186 | get_filename_component ( _ptxbase ${_file} NAME_WE ) 187 | string ( SUBSTRING ${_ptxbase} 27 -1 _ptxname ) 188 | set ( _fixed "${_ptxpath}/${_ptxname}.ptx" ) 189 | add_custom_command ( TARGET ${PROJNAME} PRE_LINK 190 | COMMAND ${CMAKE_COMMAND} -E copy ${_file} ${_fixed} 191 | ) 192 | list ( APPEND PTX_FIXED ${_fixed} ) 193 | list ( APPEND OUT_LIST ${_fixed} ) 194 | endforeach() 195 | endif() 196 | 197 | # Install PTX 198 | message ( STATUS "PTX files: ${PTX_FIXED}" ) 199 | _INSTALL ( FILES ${PTX_FIXED} DESTINATION ${_INSTALL_PTX_DESTINATION} ) 200 | 201 | set ( ${_INSTALL_PTX_OUTPUT} ${OUT_LIST} PARENT_SCOPE ) 202 | 203 | endfunction() 204 | 205 | 206 | #----------------------------------------------- CROSS-PLATFORM FIND FILES 207 | # Find one or more of a specific file in the given folder 208 | # Returns the file name w/o path 209 | 210 | macro(_FIND_FILE targetVar searchDir nameWin64 nameLnx cnt) 211 | unset ( fileList ) 212 | unset ( nameFind ) 213 | unset ( targetVar ) 214 | if ( WIN32 ) 215 | SET ( nameFind ${nameWin64} ) 216 | else() 217 | SET ( nameFind ${nameLnx} ) 218 | endif() 219 | if ( "${nameFind}" STREQUAL "" ) 220 | MATH (EXPR ${cnt} "${${cnt}}+1" ) 221 | else() 222 | file(GLOB fileList "${${searchDir}}/${nameFind}") 223 | list(LENGTH fileList NUMLIST) 224 | if (NUMLIST GREATER 0) 225 | MATH (EXPR ${cnt} "${${cnt}}+1" ) 226 | list(APPEND ${targetVar} ${nameFind} ) 227 | endif() 228 | endif() 229 | endmacro() 230 | 231 | #----------------------------------------------- CROSS-PLATFORM FIND MULTIPLE 232 | # Find all files in specified folder with the given extension. 233 | # This creates a file list, where each entry is only the filename w/o path 234 | # Return the count of files 235 | macro(_FIND_MULTIPLE targetVar searchDir extWin64 extLnx cnt) 236 | unset ( fileList ) 237 | unset ( targetVar ) 238 | unset ( ${cnt} ) 239 | set ( ${cnt} "0" ) 240 | if ( WIN32 ) 241 | SET ( extFind ${extWin64} ) 242 | else() 243 | SET ( extFind ${extLnx} ) 244 | endif() 245 | file( GLOB fileList "${${searchDir}}/*.${extFind}") 246 | list( LENGTH fileList NUMLIST) 247 | math( EXPR ${cnt} "${${cnt}}+${NUMLIST}" ) 248 | foreach ( _file ${fileList} ) 249 | get_filename_component ( fname ${_file} NAME ) 250 | list( APPEND ${targetVar} ${fname} ) 251 | endforeach() 252 | endmacro() 253 | 254 | #----------------------------------------------- LIST ALL source 255 | function(_LIST_ALL_SOURCE ) 256 | set (options "") 257 | set (oneValueArgs "" ) 258 | set (multiValueArgs FILES ) 259 | CMAKE_PARSE_ARGUMENTS(_LIST_ALL_SOURCE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 260 | 261 | unset ( SOURCE_LIST ) 262 | foreach ( _file IN ITEMS ${_LIST_ALL_SOURCE_FILES} ) 263 | message ( STATUS "Source: ${_file}") # uncomment to check source files 264 | list ( APPEND SOURCE_LIST ${_file} ) 265 | endforeach() 266 | 267 | set ( ALL_SOURCE_FILES ${SOURCE_LIST} PARENT_SCOPE ) 268 | endfunction() 269 | 270 | function(_LINK ) 271 | set (options "") 272 | set (multiValueArgs PROJECT OPT DEBUG PLATFORM ) 273 | CMAKE_PARSE_ARGUMENTS(_LINK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 274 | 275 | set_property(GLOBAL PROPERTY DEBUG_CONFIGURATIONS Debug PARENT_SCOPE ) 276 | 277 | set (PROJ_NAME ${_LINK_PROJECT}) 278 | 279 | foreach (loop_var IN ITEMS ${_LINK_PLATFORM} ) 280 | target_link_libraries( ${PROJ_NAME} general ${loop_var} ) 281 | list (APPEND LIBLIST ${loop_var}) 282 | endforeach() 283 | 284 | foreach (loop_var IN ITEMS ${_LINK_DEBUG} ) 285 | target_link_libraries ( ${PROJ_NAME} debug ${loop_var} ) 286 | list (APPEND LIBLIST ${loop_var}) 287 | endforeach() 288 | 289 | foreach (loop_var IN ITEMS ${_LINK_OPT} ) 290 | target_link_libraries ( ${PROJ_NAME} optimized ${loop_var} ) 291 | endforeach() 292 | 293 | string (REPLACE ";" "\n " OUTSTR "${LIBLIST}") 294 | message ( STATUS "Libraries used:\n ${OUTSTR}" ) 295 | endfunction() 296 | 297 | macro(_MSVC_PROPERTIES) 298 | # Instruct CMake to automatically build INSTALL project in Visual Studio 299 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 300 | 301 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${EXECUTABLE_OUTPUT_PATH} ) 302 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_DEBUG ${EXECUTABLE_OUTPUT_PATH} ) 303 | set_target_properties( ${PROJNAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY_RELEASE ${EXECUTABLE_OUTPUT_PATH} ) 304 | 305 | # Set startup PROJECT 306 | if ( (${CMAKE_MAJOR_VERSION} EQUAL 3 AND ${CMAKE_MINOR_VERSION} GREATER 5) OR (${CMAKE_MAJOR_VERSION} GREATER 3) ) 307 | message ( STATUS "VS Startup Project: ${CMAKE_CURRENT_BINARY_DIR}, ${PROJNAME}") 308 | set_property ( DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJNAME} ) 309 | endif() 310 | 311 | # Source groups 312 | source_group(CUDA FILES ${CUDA_FILES}) 313 | source_group(PTX FILES ${PTX_FILES}) 314 | source_group(OPTIX FILES ${UTIL_OPTIX_KERNELS}) 315 | endmacro () 316 | 317 | macro(_DEFAULT_INSTALL_PATH) 318 | if ( CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT ) 319 | if (WIN32) 320 | get_filename_component ( _instpath "${CMAKE_CURRENT_SOURCE_DIR}/../../_output" REALPATH ) 321 | else() 322 | get_filename_component ( _instpath "/usr/local/gvdb/_output" REALPATH ) 323 | endif() 324 | set ( CMAKE_INSTALL_PREFIX ${_instpath} CACHE PATH "default install path" FORCE) 325 | endif() 326 | get_filename_component( BIN_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/bin REALPATH) 327 | endmacro() 328 | -------------------------------------------------------------------------------- /helpers/Packages.cmake: -------------------------------------------------------------------------------- 1 | 2 | set(VERSION "1.3.3") 3 | 4 | 5 | if ( MSVC_VERSION ) 6 | # MSVC_VERSION: 7 | # https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B 8 | # 1600 - VS 11.0 - 2010 9 | # 1700 - VS 11.0 - 2012 10 | # 1800 - VS 12.0 - 2013 11 | # 1900 - VS 14.0 - 2015 12 | # 1910 - VS 14.1 - 2017 13 | # 1911 - VS 14.1 - 2017 14 | # 1912 - VS 15.0 - 2017 15 | message ( STATUS "MSVC_VERSION = ${MSVC_VERSION}" ) 16 | if ( ${MSVC_VERSION} EQUAL "1600" ) 17 | SET ( MSVC_YEAR "2010" ) 18 | endif() 19 | if ( ${MSVC_VERSION} EQUAL "1700" ) 20 | SET ( MSVC_YEAR "2012" ) 21 | endif() 22 | if ( ${MSVC_VERSION} EQUAL "1800" ) 23 | SET ( MSVC_YEAR "2013" ) 24 | endif() 25 | if ( ${MSVC_VERSION} EQUAL "1900" ) 26 | SET ( MSVC_YEAR "2015" ) 27 | endif() 28 | if ( ${MSVC_VERSION} EQUAL "1910" ) 29 | SET ( MSVC_YEAR "2017" ) 30 | endif() 31 | if ( ${MSVC_VERSION} EQUAL "1911" ) 32 | SET ( MSVC_YEAR "2017" ) 33 | endif() 34 | if ( ${MSVC_VERSION} EQUAL "1912" ) 35 | SET ( MSVC_YEAR "2017" ) # USE LIBS FOR VS2015 due to cl.exe issue 36 | endif() 37 | endif() 38 | 39 | # Set the default build to Release. Note this doesn't do anything for the VS 40 | # default build target. 41 | if(NOT CMAKE_BUILD_TYPE) 42 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING 43 | "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." 44 | FORCE) 45 | endif(NOT CMAKE_BUILD_TYPE) 46 | 47 | ##################################################################################### 48 | function(_make_relative FROM TO OUT) 49 | #message(STATUS "FROM = ${FROM}") 50 | #message(STATUS "TO = ${TO}") 51 | 52 | get_filename_component(FROM ${FROM} ABSOLUTE) 53 | get_filename_component(TO ${TO} ABSOLUTE) 54 | 55 | string(REPLACE "/" ";" FROM_LIST ${FROM}) 56 | string(REPLACE "/" ";" TO_LIST ${TO}) 57 | 58 | #message(STATUS "FROM = ${FROM_LIST}") 59 | #message(STATUS "TO = ${TO_LIST}") 60 | 61 | list(LENGTH FROM_LIST flen) 62 | math(EXPR flen "${flen} - 1" ) 63 | #message(STATUS "flen = ${flen}") 64 | list(LENGTH TO_LIST tlen) 65 | math(EXPR tlen "${tlen} - 1" ) 66 | #message(STATUS "tlen = ${tlen}") 67 | 68 | set(REL_LIST) 69 | foreach(loop_var RANGE ${flen}) 70 | #message(STATUS "i = ${loop_var}") 71 | if ((loop_var GREATER tlen) OR (loop_var EQUAL tlen)) 72 | list(APPEND REL_LIST "..") 73 | #message(STATUS "descend") 74 | else() 75 | list(GET FROM_LIST ${loop_var} f) 76 | list(GET TO_LIST ${loop_var} t) 77 | #message(STATUS "f = ${f}") 78 | #message(STATUS "t = ${t}") 79 | if (${f} STREQUAL ${t}) 80 | set(begin ${loop_var}) 81 | #message(STATUS "equal") 82 | else() 83 | list(APPEND REL_LIST "..") 84 | #message(STATUS "descend") 85 | endif() 86 | endif() 87 | endforeach(loop_var) 88 | if (begin) 89 | math(EXPR begin "${begin} + 1" ) 90 | endif() 91 | 92 | #message(STATUS "---") 93 | 94 | foreach(loop_var RANGE ${begin} ${tlen}) 95 | #message(STATUS "i = ${loop_var}") 96 | #message(STATUS "t = ${t}") 97 | #message(STATUS "ascend") 98 | list(GET TO_LIST ${loop_var} t) 99 | list(APPEND REL_LIST ${t}) 100 | endforeach(loop_var) 101 | 102 | #message(STATUS "relative = ${REL_LIST}") 103 | 104 | string (REPLACE ";" "/" _TMP_STR "${REL_LIST}") 105 | set (${OUT} "${_TMP_STR}" PARENT_SCOPE) 106 | endfunction() 107 | 108 | macro(_add_project_definitions name) 109 | if(MSVC) 110 | _make_relative("${EXECUTABLE_OUTPUT_PATH}/config" "${CMAKE_CURRENT_SOURCE_DIR}" TOPROJECT) 111 | else() 112 | _make_relative("${EXECUTABLE_OUTPUT_PATH}" "${CMAKE_CURRENT_SOURCE_DIR}" TOPROJECT) 113 | endif() 114 | 115 | #message(STATUS "${TOPROJECT}") 116 | 117 | add_definitions(-DPROJECT_RELDIRECTORY="${TOPROJECT}/") 118 | add_definitions(-DPROJECT_ABSDIRECTORY="${CMAKE_CURRENT_SOURCE_DIR}/") 119 | add_definitions(-DPROJECT_NAME="${name}") 120 | 121 | endmacro(_add_project_definitions) 122 | 123 | ##################################################################################### 124 | if(UNIX) 125 | set(OS "linux") 126 | add_definitions(-DLINUX) 127 | else(UNIX) 128 | if(APPLE) 129 | else(APPLE) 130 | if(WIN32) 131 | set(OS "win") 132 | add_definitions(-DNOMINMAX) 133 | if(MEMORY_LEAKS_CHECK) 134 | add_definitions(-DMEMORY_LEAKS_CHECK) 135 | endif() 136 | endif(WIN32) 137 | endif(APPLE) 138 | endif(UNIX) 139 | 140 | 141 | if (MSVC90) 142 | include_directories(${BASE_DIRECTORY}/stdint_old_msvc) 143 | endif(MSVC90) 144 | 145 | 146 | 147 | ####################################################### 148 | # LOCAL PACKAGES 149 | # 150 | macro ( _FIND_LOCALPACKAGE_CUDA ) 151 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/CUDA) 152 | Message(STATUS "Local CUDA detected. Using it") 153 | set(CUDA_LOCALPACK_VER "9.0" CACHE STRING "CUDA Version") 154 | set(CUDA_TOOLKIT_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/CUDA/v${CUDA_LOCALPACK_VER}_win") 155 | endif() 156 | endmacro() 157 | 158 | macro ( _FIND_LOCALPACKAGE_OPENVDB ) 159 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/OpenVDB) 160 | Message(STATUS "Local OpenVDB detected. Using it") 161 | set(OPENVDB_LOCALPACK_VER "4.0.1" CACHE STRING "OpenVDB Version") 162 | set(OPENVDB_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/OpenVDB/OpenVDB_${OPENVDB_LOCALPACK_VER}_vs2015" ) 163 | SET(USE_OPENVDB ON CACHE BOOL "Use OpenVDB" FORCE) 164 | endif() 165 | endmacro() 166 | 167 | macro ( _FIND_LOCALPACKAGE_OPTIX ) 168 | if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/Optix) 169 | Message(STATUS "Local Optix detected. Using it") 170 | set(OPTIX_LOCALPACK_VER "5.0.0" CACHE STRING "OptiX Version") 171 | set(OPTIX_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../../LocalPackages/Optix/Optix_${OPTIX_LOCALPACK_VER}_win64") 172 | endif() 173 | endmacro () 174 | 175 | ##################################################################################### 176 | # Optional GVDB 177 | # 178 | macro(_add_package_GVDB) 179 | 180 | message( STATUS "<-- Searching for package GVDB") 181 | find_package(GVDB) 182 | 183 | if ( GVDB_FOUND ) 184 | message( STATUS "--> Using package GVDB. ${GVDB_LIB_DIR} ") 185 | include_directories( ${GVDB_INCLUDE_DIR} ) 186 | add_definitions(-DUSE_GVDB) 187 | add_definitions(-DSIM_CODE) 188 | if (WIN32) 189 | LIST(APPEND LIBRARIES_OPTIMIZED ${GVDB_LIB_DIR}/${GVDB_LIB} ) 190 | LIST(APPEND LIBRARIES_DEBUG ${GVDB_LIB_DIR}/${GVDB_LIB} ) 191 | else () 192 | find_library( LIBGVDB gvdb HINTS ${GVDB_LIB_DIR} ) 193 | LIST(APPEND LIBRARIES_OPTIMIZED ${LIBGVDB} ) 194 | LIST(APPEND LIBRARIES_DEBUG ${LIBGVDB} ) 195 | endif() 196 | LIST(APPEND PACKAGE_SOURCE_FILES ${GVDB_INCLUDE_DIR}/${GVDB_HEADERS} ) 197 | source_group(GVDB FILES ${GVDB_INCLUDE_DIR}/${GVDB_HEADERS} ) 198 | else() 199 | message( FATAL_ERROR "--> Unable to find GVDB") 200 | endif() 201 | 202 | endmacro() 203 | 204 | 205 | ##################################################################################### 206 | # Optional ZLIB 207 | # 208 | macro(_add_package_ZLIB) 209 | if(EXISTS ${BASE_DIRECTORY}/zlib) 210 | set(ZLIB_ROOT ${BASE_DIRECTORY}/zlib) 211 | endif() 212 | Message(STATUS "--> using package ZLIB") 213 | find_package(ZLIB) 214 | if(ZLIB_FOUND) 215 | include_directories(${ZLIB_INCLUDE_DIR}) 216 | LIST(APPEND PACKAGE_SOURCE_FILES 217 | ${ZLIB_HEADERS} 218 | ) 219 | LIST(APPEND LIBRARIES_OPTIMIZED ${ZLIB_LIBRARY}) 220 | LIST(APPEND LIBRARIES_DEBUG ${ZLIB_LIBRARY}) 221 | else() 222 | Message(WARNING "ZLIB not available. setting NOGZLIB define") 223 | add_definitions(-DNOGZLIB) 224 | endif() 225 | endmacro() 226 | 227 | ##################################################################################### 228 | # Optional OpenVDB 229 | # 230 | macro(_add_package_OpenVDB) 231 | 232 | Message(STATUS "\n<-- Searching for OpenVDB") 233 | 234 | _FIND_LOCALPACKAGE_OPENVDB () 235 | 236 | if ( NOT DEFINED USE_OPENVDB ) 237 | SET(USE_OPENVDB OFF CACHE BOOL "Use OpenVDB") 238 | endif () 239 | 240 | find_package(OpenVDB) 241 | if (OPENVDB_FOUND) 242 | if ( NOT DEFINED USE_OPENVDB ) 243 | SET(USE_OPENVDB ON CACHE BOOL "Use OpenVDB") 244 | endif() 245 | if ( USE_OPENVDB ) 246 | Message(STATUS "--> Using package OpenVDB") 247 | 248 | add_definitions(-DUSEOPENVDB) 249 | add_definitions(-DOPENEXR_DLL) 250 | add_definitions(-DOPENVDB_3_ABI_COMPATIBLE) 251 | add_definitions(-DBUILD_OPENVDB) 252 | add_definitions(-DOPENVDB_STATICLIB) 253 | add_definitions(-DOPENVDB_USE_BLOSC) 254 | 255 | message ( STATUS "Adding OpenVDB includes: ${OPENVDB_INCLUDE_DIR}" ) 256 | include_directories(${OPENVDB_INCLUDE_DIR}) 257 | include_directories ("${OPENVDB_INCLUDE_DIR}") 258 | include_directories ("${OPENVDB_INCLUDE_DIR}/IlmBase") 259 | include_directories ("${OPENVDB_INCLUDE_DIR}/tbb") 260 | LIST(APPEND LIBRARIES_OPTIMIZED ${OPENVDB_LIB_RELEASE} ) 261 | LIST(APPEND LIBRARIES_DEBUG ${OPENVDB_LIB_DEBUG} ) 262 | 263 | if ( OPENVDB_LIB_DIR STREQUAL "" ) 264 | message ( FATAL_ERROR "OpenVDB package found, but OpenVDB library directory not found. OPENVDB_LIB_DIR." ) 265 | endif () 266 | 267 | if (MSVC_VERSION EQUAL 1600) 268 | set ( MSVCX "vc10" ) 269 | endif() 270 | if (MSVC_VERSION EQUAL 1700) 271 | set ( MSVCX "vc11" ) 272 | endif() 273 | if (MSVC_VERSION EQUAL 1800) 274 | set ( MSVCX "vc12" ) 275 | endif() 276 | if (MSVC_VERSION EQUAL 1900) 277 | set ( MSVCX "vc14" ) 278 | endif() 279 | 280 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/Blosc.lib" ) 281 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/Blosc.lib" ) 282 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/Half.lib" ) 283 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/Half.lib" ) 284 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/zlib.lib" ) 285 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/zlibd.lib" ) 286 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/tbb.lib" ) 287 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/tbb_debug.lib" ) 288 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/boost_system-${MSVCX}0-mt-1_64.lib" ) 289 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/boost_system-${MSVCX}0-mt-gd-1_64.lib" ) 290 | LIST(APPEND LIBRARIES_OPTIMIZED "${OPENVDB_LIB_DIR}/boost_thread-${MSVCX}0-mt-1_64.lib" ) 291 | LIST(APPEND LIBRARIES_DEBUG "${OPENVDB_LIB_DIR}/boost_thread-${MSVCX}0-mt-gd-1_64.lib" ) 292 | 293 | LIST(APPEND PACKAGE_SOURCE_FILES ${OPENVDB_HEADERS} ) 294 | endif () 295 | else() 296 | SET(USE_OPENVDB OFF CACHE BOOL "Use OpenVDB" FORCE) 297 | endif() 298 | endmacro() 299 | 300 | 301 | ##################################################################################### 302 | # Optional Utils package 303 | # 304 | macro(_add_package_Utils) 305 | 306 | find_package(Utils) 307 | 308 | if ( UTILS_FOUND ) 309 | 310 | if (WIN32) 311 | # Windows platform 312 | if(REQUIRE_OPENGL) 313 | add_definitions(-DBUILD_OPENGL) 314 | set(PLATFORM_LIBRARIES ${OPENGL_LIBRARY} ) 315 | endif() 316 | else() 317 | # Linux platform 318 | if(REQUIRE_OPENGL) 319 | add_definitions(-DBUILD_OPENGL) 320 | set(PLATFORM_LIBRARIES GL GLU GLEW X11) 321 | endif() 322 | if(USE_NVTX) 323 | find_library(LIBNVTX nvToolsExt HINTS ${CUDA_TOOLKIT_ROOT_DIR}/lib64) 324 | LIST(APPEND LIBRARIES_OPTIMIZED ${LIBNVTX}) 325 | LIST(APPEND LIBRARIES_DEBUG {LIBNVTX}) 326 | endif() 327 | endif() 328 | endif() 329 | 330 | endmacro() 331 | 332 | ##################################################################################### 333 | # Optional CUDA package 334 | # 335 | 336 | macro(_set_cuda_suffix) 337 | #------ CUDA VERSION 338 | if ( CUDA_VERSION ) 339 | if ( ${CUDA_VERSION} EQUAL "8.0" ) 340 | SET ( CUDA_SUFFIX "cu8" ) 341 | endif () 342 | if ( ${CUDA_VERSION} EQUAL "9.0" ) 343 | SET ( CUDA_SUFFIX "cu9" ) 344 | endif () 345 | if ( ${CUDA_VERSION} EQUAL "9.1" ) 346 | SET ( CUDA_SUFFIX "cu9" ) 347 | endif () 348 | else() 349 | message ( FATAL_ERROR "\nNVIDIA CUDA not found.\n" ) 350 | endif() 351 | endmacro() 352 | 353 | macro(_add_package_CUDA) 354 | 355 | Message(STATUS "\n<-- Searching for CUDA") 356 | 357 | _FIND_LOCALPACKAGE_CUDA () 358 | 359 | find_package(CUDA) 360 | 361 | if ( CUDA_FOUND ) 362 | _set_cuda_suffix() 363 | message( STATUS "--> Using package CUDA (ver ${CUDA_VERSION})") 364 | add_definitions(-DUSECUDA) 365 | include_directories(${CUDA_TOOLKIT_INCLUDE}) 366 | if (WIN32) 367 | LIST(APPEND LIBRARIES_OPTIMIZED ${CUDA_CUDA_LIBRARY} ${CUDA_CUDART_LIBRARY} ) 368 | LIST(APPEND LIBRARIES_DEBUG ${CUDA_CUDA_LIBRARY} ${CUDA_CUDART_LIBRARY} ) 369 | else() 370 | find_library(LIBCUDA cuda HINTS ${CUDA_TOOLKIT_ROOT_DIR}/lib64) 371 | find_library(LIBCUDART cudart HINTS ${CUDA_TOOLKIT_ROOT_DIR}/lib64) 372 | LIST(APPEND LIBRARIES_OPTIMIZED ${LIBCUDA}) 373 | LIST(APPEND LIBRARIES_DEBUG ${LIBCUDART}) 374 | endif() 375 | LIST(APPEND PACKAGE_SOURCE_FILES ${CUDA_TOOLKIT_INCLUDE} ) 376 | source_group(CUDA FILES ${CUDA_TOOLKIT_INCLUDE} ) 377 | else() 378 | message ( FATAL_ERROR "---> Unable to find package CUDA") 379 | endif() 380 | 381 | endmacro() 382 | 383 | ##################################################################################### 384 | # Optional CUDPP package 385 | # 386 | macro(_add_package_CUDPP) 387 | 388 | Message(STATUS "\n<-- Searching for CUDPP") 389 | 390 | find_package(CUDPP) 391 | 392 | if (CUDPP_FOUND) 393 | if ( NOT DEFINED USE_CUDPP ) 394 | SET(USE_CUDPP ON CACHE BOOL "Use CUDPP") 395 | endif() 396 | if ( USE_CUDPP ) 397 | Message(STATUS "--> Using package CUDPP") 398 | add_definitions(-DUSE_CUDPP) 399 | include_directories( ${CUDPP_INCLUDE_DIR} ) 400 | link_directories( ${CUDPP_LIB_DIR} ) 401 | if (WIN32) 402 | LIST(APPEND LIBRARIES_OPTIMIZED ${CUDPP_LIB1_REL} ) 403 | LIST(APPEND LIBRARIES_OPTIMIZED ${CUDPP_LIB2_REL} ) 404 | LIST(APPEND LIBRARIES_DEBUG ${CUDPP_LIB1_DEBUG} ) 405 | LIST(APPEND LIBRARIES_DEBUG ${CUDPP_LIB2_DEBUG} ) 406 | else() 407 | find_library(LIBCUDPP cudpp HINTS ${GVDB_LIB_DIR} ) 408 | find_library(LIBCUDPP_HASH cudpp_hash HINTS ${GVDB_LIB_DIR} ) 409 | LIST(APPEND LIBRARIES_OPTIMIZED ${LIBCUDPP} ${LIBCUDPP_HASH} ) 410 | LIST(APPEND LIBRARIES_DEBUG ${LIBCUDPP} ${LIBCUDPP_HASH} ) 411 | endif() 412 | LIST(APPEND PACKAGE_SOURCE_FILES ${CUDPP_INCLUDE_DIR}/${CUDPP_HEADERS} ) 413 | else() 414 | Message(FATAL_ERROR "--> NOT USING package CUDPP. Set USE_CUDPP true.") 415 | endif () 416 | else () 417 | SET(USE_CUDPP OFF CACHE BOOL "Use CUDPP") 418 | endif() 419 | 420 | endmacro() 421 | 422 | ##################################################################################### 423 | # Optional OptiX package 424 | # 425 | macro(_add_package_Optix) 426 | 427 | Message(STATUS "<-- Searching for package OptiX") 428 | 429 | _FIND_LOCALPACKAGE_OPTIX() 430 | 431 | find_package(Optix) 432 | 433 | if (OPTIX_FOUND) 434 | if ( NOT DEFINED USE_OPTIX ) 435 | SET(USE_OPTIX ON CACHE BOOL "Use OPTIX") 436 | endif() 437 | if ( USE_OPTIX ) 438 | add_definitions(-DUSE_OPTIX) 439 | add_definitions(-DBUILD_OPTIX) 440 | include_directories(${OPTIX_INCLUDE_DIR}) 441 | if (WIN32) 442 | LIST(APPEND LIBRARIES_OPTIMIZED ${OPTIX_LIB_DIR}/${OPTIX_LIB1} ) 443 | LIST(APPEND LIBRARIES_OPTIMIZED ${OPTIX_LIB_DIR}/${OPTIX_LIB2} ) 444 | LIST(APPEND LIBRARIES_DEBUG ${OPTIX_LIB_DIR}/${OPTIX_LIB1} ) 445 | LIST(APPEND LIBRARIES_DEBUG ${OPTIX_LIB_DIR}/${OPTIX_LIB2} ) 446 | else() 447 | find_library(LIBOPTIX optix HINTS ${OPTIX_LIB_DIR}) 448 | find_library(LIBOPTIXU optixu HINTS ${OPTIX_LIB_DIR}) 449 | LIST(APPEND LIBRARIES_OPTIMIZED ${LIBOPTIX} ${LIBOPTIXU} ) 450 | LIST(APPEND LIBRARIES_DEBUG ${LIBOPTIX} ${LIBOPTIXU} ) 451 | endif() 452 | LIST(APPEND PACKAGE_SOURCE_FILES ${OPTIX_INCLUDE_DIR}/${OPTIX_HEADERS} ) 453 | 454 | _COMPILEPTX ( SOURCES ${UTIL_OPTIX_KERNELS} TARGET_PATH ${EXECUTABLE_OUTPUT_PATH} GENERATED UTIL_OPTIX_PTX GENPATHS UTIL_OPTIX_PTX_PATHS INCLUDE "${OPTIX_ROOT_DIR}/include/,${GVDB_INCLUDE_DIR},${CMAKE_CURRENT_SOURCE_DIR}" OPTIONS -arch=compute_30 -code=sm_30 --ptxas-options=-v -O3 --use_fast_math --maxrregcount=128 ) 455 | message (STATUS " OptiX FILES: ${UTIL_OPTIX_FILES}") 456 | message (STATUS " OptiX KERNELS: ${UTIL_OPTIX_KERNELS}") 457 | message (STATUS " OptiX PTX: ${UTIL_OPTIX_PTX}") 458 | Message(STATUS "--> Using package OptiX\n") 459 | 460 | endif() 461 | else() 462 | Message(STATUS "Warning: OptiX not found. The gInteractiveOptix sample will not work without OptiX.") 463 | SET(USE_OPTIX OFF CACHE BOOL "Use Optix" FORCE) 464 | endif() 465 | endmacro() 466 | 467 | ##################################################################################### 468 | # 469 | # 470 | set(TINYTHREADPP_DIRECTORY ${BASE_DIRECTORY}/tinythreadpp) 471 | 472 | macro(_add_package_tinythreadpp) 473 | Message(STATUS "--> using package TinyThread++") 474 | include_directories(${TINYTHREADPP_DIRECTORY}/src) 475 | set(PROJECT_TINYTHREADPP ${TINYTHREADPP_DIRECTORY}/src/fast_mutex.h ${TINYTHREADPP_DIRECTORY}/src/tinythread.h ${TINYTHREADPP_DIRECTORY}/src/tinythread.cpp) 476 | 477 | LIST(APPEND PACKAGE_SOURCE_FILES 478 | ${PROJECT_TINYTHREADPP} 479 | ) 480 | endmacro(_add_package_tinythreadpp) 481 | 482 | ##################################################################################### 483 | # Glew : source or lib 484 | if(GLEW_SOURCE) 485 | message(STATUS "found Glew source code. Using it instead of library") 486 | add_definitions(-DGLEW_STATIC) 487 | else() 488 | message(STATUS "using GLEW library") 489 | LIST(APPEND PLATFORM_LIBRARIES ${GLEW_LIBRARY}) 490 | endif() 491 | add_definitions(-DGLEW_NO_GLU) 492 | 493 | #################################################################################### 494 | # XF86 495 | if (UNIX) 496 | LIST(APPEND PLATFORM_LIBRARIES "Xxf86vm") 497 | endif() 498 | 499 | ##################################################################################### 500 | # NSight 501 | # 502 | # still need the include directory when no use of NSIGHT: for empty #defines 503 | macro(_add_package_NSight) 504 | Message(STATUS "--> using package NSight") 505 | include_directories( 506 | ${BASE_DIRECTORY}/NSight 507 | ) 508 | if(SUPPORT_NVTOOLSEXT) 509 | link_directories( 510 | ${BASE_DIRECTORY}/NSight 511 | ) 512 | LIST(APPEND PACKAGE_SOURCE_FILES 513 | ${BASE_DIRECTORY}/NSight/NSightEvents.h 514 | ${BASE_DIRECTORY}/NSight/nvToolsExt.h 515 | ) 516 | add_definitions(-DSUPPORT_NVTOOLSEXT) 517 | if(ARCH STREQUAL "x86") 518 | SET(NSIGHT_DLL ${BASE_DIRECTORY}/NSight/nvToolsExt32_1.dll) 519 | SET(NSIGHT_LIB ${BASE_DIRECTORY}/NSight/nvToolsExt32_1.lib) 520 | else() 521 | SET(NSIGHT_DLL ${BASE_DIRECTORY}/NSight/nvToolsExt64_1.dll) 522 | SET(NSIGHT_LIB ${BASE_DIRECTORY}/NSight/nvToolsExt64_1.lib) 523 | endif() 524 | LIST(APPEND LIBRARIES_OPTIMIZED ${NSIGHT_LIB}) 525 | LIST(APPEND LIBRARIES_DEBUG ${NSIGHT_LIB}) 526 | endif() 527 | endmacro() 528 | 529 | ## -- no shared_sources to include (-Rama) 530 | # include_directories( 531 | # ${BASE_DIRECTORY}/shared_sources 532 | # ) 533 | 534 | ##################################################################################### 535 | # Macro to download a file from a URL 536 | # 537 | macro(_download_file _URL _TARGET _FORCE) 538 | if(${_FORCE} OR (NOT EXISTS ${_TARGET})) 539 | Message(STATUS "downloading ${_URL} ==> ${_TARGET}") 540 | file(DOWNLOAD ${_URL} ${_TARGET} SHOW_PROGRESS) 541 | else() 542 | Message(STATUS "model ${_TARGET} already loaded...") 543 | endif() 544 | endmacro() 545 | # 546 | # example: _download_files("${FILELIST}" "http://..." "${BASE_DIRECTORY}/shared_external/..." ${MODELS_DOWNLOAD_FORCE} ) 547 | # 548 | macro(_download_files _FILELIST _URL _TARGET _FORCE ) 549 | foreach(_FILE ${_FILELIST}) 550 | if(${_FORCE} OR (NOT EXISTS "${_TARGET}/${_FILE}")) 551 | Message(STATUS "*******************************************") 552 | Message(STATUS "downloading ${_URL}/${_FILE}\n ==>\n ${_TARGET}") 553 | Message(STATUS "*******************************************") 554 | file(DOWNLOAD ${_URL}/${_FILE} ${_TARGET}/${_FILE} SHOW_PROGRESS) 555 | else() 556 | Message(STATUS "model ${_FILE} already loaded...") 557 | endif() 558 | endforeach(_FILE) 559 | endmacro() 560 | -------------------------------------------------------------------------------- /helpers/camera.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | 23 | #include "camera.h" 24 | 25 | // Camera3D IMPLEMENTATION 26 | // 27 | // The Camera3D transformation of an arbitrary point is: 28 | // 29 | // Q' = Q * T * R * P 30 | // 31 | // where Q = 3D point 32 | // Q' = Screen point 33 | // T = Camera3D Translation (moves Camera3D to origin) 34 | // R = Camera3D Rotation (rotates Camera3D to point 'up' along Z axis) 35 | // P = Projection (projects points onto XY plane) 36 | // 37 | // T is a unit-coordinate system translated to origin from Camera3D: 38 | // [1 0 0 0] 39 | // [0 1 0 0] 40 | // [0 0 1 0] 41 | // [-cx -cy -cz 0] where c is Camera3D location 42 | // R is a basis matrix: 43 | // 44 | // P is a projection matrix: 45 | // 46 | 47 | Camera3D::Camera3D () 48 | { 49 | 50 | mProjType = Perspective; 51 | mWire = 0; 52 | 53 | up_dir.Set ( 0.0, 1.0, 0 ); // frustum params 54 | mAspect = (float) 800.0f/600.0f; 55 | mDolly = 5.0; 56 | mFov = 40.0; 57 | mNear = (float) 0.1; 58 | mFar = (float) 6000.0; 59 | mTile.Set ( 0, 0, 1, 1 ); 60 | 61 | for (int n=0; n < 8; n++ ) mOps[n] = false; 62 | mOps[0] = false; 63 | 64 | // mOps[0] = true; 65 | // mOps[1] = true; 66 | 67 | setOrbit ( 0, 45, 0, Vector3DF(0,0,0), 120.0, 1.0 ); 68 | updateMatricies (); 69 | } 70 | 71 | /*void Camera3D::draw_gl () 72 | { 73 | Vector3DF pnt; 74 | int va, vb; 75 | 76 | if ( !mOps[0] ) return; 77 | 78 | // Box testing 79 | // 80 | // NOTES: This demonstrates AABB box testing against the frustum 81 | // Boxes tested are 10x10x10 size, spaced apart from each other so we can see them. 82 | if ( mOps[5] ) { 83 | glPushMatrix (); 84 | glEnable ( GL_LIGHTING ); 85 | glColor3f ( 1, 1, 1 ); 86 | Vector3DF bmin, bmax, vmin, vmax; 87 | int lod; 88 | for (float y=0; y < 100; y += 10.0 ) { 89 | for (float z=-100; z < 100; z += 10.0 ) { 90 | for (float x=-100; x < 100; x += 10.0 ) { 91 | bmin.Set ( x, y, z ); 92 | bmax.Set ( x+8, y+8, z+8 ); 93 | if ( boxInFrustum ( bmin, bmax ) ) { 94 | lod = (int) calculateLOD ( bmin, 1, 5, 300.0 ); 95 | //rendGL->drawCube ( bmin, bmax, Vector3DF(1,1,1) ); 96 | } 97 | } 98 | } 99 | } 100 | glPopMatrix (); 101 | } 102 | 103 | glDisable ( GL_LIGHTING ); 104 | glLoadMatrixf ( getViewMatrix().GetDataF() ); 105 | 106 | // Frustum planes (world space) 107 | // 108 | // NOTE: The frustum planes are drawn as discs because 109 | // they are boundless (infinite). The minimum information contained in the 110 | // plane equation is normal direction and distance from plane to origin. 111 | // This sufficiently defines infinite planes for inside/outside testing, 112 | // but cannot be used to draw the view frustum without more information. 113 | // Drawing is done as discs here to verify the frustum plane equations. 114 | if ( mOps[2] ) { 115 | glBegin ( GL_POINTS ); 116 | glColor3f ( 1, 1, 0 ); 117 | Vector3DF norm; 118 | Vector3DF side, up; 119 | for (int n=0; n < 6; n++ ) { 120 | norm.Set ( frustum[n][0], frustum[n][1], frustum[n][2] ); 121 | glColor3f ( n/6.0, 1.0- (n/6.0), 0.5 ); 122 | side = Vector3DF(0,1,0); side.Cross ( norm ); side.Normalize (); 123 | up = side; up.Cross ( norm ); up.Normalize(); 124 | norm *= frustum[n][3]; 125 | for (float y=-50; y < 50; y += 1.0 ) { 126 | for (float x=-50; x < 50; x += 1.0 ) { 127 | if ( x*x+y*y < 1000 ) { 128 | //pnt = side * x + up * y - norm; 129 | pnt = side; 130 | Vector3DF tv = up; 131 | 132 | tv *= y; 133 | pnt *= x; 134 | pnt += tv; 135 | pnt -= norm; 136 | 137 | glVertex3f ( pnt.x, pnt.y, pnt.z ); 138 | } 139 | } 140 | } 141 | } 142 | glEnd (); 143 | } 144 | 145 | // Inside/outside testing 146 | // 147 | // NOTES: This code demonstrates frustum clipping 148 | // tests on individual points. 149 | if ( mOps[4] ) { 150 | glColor3f ( 1, 1, 1 ); 151 | glBegin ( GL_POINTS ); 152 | for (float z=-100; z < 100; z += 4.0 ) { 153 | for (float y=0; y < 100; y += 4.0 ) { 154 | for (float x=-100; x < 100; x += 4.0 ) { 155 | if ( pointInFrustum ( x, y, z) ) { 156 | glVertex3f ( x, y, z ); 157 | } 158 | } 159 | } 160 | } 161 | glEnd (); 162 | } 163 | 164 | // Inverse rays (world space) 165 | // 166 | // NOTES: This code demonstrates drawing 167 | // inverse camera rays, as might be needed for raytracing or hit testing. 168 | if ( mOps[3] ) { 169 | glBegin ( GL_LINES ); 170 | glColor3f ( 0, 1, 0); 171 | for (float x = 0; x <= 1.0; x+= 0.5 ) { 172 | for (float y = 0; y <= 1.0; y+= 0.5 ) { 173 | pnt = inverseRay ( x, y, mFar ); 174 | pnt += from_pos; 175 | glVertex3f ( from_pos.x, from_pos.y, from_pos.z ); // all inverse rays originate at the camera center 176 | glVertex3f ( pnt.x, pnt.y, pnt.z ); 177 | } 178 | } 179 | glEnd (); 180 | } 181 | 182 | // Projection 183 | // 184 | // NOTES: This code demonstrates 185 | // perspective projection _without_ using the OpenGL pipeline. 186 | // Projection is done by the camera class. A cube is drawn on the near plane. 187 | 188 | // Cube geometry 189 | Vector3DF pnts[8]; 190 | Vector3DI edge[12]; 191 | pnts[0].Set ( 0, 0, 0 ); pnts[1].Set ( 10, 0, 0 ); pnts[2].Set ( 10, 0, 10 ); pnts[3].Set ( 0, 0, 10 ); // lower points (y=0) 192 | pnts[4].Set ( 0, 10, 0 ); pnts[5].Set ( 10, 10, 0 ); pnts[6].Set ( 10, 10, 10 ); pnts[7].Set ( 0, 10, 10 ); // upper points (y=10) 193 | edge[0].Set ( 0, 1, 0 ); edge[1].Set ( 1, 2, 0 ); edge[2].Set ( 2, 3, 0 ); edge[3].Set ( 3, 0, 0 ); // 4 lower edges 194 | edge[4].Set ( 4, 5, 0 ); edge[5].Set ( 5, 6, 0 ); edge[6].Set ( 6, 7, 0 ); edge[7].Set ( 7, 4, 0 ); // 4 upper edges 195 | edge[8].Set ( 0, 4, 0 ); edge[9].Set ( 1, 5, 0 ); edge[10].Set ( 2, 6, 0 ); edge[11].Set ( 3, 7, 0 ); // 4 vertical edges 196 | 197 | // -- White cube is drawn using OpenGL projection 198 | if ( mOps[6] ) { 199 | glBegin ( GL_LINES ); 200 | glColor3f ( 1, 1, 1); 201 | for (int e = 0; e < 12; e++ ) { 202 | va = edge[e].x; 203 | vb = edge[e].y; 204 | glVertex3f ( pnts[va].x, pnts[va].y, pnts[va].z ); 205 | glVertex3f ( pnts[vb].x, pnts[vb].y, pnts[vb].z ); 206 | } 207 | glEnd (); 208 | } 209 | 210 | //---- Draw the following in camera space.. 211 | // NOTES: 212 | // The remainder drawing steps are done in 213 | // camera space. This is done by multiplying by the 214 | // inverse_rotation matrix, which transforms from camera to world space. 215 | // The camera axes, near, and far planes can now be drawn in camera space. 216 | glPushMatrix (); 217 | glLoadMatrixf ( getViewMatrix().GetDataF() ); 218 | glTranslatef ( from_pos.x, from_pos.y, from_pos.z ); 219 | glMultMatrixf ( invrot_matrix.GetDataF() ); // camera space --to--> world space 220 | 221 | // -- Red cube is drawn on the near plane using software projection pipeline. See Camera3D::project 222 | if ( mOps[6] ) { 223 | glBegin ( GL_LINES ); 224 | glColor3f ( 1, 0, 0); 225 | Vector4DF proja, projb; 226 | for (int e = 0; e < 12; e++ ) { 227 | va = edge[e].x; 228 | vb = edge[e].y; 229 | proja = project ( pnts[va] ); 230 | projb = project ( pnts[vb] ); 231 | if ( proja.w > 0 && projb.w > 0 && proja.w < 1 && projb.w < 1) { // Very simple Z clipping (try commenting this out and see what happens) 232 | glVertex3f ( proja.x, proja.y, proja.z ); 233 | glVertex3f ( projb.x, projb.y, projb.z ); 234 | } 235 | } 236 | glEnd (); 237 | } 238 | // Camera axes 239 | glBegin ( GL_LINES ); 240 | float to_d = (from_pos - to_pos).Length(); 241 | glColor3f ( .8,.8,.8); glVertex3f ( 0, 0, 0 ); glVertex3f ( 0, 0, -to_d ); 242 | glColor3f ( 1,0,0); glVertex3f ( 0, 0, 0 ); glVertex3f ( 10, 0, 0 ); 243 | glColor3f ( 0,1,0); glVertex3f ( 0, 0, 0 ); glVertex3f ( 0, 10, 0 ); 244 | glColor3f ( 0,0,1); glVertex3f ( 0, 0, 0 ); glVertex3f ( 0, 0, 10 ); 245 | glEnd (); 246 | 247 | if ( mOps[1] ) { 248 | // Near plane 249 | float sy = tan ( mFov * DEGtoRAD / 2.0); 250 | float sx = sy * mAspect; 251 | glColor3f ( 0.8, 0.8, 0.8 ); 252 | glBegin ( GL_LINE_LOOP ); 253 | glVertex3f ( -mNear*sx, mNear*sy, -mNear ); 254 | glVertex3f ( mNear*sx, mNear*sy, -mNear ); 255 | glVertex3f ( mNear*sx, -mNear*sy, -mNear ); 256 | glVertex3f ( -mNear*sx, -mNear*sy, -mNear ); 257 | glEnd (); 258 | // Far plane 259 | glBegin ( GL_LINE_LOOP ); 260 | glVertex3f ( -mFar*sx, mFar*sy, -mFar ); 261 | glVertex3f ( mFar*sx, mFar*sy, -mFar ); 262 | glVertex3f ( mFar*sx, -mFar*sy, -mFar ); 263 | glVertex3f ( -mFar*sx, -mFar*sy, -mFar ); 264 | glEnd (); 265 | 266 | // Subview Near plane 267 | float l, r, t, b; 268 | l = -sx + 2.0*sx*mTile.x; // Tile is in range 0 <= x,y <= 1 269 | r = -sx + 2.0*sx*mTile.z; 270 | t = sy - 2.0*sy*mTile.y; 271 | b = sy - 2.0*sy*mTile.w; 272 | glColor3f ( 0.8, 0.8, 0.0 ); 273 | glBegin ( GL_LINE_LOOP ); 274 | glVertex3f ( l * mNear, t * mNear, -mNear ); 275 | glVertex3f ( r * mNear, t * mNear, -mNear ); 276 | glVertex3f ( r * mNear, b * mNear, -mNear ); 277 | glVertex3f ( l * mNear, b * mNear, -mNear ); 278 | glEnd (); 279 | // Subview Far plane 280 | glBegin ( GL_LINE_LOOP ); 281 | glVertex3f ( l * mFar, t * mFar, -mFar ); 282 | glVertex3f ( r * mFar, t * mFar, -mFar ); 283 | glVertex3f ( r * mFar, b * mFar, -mFar ); 284 | glVertex3f ( l * mFar, b * mFar, -mFar ); 285 | glEnd (); 286 | } 287 | 288 | glPopMatrix (); 289 | } 290 | */ 291 | 292 | bool Camera3D::pointInFrustum ( float x, float y, float z ) 293 | { 294 | int p; 295 | for ( p = 0; p < 6; p++ ) 296 | if( frustum[p][0] * x + frustum[p][1] * y + frustum[p][2] * z + frustum[p][3] <= 0 ) 297 | return false; 298 | return true; 299 | } 300 | 301 | bool Camera3D::boxInFrustum ( Vector3DF bmin, Vector3DF bmax) 302 | { 303 | Vector3DF vmin, vmax; 304 | int p; 305 | bool ret = true; 306 | for ( p = 0; p < 6; p++ ) { 307 | vmin.x = ( frustum[p][0] > 0 ) ? bmin.x : bmax.x; // Determine nearest and farthest point to plane 308 | vmax.x = ( frustum[p][0] > 0 ) ? bmax.x : bmin.x; 309 | vmin.y = ( frustum[p][1] > 0 ) ? bmin.y : bmax.y; 310 | vmax.y = ( frustum[p][1] > 0 ) ? bmax.y : bmin.y; 311 | vmin.z = ( frustum[p][2] > 0 ) ? bmin.z : bmax.z; 312 | vmax.z = ( frustum[p][2] > 0 ) ? bmax.z : bmin.z; 313 | if ( frustum[p][0]*vmax.x + frustum[p][1]*vmax.y + frustum[p][2]*vmax.z + frustum[p][3] <= 0 ) return false; // If nearest point is outside, Box is outside 314 | else if ( frustum[p][0]*vmin.x + frustum[p][1]*vmin.y + frustum[p][2]*vmin.z + frustum[p][3] <= 0 ) ret = true; // If nearest inside and farthest point is outside, Box intersects 315 | } 316 | return ret; // No points found outside. Box must be inside. 317 | 318 | /* --- Original method - Slow yet simpler. 319 | int p; 320 | for ( p = 0; p < 6; p++ ) { 321 | if( frustum[p][0] * bmin.x + frustum[p][1] * bmin.y + frustum[p][2] * bmin.z + frustum[p][3] > 0 ) continue; 322 | if( frustum[p][0] * bmax.x + frustum[p][1] * bmin.y + frustum[p][2] * bmin.z + frustum[p][3] > 0 ) continue; 323 | if( frustum[p][0] * bmax.x + frustum[p][1] * bmin.y + frustum[p][2] * bmax.z + frustum[p][3] > 0 ) continue; 324 | if( frustum[p][0] * bmin.x + frustum[p][1] * bmin.y + frustum[p][2] * bmax.z + frustum[p][3] > 0 ) continue; 325 | if( frustum[p][0] * bmin.x + frustum[p][1] * bmax.y + frustum[p][2] * bmin.z + frustum[p][3] > 0 ) continue; 326 | if( frustum[p][0] * bmax.x + frustum[p][1] * bmax.y + frustum[p][2] * bmin.z + frustum[p][3] > 0 ) continue; 327 | if( frustum[p][0] * bmax.x + frustum[p][1] * bmax.y + frustum[p][2] * bmax.z + frustum[p][3] > 0 ) continue; 328 | if( frustum[p][0] * bmin.x + frustum[p][1] * bmax.y + frustum[p][2] * bmax.z + frustum[p][3] > 0 ) continue; 329 | return false; 330 | } 331 | return true;*/ 332 | } 333 | 334 | void Camera3D::setOrbit ( Vector3DF angs, Vector3DF tp, float dist, float dolly ) 335 | { 336 | setOrbit ( angs.x, angs.y, angs.z, tp, dist, dolly ); 337 | } 338 | 339 | void Camera3D::setOrbit ( float ax, float ay, float az, Vector3DF tp, float dist, float dolly ) 340 | { 341 | ang_euler.Set ( ax, ay, az ); 342 | mOrbitDist = dist; 343 | mDolly = dolly; 344 | double dx, dy, dz; 345 | dx = cos ( ang_euler.y * DEGtoRAD ) * sin ( ang_euler.x * DEGtoRAD ) ; 346 | dy = sin ( ang_euler.y * DEGtoRAD ); 347 | dz = cos ( ang_euler.y * DEGtoRAD ) * cos ( ang_euler.x * DEGtoRAD ); 348 | from_pos.x = tp.x + (float) dx * mOrbitDist; 349 | from_pos.y = tp.y + (float) dy * mOrbitDist; 350 | from_pos.z = tp.z + (float) dz * mOrbitDist; 351 | to_pos = tp; 352 | //to_pos.x = from_pos.x - (float) dx * mDolly; 353 | //to_pos.y = from_pos.y - (float) dy * mDolly; 354 | //to_pos.z = from_pos.z - (float) dz * mDolly; 355 | updateMatricies (); 356 | } 357 | 358 | void Camera3D::moveOrbit ( float ax, float ay, float az, float dd ) 359 | { 360 | ang_euler += Vector3DF(ax,ay,az); 361 | mOrbitDist += dd; 362 | 363 | double dx, dy, dz; 364 | dx = cos ( ang_euler.y * DEGtoRAD ) * sin ( ang_euler.x * DEGtoRAD ) ; 365 | dy = sin ( ang_euler.y * DEGtoRAD ); 366 | dz = cos ( ang_euler.y * DEGtoRAD ) * cos ( ang_euler.x * DEGtoRAD ); 367 | from_pos.x = to_pos.x + (float) dx * mOrbitDist; 368 | from_pos.y = to_pos.y + (float) dy * mOrbitDist; 369 | from_pos.z = to_pos.z + (float) dz * mOrbitDist; 370 | updateMatricies (); 371 | } 372 | 373 | void Camera3D::moveToPos ( float tx, float ty, float tz ) 374 | { 375 | to_pos += Vector3DF(tx,ty,tz); 376 | 377 | double dx, dy, dz; 378 | dx = cos ( ang_euler.y * DEGtoRAD ) * sin ( ang_euler.x * DEGtoRAD ) ; 379 | dy = sin ( ang_euler.y * DEGtoRAD ); 380 | dz = cos ( ang_euler.y * DEGtoRAD ) * cos ( ang_euler.x * DEGtoRAD ); 381 | from_pos.x = to_pos.x + (float) dx * mOrbitDist; 382 | from_pos.y = to_pos.y + (float) dy * mOrbitDist; 383 | from_pos.z = to_pos.z + (float) dz * mOrbitDist; 384 | updateMatricies (); 385 | } 386 | 387 | void Camera3D::Copy ( Camera3D& op ) 388 | { 389 | mDolly = op.mDolly; 390 | mOrbitDist = op.mOrbitDist; 391 | from_pos = op.from_pos; 392 | to_pos = op.to_pos; 393 | ang_euler = op.ang_euler; 394 | mProjType = op.mProjType; 395 | mAspect = op.mAspect; 396 | mFov = op.mFov; 397 | mNear = op.mNear; 398 | mFar = op.mFar; 399 | updateMatricies (); 400 | } 401 | 402 | void Camera3D::setAngles ( float ax, float ay, float az ) 403 | { 404 | ang_euler = Vector3DF(ax,ay,az); 405 | to_pos.x = from_pos.x - (float) (cos ( ang_euler.y * DEGtoRAD ) * sin ( ang_euler.x * DEGtoRAD ) * mDolly); 406 | to_pos.y = from_pos.y - (float) (sin ( ang_euler.y * DEGtoRAD ) * mDolly); 407 | to_pos.z = from_pos.z - (float) (cos ( ang_euler.y * DEGtoRAD ) * cos ( ang_euler.x * DEGtoRAD ) * mDolly); 408 | updateMatricies (); 409 | } 410 | 411 | 412 | void Camera3D::moveRelative ( float dx, float dy, float dz ) 413 | { 414 | Vector3DF vec ( dx, dy, dz ); 415 | vec *= invrot_matrix; 416 | to_pos += vec; 417 | from_pos += vec; 418 | updateMatricies (); 419 | } 420 | 421 | void Camera3D::setProjection (eProjection proj_type) 422 | { 423 | mProjType = proj_type; 424 | } 425 | 426 | void Camera3D::updateMatricies () 427 | { 428 | Matrix4F basis; 429 | Vector3DF temp; 430 | 431 | // compute camera direction vectors --- MATCHES OpenGL's gluLookAt function (DO NOT MODIFY) 432 | dir_vec = to_pos; // f vector in gluLookAt docs 433 | dir_vec -= from_pos; // eye = from_pos in gluLookAt docs 434 | dir_vec.Normalize (); 435 | side_vec = dir_vec; 436 | side_vec.Cross ( up_dir ); 437 | side_vec.Normalize (); 438 | up_vec = side_vec; 439 | up_vec.Cross ( dir_vec ); 440 | up_vec.Normalize(); 441 | dir_vec *= -1; 442 | 443 | // construct view matrix 444 | rotate_matrix.Basis (side_vec, up_vec, dir_vec ); 445 | view_matrix = rotate_matrix; 446 | view_matrix.PreTranslate ( Vector3DF(-from_pos.x, -from_pos.y, -from_pos.z ) ); 447 | 448 | // construct projection matrix --- MATCHES OpenGL's gluPerspective function (DO NOT MODIFY) 449 | float sx = (float) tan ( mFov * DEGtoRAD/2.0f ) * mNear; 450 | float sy = sx / mAspect; 451 | proj_matrix = 0.0f; 452 | proj_matrix(0,0) = 2.0f*mNear / sx; // matches OpenGL definition 453 | proj_matrix(1,1) = 2.0f*mNear / sy; 454 | proj_matrix(2,2) = -(mFar + mNear)/(mFar - mNear); // C 455 | proj_matrix(2,3) = -(2.0f*mFar * mNear)/(mFar - mNear); // D 456 | proj_matrix(3,2) = -1.0f; 457 | 458 | // construct tile projection matrix --- MATCHES OpenGL's glFrustum function (DO NOT MODIFY) 459 | float l, r, t, b; 460 | l = -sx + 2.0f*sx*mTile.x; // Tile is in range 0 <= x,y <= 1 461 | r = -sx + 2.0f*sx*mTile.z; 462 | t = sy - 2.0f*sy*mTile.y; 463 | b = sy - 2.0f*sy*mTile.w; 464 | tileproj_matrix = 0.0f; 465 | tileproj_matrix(0,0) = 2.0f*mNear / (r - l); 466 | tileproj_matrix(1,1) = 2.0f*mNear / (t - b); 467 | tileproj_matrix(0,2) = (r + l) / (r - l); // A 468 | tileproj_matrix(1,2) = (t + b) / (t - b); // B 469 | tileproj_matrix(2,2) = proj_matrix(2,2); // C 470 | tileproj_matrix(2,3) = proj_matrix(2,3); // D 471 | tileproj_matrix(3,2) = -1.0f; 472 | tileproj_matrix = proj_matrix; 473 | 474 | // construct inverse rotate and inverse projection matrix 475 | Vector3DF tvz(0, 0, 0); 476 | invrot_matrix.InverseView ( view_matrix.GetDataF(), tvz ); // Computed using rule: "Inverse of a basis rotation matrix is its transpose." (So long as translation is taken out) 477 | invproj_matrix.InverseProj ( tileproj_matrix.GetDataF() ); 478 | 479 | Matrix4F view_matrix_notranslation = view_matrix; 480 | view_matrix_notranslation(12) = 0.0f; 481 | view_matrix_notranslation(13) = 0.0f; 482 | view_matrix_notranslation(14) = 0.0f; 483 | 484 | invviewproj_matrix = tileproj_matrix; 485 | invviewproj_matrix *= view_matrix_notranslation; 486 | invviewproj_matrix.InvertTRS(); 487 | updateFrustum(); 488 | } 489 | 490 | void Camera3D::setModelMatrix ( float* mtx ) 491 | { 492 | memcpy ( model_matrix.GetDataF(), mtx, sizeof(float)*16 ); 493 | } 494 | 495 | void Camera3D::setMatrices(const float* view_mtx, const float* proj_mtx) 496 | { 497 | // Assign the matrices we have 498 | view_matrix = Matrix4F(view_mtx); 499 | proj_matrix = Matrix4F(proj_mtx); 500 | 501 | // Assign model matrix 502 | Matrix4F mdl_matrix(view_mtx); 503 | mdl_matrix.InvertTRS(); 504 | 505 | // Extract position 506 | from_pos = Vector3DF(mdl_matrix(0, 3), mdl_matrix(1, 3), mdl_matrix(2, 3)); 507 | 508 | // construct tile projection matrix --- MATCHES OpenGL's glFrustum function (DO NOT MODIFY) 509 | tileproj_matrix = proj_matrix; 510 | 511 | // construct inverse rotate and inverse projection matrix 512 | Vector3DF tvz(0, 0, 0); 513 | invrot_matrix.InverseView(view_matrix.GetDataF(), tvz); 514 | invproj_matrix.InverseProj(tileproj_matrix.GetDataF()); 515 | 516 | Matrix4F view_matrix_notranslation = view_matrix; 517 | view_matrix_notranslation(12) = 0.0f; 518 | view_matrix_notranslation(13) = 0.0f; 519 | view_matrix_notranslation(14) = 0.0f; 520 | 521 | invviewproj_matrix = tileproj_matrix; 522 | invviewproj_matrix *= view_matrix_notranslation; 523 | invviewproj_matrix.InvertTRS(); 524 | 525 | // mFov, mAspect, mNear, mFar 526 | mNear = (2.0f * proj_matrix(2, 3)) / (2.0f * proj_matrix(2, 2) - 2.0f); 527 | mFar = ((proj_matrix(2, 2) - 1.0f) * mNear) / (proj_matrix(2, 2) + 1.0); 528 | float sx = 2.0f * mNear / proj_matrix(0, 0); 529 | float sy = 2.0f * mFar / proj_matrix(1, 1); 530 | mAspect = sx / sy; 531 | mFov = atan(sx / mNear) * 2.0f / DEGtoRAD; 532 | 533 | updateFrustum(); 534 | } 535 | 536 | void Camera3D::setViewMatrix ( float* mtx, float* invmtx ) 537 | { 538 | memcpy ( view_matrix.GetDataF(), mtx, sizeof(float)*16 ); 539 | memcpy ( invrot_matrix.GetDataF(), invmtx, sizeof(float)*16 ); 540 | } 541 | void Camera3D::setProjMatrix ( float* mtx, float* invmtx ) 542 | { 543 | memcpy ( proj_matrix.GetDataF(), mtx, sizeof(float)*16 ); 544 | memcpy ( tileproj_matrix.GetDataF(), mtx, sizeof(float)*16 ); 545 | memcpy ( invproj_matrix.GetDataF(), invmtx, sizeof(float)*16 ); 546 | } 547 | 548 | void Camera3D::updateFrustum () 549 | { 550 | Matrix4F mv; 551 | mv = tileproj_matrix; // Compute the model-view-projection matrix 552 | mv *= view_matrix; 553 | float* mvm = mv.GetDataF(); 554 | float t; 555 | 556 | // Right plane 557 | frustum[0][0] = mvm[ 3] - mvm[ 0]; 558 | frustum[0][1] = mvm[ 7] - mvm[ 4]; 559 | frustum[0][2] = mvm[11] - mvm[ 8]; 560 | frustum[0][3] = mvm[15] - mvm[12]; 561 | t = sqrt( frustum[0][0] * frustum[0][0] + frustum[0][1] * frustum[0][1] + frustum[0][2] * frustum[0][2] ); 562 | frustum[0][0] /= t; frustum[0][1] /= t; frustum[0][2] /= t; frustum[0][3] /= t; 563 | // Left plane 564 | frustum[1][0] = mvm[ 3] + mvm[ 0]; 565 | frustum[1][1] = mvm[ 7] + mvm[ 4]; 566 | frustum[1][2] = mvm[11] + mvm[ 8]; 567 | frustum[1][3] = mvm[15] + mvm[12]; 568 | t = sqrt( frustum[1][0] * frustum[1][0] + frustum[1][1] * frustum[1][1] + frustum[1][2] * frustum[1][2] ); 569 | frustum[1][0] /= t; frustum[1][1] /= t; frustum[1][2] /= t; frustum[1][3] /= t; 570 | // Bottom plane 571 | frustum[2][0] = mvm[ 3] + mvm[ 1]; 572 | frustum[2][1] = mvm[ 7] + mvm[ 5]; 573 | frustum[2][2] = mvm[11] + mvm[ 9]; 574 | frustum[2][3] = mvm[15] + mvm[13]; 575 | t = sqrt( frustum[2][0] * frustum[2][0] + frustum[2][1] * frustum[2][1] + frustum[2][2] * frustum[2][2] ); 576 | frustum[2][0] /= t; frustum[2][1] /= t; frustum[2][2] /= t; frustum[2][3] /= t; 577 | // Top plane 578 | frustum[3][0] = mvm[ 3] - mvm[ 1]; 579 | frustum[3][1] = mvm[ 7] - mvm[ 5]; 580 | frustum[3][2] = mvm[11] - mvm[ 9]; 581 | frustum[3][3] = mvm[15] - mvm[13]; 582 | t = sqrt( frustum[3][0] * frustum[3][0] + frustum[3][1] * frustum[3][1] + frustum[3][2] * frustum[3][2] ); 583 | frustum[3][0] /= t; frustum[3][1] /= t; frustum[3][2] /= t; frustum[3][3] /= t; 584 | // Far plane 585 | frustum[4][0] = mvm[ 3] - mvm[ 2]; 586 | frustum[4][1] = mvm[ 7] - mvm[ 6]; 587 | frustum[4][2] = mvm[11] - mvm[10]; 588 | frustum[4][3] = mvm[15] - mvm[14]; 589 | t = sqrt( frustum[4][0] * frustum[4][0] + frustum[4][1] * frustum[4][1] + frustum[4][2] * frustum[4][2] ); 590 | frustum[4][0] /= t; frustum[4][1] /= t; frustum[4][2] /= t; frustum[4][3] /= t; 591 | // Near plane 592 | frustum[5][0] = mvm[ 3] + mvm[ 2]; 593 | frustum[5][1] = mvm[ 7] + mvm[ 6]; 594 | frustum[5][2] = mvm[11] + mvm[10]; 595 | frustum[5][3] = mvm[15] + mvm[14]; 596 | t = sqrt( frustum[5][0] * frustum[5][0] + frustum[5][1] * frustum[5][1] + frustum[5][2] * frustum[5][2] ); 597 | frustum[5][0] /= t; frustum[5][1] /= t; frustum[5][2] /= t; frustum[5][3] /= t; 598 | 599 | tlRayWorld = inverseRayProj(-1.0f, 1.0f, 1.0f); 600 | trRayWorld = inverseRayProj(1.0f, 1.0f, 1.0f); 601 | blRayWorld = inverseRayProj(-1.0f, -1.0f, 1.0f); 602 | brRayWorld = inverseRayProj(1.0f, -1.0f, 1.0f); 603 | } 604 | 605 | float Camera3D::calculateLOD ( Vector3DF pnt, float minlod, float maxlod, float maxdist ) 606 | { 607 | Vector3DF vec = pnt; 608 | vec -= from_pos; 609 | float lod = minlod + ((float) vec.Length() * (maxlod-minlod) / maxdist ); 610 | lod = (lod < minlod) ? minlod : lod; 611 | lod = (lod > maxlod) ? maxlod : lod; 612 | return lod; 613 | } 614 | 615 | float Camera3D::getDu () 616 | { 617 | return (float) tan ( mFov * DEGtoRAD/2.0f ) * mNear; 618 | } 619 | float Camera3D::getDv () 620 | { 621 | return (float) tan ( mFov * DEGtoRAD/2.0f ) * mNear / mAspect; 622 | } 623 | 624 | Vector3DF Camera3D::getU () 625 | { 626 | return side_vec; 627 | } 628 | Vector3DF Camera3D::getV () 629 | { 630 | return up_vec; 631 | } 632 | Vector3DF Camera3D::getW () 633 | { 634 | return dir_vec; 635 | } 636 | 637 | 638 | /*void Camera3D::setModelMatrix () 639 | { 640 | glGetFloatv ( GL_MODELVIEW_MATRIX, model_matrix.GetDataF() ); 641 | } 642 | void Camera3D::setModelMatrix ( Matrix4F& model ) 643 | { 644 | model_matrix = model; 645 | mv_matrix = model; 646 | mv_matrix *= view_matrix; 647 | #ifdef USE_DX 648 | 649 | #else 650 | glLoadMatrixf ( mv_matrix.GetDataF() ); 651 | #endif 652 | } 653 | */ 654 | 655 | Vector3DF Camera3D::inverseRayProj(float x, float y, float z) 656 | { 657 | Vector4DF p(x, y, z, 1.0f); 658 | 659 | Vector4DF wp(0.0f, 0.0f, 0.0f, 0.0f); 660 | wp.x = invviewproj_matrix.data[0] * p.x + invviewproj_matrix.data[4] * p.y + invviewproj_matrix.data[8] * p.z + invviewproj_matrix.data[12]; 661 | wp.y = invviewproj_matrix.data[1] * p.x + invviewproj_matrix.data[5] * p.y + invviewproj_matrix.data[9] * p.z + invviewproj_matrix.data[13]; 662 | wp.z = invviewproj_matrix.data[2] * p.x + invviewproj_matrix.data[6] * p.y + invviewproj_matrix.data[10] * p.z + invviewproj_matrix.data[14]; 663 | wp.w = invviewproj_matrix.data[3] * p.x + invviewproj_matrix.data[7] * p.y + invviewproj_matrix.data[11] * p.z + invviewproj_matrix.data[15]; 664 | 665 | return Vector3DF(wp.x / wp.w, wp.y / wp.w, wp.z / wp.w); 666 | } 667 | 668 | Vector3DF Camera3D::inverseRay (float x, float y, float z) 669 | { 670 | float sx = (float) tan ( mFov * DEGtoRAD/2.0f); 671 | float sy = sx / mAspect; 672 | float tu, tv; 673 | tu = mTile.x + x * (mTile.z-mTile.x); 674 | tv = mTile.y + y * (mTile.w-mTile.y); 675 | Vector4DF pnt ( (tu*2.0f-1.0f) * z*sx, (1.0f-tv*2.0f) * z*sy, -z, 1 ); 676 | pnt *= invrot_matrix; 677 | return pnt; 678 | } 679 | 680 | Vector3DF Camera3D::inverseRay ( float x, float y ) 681 | { 682 | float sx = (float) tan ( mFov * DEGtoRAD/2.0f); 683 | float sy = sx / mAspect; 684 | float tu, tv; 685 | tu = mTile.x + (x/mXres) * (mTile.z-mTile.x); 686 | tv = mTile.y + (y/mYres) * (mTile.w-mTile.y); 687 | Vector4DF pnt ( (tu-0.5f) * sx*mNear, (0.5f-tv) *sy*mNear, -mNear, 1 ); 688 | pnt *= invrot_matrix; 689 | pnt.Normalize (); 690 | return pnt; 691 | } 692 | 693 | Vector4DF Camera3D::project ( Vector3DF& p, Matrix4F& vm ) 694 | { 695 | Vector4DF q = p; // World coordinates 696 | q.w = 1.0; 697 | 698 | q *= vm; // Eye coordinates 699 | 700 | q *= tileproj_matrix; // Projection 701 | 702 | q /= q.w; // Normalized Device Coordinates (w-divide) 703 | 704 | q.x = (q.x*0.5f+0.5f) / mXres; 705 | q.y = (q.y*0.5f+0.5f) / mYres; 706 | q.z = q.z*0.5f + 0.5f; // Stored depth buffer value 707 | 708 | return q; 709 | } 710 | 711 | Vector4DF Camera3D::project ( Vector3DF& p ) 712 | { 713 | Vector4DF q = p; // World coordinates 714 | q.w = 1.0; 715 | q *= view_matrix; // Eye coordinates 716 | 717 | q *= tileproj_matrix; // Clip coordinates 718 | 719 | q /= q.w; // Normalized Device Coordinates (w-divide) 720 | 721 | q.x = (q.x*0.5f+0.5f)*mXres; 722 | q.y = (0.5f-q.y*0.5f)*mYres; 723 | q.z = q.z*0.5f + 0.5f; // Stored depth buffer value 724 | 725 | return q; 726 | } 727 | 728 | void PivotX::setPivot ( float x, float y, float z, float rx, float ry, float rz ) 729 | { 730 | from_pos.Set ( x,y,z); 731 | ang_euler.Set ( rx,ry,rz ); 732 | } 733 | 734 | void PivotX::updateTform () 735 | { 736 | trans.RotateZYXT ( ang_euler, from_pos ); 737 | } 738 | 739 | -------------------------------------------------------------------------------- /helpers/camera.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | #include "vec.h" 23 | 24 | #ifndef DEF_PIVOTX 25 | #define DEF_PIVOTX 26 | 27 | class PivotX { 28 | public: 29 | PivotX() { from_pos.Set(0,0,0); to_pos.Set(0,0,0); ang_euler.Set(0,0,0); scale.Set(1,1,1); trans.Identity(); } 30 | PivotX( Vector3DF& f, Vector3DF& t, Vector3DF& s, Vector3DF& a) { from_pos=f; to_pos=t; scale=s; ang_euler=a; } 31 | 32 | void setPivot ( float x, float y, float z, float rx, float ry, float rz ); 33 | void setPivot ( Vector3DF& pos, Vector3DF& ang ) { from_pos = pos; ang_euler = ang; } 34 | void setPivot ( PivotX piv ) { from_pos = piv.from_pos; to_pos = piv.to_pos; ang_euler = piv.ang_euler; updateTform(); } 35 | void setPivot ( PivotX& piv ) { from_pos = piv.from_pos; to_pos = piv.to_pos; ang_euler = piv.ang_euler; updateTform(); } 36 | 37 | void setIdentity () { from_pos.Set(0,0,0); to_pos.Set(0,0,0); ang_euler.Set(0,0,0); scale.Set(1,1,1); trans.Identity(); } 38 | 39 | void setAng ( float rx, float ry, float rz ) { ang_euler.Set(rx,ry,rz); updateTform(); } 40 | void setAng ( Vector3DF& a ) { ang_euler = a; updateTform(); } 41 | 42 | void setPos ( float x, float y, float z ) { from_pos.Set(x,y,z); updateTform(); } 43 | void setPos ( Vector3DF& p ) { from_pos = p; updateTform(); } 44 | 45 | void setToPos ( float x, float y, float z ) { to_pos.Set(x,y,z); updateTform(); } 46 | 47 | void updateTform (); 48 | void setTform ( Matrix4F& t ) { trans = t; } 49 | inline Matrix4F& getTform () { return trans; } 50 | inline float* getTformData () { return trans.GetDataF(); } 51 | 52 | // Pivot 53 | PivotX getPivot () { return PivotX(from_pos, to_pos, scale, ang_euler); } 54 | Vector3DF& getPos () { return from_pos; } 55 | Vector3DF& getToPos () { return to_pos; } 56 | Vector3DF& getAng () { return ang_euler; } 57 | Vector3DF getDir () { 58 | return to_pos - from_pos; 59 | } 60 | 61 | Vector3DF from_pos; 62 | Vector3DF to_pos; 63 | Vector3DF scale; 64 | Vector3DF ang_euler; 65 | Matrix4F trans; 66 | 67 | //Quatern ang_quat; 68 | //Quatern dang_quat; 69 | }; 70 | 71 | #endif 72 | 73 | #ifndef DEF_CAMERA_3D 74 | #define DEF_CAMERA_3D 75 | 76 | #define DEG_TO_RAD (3.141592/180.0) 77 | 78 | class Camera3D : public PivotX { 79 | public: 80 | enum eProjection { 81 | Perspective = 0, 82 | Parallel = 1 83 | }; 84 | Camera3D (); 85 | void Copy ( Camera3D& op ); 86 | 87 | void draw_gl(); 88 | 89 | // Camera settings 90 | void setAspect ( float asp ) { mAspect = asp; updateMatricies(); } 91 | void setPos ( float x, float y, float z ) { from_pos.Set(x,y,z); updateMatricies(); } 92 | void setToPos ( float x, float y, float z ) { to_pos.Set(x,y,z); updateMatricies(); } 93 | void setFov (float fov) { mFov = fov; updateMatricies(); } 94 | void setNearFar (float n, float f ) { mNear = n; mFar = f; updateMatricies(); } 95 | void setDist ( float d ) { mOrbitDist = d; updateMatricies(); } 96 | void setTile ( float x1, float y1, float x2, float y2 ) { mTile.Set ( x1, y1, x2, y2 ); updateMatricies(); } 97 | void setSize ( float w, float h ) { mXres=w; mYres=h; } 98 | void setProjection (eProjection proj_type); 99 | void setModelMatrix ( float* mtx ); 100 | void setViewMatrix ( float* mtx, float* invmtx ); 101 | void setProjMatrix ( float* mtx, float* invmtx ); 102 | void setMatrices ( const float* view_mtx, const float* proj_mtx ); 103 | 104 | // Camera motion 105 | void setOrbit ( float ax, float ay, float az, Vector3DF tp, float dist, float dolly ); 106 | void setOrbit ( Vector3DF angs, Vector3DF tp, float dist, float dolly ); 107 | void setAngles ( float ax, float ay, float az ); 108 | void moveOrbit ( float ax, float ay, float az, float dist ); 109 | void moveToPos ( float tx, float ty, float tz ); 110 | void moveRelative ( float dx, float dy, float dz ); 111 | 112 | // Frustum testing 113 | bool pointInFrustum ( float x, float y, float z ); 114 | bool boxInFrustum ( Vector3DF bmin, Vector3DF bmax); 115 | float calculateLOD ( Vector3DF pnt, float minlod, float maxlod, float maxdist ); 116 | 117 | // Utility functions 118 | void updateMatricies (); // Updates camera axes and projection matricies 119 | void updateFrustum (); // Updates frustum planes 120 | Vector3DF inverseRay ( float x, float y, float z ); 121 | Vector3DF inverseRay ( float x, float y ); 122 | Vector3DF inverseRayProj ( float x, float y, float z ); 123 | Vector4DF project ( Vector3DF& p ); 124 | Vector4DF project ( Vector3DF& p, Matrix4F& vm ); // Project point - override view matrix 125 | 126 | void getVectors ( Vector3DF& dir, Vector3DF& up, Vector3DF& side ) { dir = dir_vec; up = up_vec; side = side_vec; } 127 | void getBounds ( float dst, Vector3DF& min, Vector3DF& max ); 128 | float getNear () { return mNear; } 129 | float getFar () { return mFar; } 130 | float getFov () { return mFov; } 131 | float getDolly() { return mDolly; } 132 | float getOrbitDist() { return mOrbitDist; } 133 | Vector3DF& getUpDir () { return up_dir; } 134 | Vector4DF& getTile () { return mTile; } 135 | Matrix4F& getInvViewProjMatrix () { return invviewproj_matrix; } 136 | Matrix4F& getViewMatrix () { return view_matrix; } 137 | Matrix4F& getInvView () { return invrot_matrix; } 138 | Matrix4F& getRotateMatrix () { return rotate_matrix; } 139 | Matrix4F& getProjMatrix () { return tileproj_matrix; } 140 | Matrix4F& getFullProjMatrix () { return proj_matrix; } 141 | Matrix4F& getModelMatrix() { return model_matrix; } 142 | Matrix4F& getMVMatrix() { return mv_matrix; } 143 | float getAspect () { return mAspect; } 144 | Vector3DF getU (); 145 | Vector3DF getV (); 146 | Vector3DF getW (); 147 | float getDu (); 148 | float getDv (); 149 | 150 | 151 | public: 152 | eProjection mProjType; // Projection type 153 | 154 | // Camera Parameters // NOTE: Pivot maintains camera from and orientation 155 | float mDolly; // Camera to distance 156 | float mOrbitDist; 157 | float mFov, mAspect; // Camera field-of-view 158 | float mNear, mFar; // Camera frustum planes 159 | float mXres, mYres; 160 | Vector3DF dir_vec, side_vec, up_vec; // Camera aux vectors (W, V, and U) 161 | Vector3DF up_dir; 162 | Vector4DF mTile; 163 | 164 | // Transform Matricies 165 | Matrix4F invviewproj_matrix; 166 | Matrix4F rotate_matrix; // Vr matrix (rotation only) 167 | Matrix4F view_matrix; // V matrix (rotation + translation) 168 | Matrix4F proj_matrix; // P matrix 169 | Matrix4F invrot_matrix; // Vr^-1 matrix 170 | Matrix4F invproj_matrix; 171 | Matrix4F tileproj_matrix; // tiled projection matrix 172 | Matrix4F model_matrix; 173 | Matrix4F mv_matrix; 174 | float frustum[6][4]; // frustum plane equations 175 | 176 | bool mOps[8]; 177 | int mWire; 178 | 179 | Vector4DF tlRayWorld; 180 | Vector4DF trRayWorld; 181 | Vector4DF blRayWorld; 182 | Vector4DF brRayWorld; 183 | }; 184 | 185 | typedef Camera3D Light; 186 | 187 | #endif 188 | -------------------------------------------------------------------------------- /helpers/directory.cpp: -------------------------------------------------------------------------------- 1 | #include "directory.h" 2 | 3 | #include "string_helper.h" 4 | 5 | #include 6 | 7 | #ifdef _MSC_VER 8 | #include 9 | #endif 10 | 11 | #ifdef BUILD_GCC 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | 24 | #endif 25 | 26 | std::string Directory::gPathDelim = "\\"; 27 | 28 | Directory::Directory () 29 | { 30 | mPath = ""; 31 | #ifdef _MSC_VER 32 | gPathDelim = "\\"; 33 | #else 34 | gPathDelim = "/"; 35 | #endif 36 | } 37 | 38 | std::string Directory::NormalizeSlashies( std::string path ) 39 | { 40 | #ifdef _MSC_VER 41 | return strReplace( path, "/", "\\" ); 42 | #else 43 | return strReplace( path, "\\", "/" ); 44 | #endif 45 | } 46 | 47 | 48 | dir_list Directory::GetDirectoryItems( dir_list input ) 49 | { 50 | dir_list out; 51 | 52 | for ( unsigned int i=0; i < input.size(); i++ ) { 53 | if ( input[i].type == FILE_TYPE_DIR && input[i].text != "." ) out.push_back( input[i] ); 54 | } 55 | return out; 56 | } 57 | 58 | dir_list Directory::GetFileItems( dir_list input ) 59 | { 60 | dir_list out; 61 | 62 | for ( unsigned int i=0; i < input.size(); i++ ){ 63 | if ( input[i].type == FILE_TYPE_FILE && input[i].length >= 0 ) out.push_back( input[i] ); 64 | } 65 | return out; 66 | } 67 | 68 | 69 | std::string Directory::GetExtension( std::string path ) 70 | { 71 | path = Directory::NormalizeSlashies( path ); 72 | 73 | std::vector< std::string > temp = strSplitMultiple ( path, "." ); 74 | if ( temp.size() > 1 ) { 75 | return temp[ temp.size() ]; 76 | } 77 | return ""; 78 | } 79 | 80 | 81 | bool Directory::FileExists( std::string filename ) 82 | { 83 | struct stat stFileInfo; 84 | bool exists = false; 85 | int intStat; 86 | 87 | intStat = stat( filename.c_str(), &stFileInfo ); 88 | 89 | if(intStat == 0) { 90 | exists = true; 91 | } 92 | 93 | return( exists ); 94 | } 95 | 96 | 97 | #ifdef _MSC_VER 98 | 99 | int Directory::CreateDir ( std::string path ) 100 | { 101 | path = Directory::NormalizeSlashies( path ); 102 | 103 | int out = 0; 104 | 105 | std::vector< std::string > pathSet = strSplitMultiple ( path, Directory::gPathDelim ); 106 | 107 | std::string currPath = ""; 108 | 109 | std::vector< std::string >::iterator it; 110 | 111 | for ( it = pathSet.begin() ; it < pathSet.end(); it++ ) { 112 | 113 | out = CreateDirectory ( (currPath + *it).c_str() , NULL ); 114 | 115 | currPath += *it + Directory::gPathDelim ; 116 | 117 | if ( out == 0 ) { 118 | DWORD d = GetLastError(); 119 | if ( d == ERROR_PATH_NOT_FOUND ) { return 0; } 120 | if ( d == ERROR_ALREADY_EXISTS ) { /* continue */ } 121 | } 122 | 123 | } 124 | 125 | return out; 126 | } 127 | 128 | 129 | std::string Directory::GetCollapsedPath( std::string path ) 130 | { 131 | // Create a fully resolved path 132 | path = GetExecutablePath() + "/" + path; 133 | 134 | // Normalize slashes (\ vs /) 135 | path = Directory::NormalizeSlashies( path ); 136 | 137 | elem_vec_t out; 138 | std::vector< std::string > temp; 139 | std::string mFileFound = ""; 140 | 141 | // Split path 142 | temp = strSplitMultiple ( path, Directory::gPathDelim); 143 | 144 | for ( unsigned int i = 0; i < temp.size(); i++ ) { 145 | if ( ( temp[i].compare("..") == 0 ) && out.size() > 0 ) { 146 | out.pop_back(); 147 | } else if ( temp[i].size() > 0 && temp[i].find(".") == -1 ) { 148 | text_element_t element; 149 | element.text = temp[i]; 150 | element.length = -1; 151 | out.push_back( element ); 152 | } 153 | 154 | if ( temp[i].find(".") != -1 ) { 155 | mFileFound = temp[i]; 156 | } 157 | } 158 | 159 | std::string outStr = ""; 160 | 161 | for ( unsigned int k = 0; k < out.size(); k++ ) 162 | { 163 | outStr += out[k].text; 164 | outStr += Directory::gPathDelim; 165 | } 166 | 167 | // remember to remove the last extraneous slashies 168 | return outStr.substr( 0, outStr.length() - 1 ); 169 | } 170 | 171 | std::string Directory::GetExecutablePath() 172 | { 173 | LPTSTR szAppPath[MAX_PATH]; 174 | 175 | std::string strAppDirectory; 176 | 177 | ::GetModuleFileName(NULL, szAppPath[0], (sizeof(szAppPath) - 1)/sizeof(TCHAR)); 178 | 179 | // Extract directory 180 | strAppDirectory = (char*) szAppPath; // use ws2s for conversion to wchar 181 | strAppDirectory = strAppDirectory.substr(0, strAppDirectory.rfind("\\")); 182 | 183 | strAppDirectory = strReplace( strAppDirectory, "\\", "/" ); 184 | 185 | return strAppDirectory; 186 | } 187 | 188 | std::string Directory::ws2s(const std::wstring& s) 189 | { 190 | int len; 191 | int slength = (int)s.length() + 1; 192 | len = WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, 0, 0, 0, 0); 193 | char* buf = (char*) malloc ( len ); // temporary, don't register with memory checker 194 | WideCharToMultiByte(CP_ACP, 0, s.c_str(), slength, buf, len, 0, 0); 195 | std::string r(buf); 196 | free ( buf ); 197 | return r; 198 | } 199 | 200 | std::wstring Directory::s2ws(const std::string& s) 201 | { 202 | int len; 203 | int slength = (int)s.length() + 1; 204 | len = MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, 0, 0); 205 | wchar_t* buf = (wchar_t*) malloc ( len *sizeof(wchar_t) ); // temporary, don't register with memory checker 206 | MultiByteToWideChar(CP_ACP, 0, s.c_str(), slength, buf, len); 207 | std::wstring r(buf); 208 | free ( buf ); 209 | return r; 210 | } 211 | 212 | void Directory::LoadDir ( std::string path, std::string ext ) 213 | { 214 | mPath = path; 215 | mFileFilter = ext; 216 | mList = DirList ( path, ext ); 217 | } 218 | 219 | dir_list Directory::DirList( std::string path, std::string ext ) 220 | { 221 | path = Directory::NormalizeSlashies( path ) + ext; 222 | 223 | //NOTE this is not unicode compliant 224 | WIN32_FIND_DATAA fileData; 225 | HANDLE hFind = INVALID_HANDLE_VALUE; 226 | LARGE_INTEGER filesize; 227 | 228 | DWORD dwError=0; 229 | 230 | dir_list out; 231 | 232 | hFind = FindFirstFileA( path.c_str(), &fileData ); 233 | 234 | if (INVALID_HANDLE_VALUE == hFind) 235 | { 236 | // 237 | } 238 | 239 | do 240 | { 241 | if (( fileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) && !( fileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN )) 242 | { 243 | dir_list_element e; 244 | e.length = 0; 245 | e.text = fileData.cFileName; 246 | e.extension = "DIR"; 247 | e.type = FILE_TYPE_DIR; 248 | out.push_back( e ); 249 | } 250 | else 251 | { 252 | filesize.LowPart = fileData.nFileSizeLow; 253 | filesize.HighPart = fileData.nFileSizeHigh; 254 | 255 | dir_list_element e; 256 | e.length = (int) filesize.QuadPart; 257 | e.text = fileData.cFileName; 258 | e.extension = strSplitArg ( e.text, "." ); 259 | e.type = FILE_TYPE_FILE; 260 | out.push_back( e ); 261 | } 262 | } 263 | while (FindNextFileA(hFind, &fileData) != 0); 264 | 265 | dwError = GetLastError(); 266 | if (dwError != ERROR_NO_MORE_FILES) 267 | { 268 | // 269 | } 270 | 271 | FindClose(hFind); 272 | return out; 273 | } 274 | #endif 275 | 276 | #ifdef BUILD_GCC 277 | //#ifdef __LINUX 278 | 279 | // DUMMY!!! 280 | int Directory::CreatePath( std::string path ) { 281 | return 0; 282 | } 283 | 284 | std::string Directory::GetExecutablePath() 285 | { 286 | char result[ PATH_MAX ]; 287 | ssize_t count = readlink( "/proc/self/exe", result, PATH_MAX ); 288 | return std::string( result, (count > 0) ? count : 0 ); 289 | } 290 | 291 | /**************** 292 | * PROBABLY NOT WORKING!! 293 | ****************/ 294 | dir_list Directory::DirList( std::string path ) 295 | { 296 | path = Directory::NormalizeSlashies( path ); 297 | 298 | dir_list out; 299 | 300 | DIR *dp; 301 | struct dirent *dirp; 302 | if( ( dp = opendir( path.c_str() ) ) == NULL ) { 303 | //cout << "Error(" << errno << ") opening " << dir << endl; 304 | printf("Error(%i) opening\n", errno); 305 | //return dir_list; 306 | return out; 307 | } 308 | 309 | while ( (dirp = readdir( dp ) ) != NULL ) { 310 | 311 | dir_list_element e; 312 | e.length = dirp->d_reclen; 313 | e.text = std::string( dirp->d_name ); 314 | //e.extension = "DIR"; 315 | //e.type = FILE_TYPE_DIR; 316 | out.push_back( e ); 317 | 318 | } 319 | 320 | closedir( dp ); 321 | //return 0; 322 | return out; 323 | } 324 | 325 | std::string Directory::GetCollapsedPath( std::string path ) 326 | { 327 | path = Directory::NormalizeSlashies( path ); 328 | 329 | elem_vec_t out; 330 | std::vector< std::string > temp; 331 | 332 | std::string mFileFound = ""; 333 | 334 | temp = StringHelper::SplitString( path, Directory::mPathDelim); 335 | 336 | for ( unsigned int i = 0; i < temp.size(); i++ ) 337 | { 338 | if ( ( temp[i].compare("..") == 0 ) && out.size() > 0 ) { 339 | out.pop_back(); 340 | } else if ( temp[i].size() > 0 && temp[i].find(".") == -1 ) { 341 | text_element_t element; 342 | element.text = temp[i]; 343 | element.length = -1; 344 | out.push_back( element ); 345 | } 346 | 347 | if ( temp[i].find(".") != -1 ) { 348 | mFileFound = temp[i]; 349 | } 350 | } 351 | 352 | std::string outStr = ""; 353 | 354 | for ( unsigned int k = 0; k < out.size(); k++ ) 355 | { 356 | outStr += out[k].text; 357 | outStr += Directory::mPathDelim; 358 | } 359 | 360 | // remember to remove the last extraneous slashies 361 | return outStr.substr( 0, outStr.length() - 1 ); 362 | } 363 | 364 | 365 | #endif 366 | -------------------------------------------------------------------------------- /helpers/directory.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef DEF_DIR_OBJECT 3 | #define DEF_DIR_OBJECT 4 | 5 | //#include "common_defs.h" 6 | #include 7 | 8 | #define FILE_TYPE_DIR 0 9 | #define FILE_TYPE_FILE 1 10 | 11 | // Hmmm - have to factor this specific stuff out. 12 | #define FILE_TYPE_BLOCKSAVE 2 13 | #define FILE_TYPE_BLOCKLEV 3 14 | 15 | typedef struct { 16 | int type; 17 | std::string text; 18 | std::string extension; 19 | int length; 20 | } dir_list_element; 21 | 22 | typedef std::vector< dir_list_element > dir_list; 23 | 24 | typedef struct { 25 | std::string text; 26 | float length; 27 | } text_element_t; 28 | 29 | typedef std::vector< text_element_t > elem_vec_t; 30 | 31 | 32 | class Directory { 33 | public: 34 | Directory (); 35 | 36 | void LoadDir ( std::string path, std::string filter ); 37 | int CreateDir ( std::string path ); 38 | int getNumFiles () { return (int) mList.size(); } 39 | dir_list_element getFile ( int n ) { return mList[n]; } 40 | bool isDir ( int n ) { return mList[n].type==0; } 41 | 42 | // Static functions 43 | static dir_list DirList( std::string path, std::string filter ); 44 | static bool FileExists( std::string filename ); 45 | static std::string ws2s(const std::wstring& s); 46 | static std::wstring s2ws(const std::string& s); 47 | static dir_list GetFileItems( dir_list input); 48 | static dir_list GetDirectoryItems( dir_list input); 49 | static std::string NormalizeSlashies( std::string path ); 50 | static std::string GetExtension( std::string path ); 51 | static std::string GetExecutablePath(); 52 | static std::string GetCollapsedPath( std::string path ); 53 | static std::string gPathDelim; 54 | 55 | std::string getPath () { return mPath; } 56 | std::string getFilter () { return mFileFilter; } 57 | 58 | private: 59 | 60 | std::string mPath; 61 | std::string mFileFilter; 62 | 63 | 64 | dir_list mList; 65 | 66 | }; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /helpers/file_tga.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | 23 | //------------------------------------------------ TGA FORMAT 24 | #ifndef TGA_NOIMPL 25 | 26 | #include "file_tga.h" 27 | 28 | #ifdef DEBUG_HEAP 29 | #define _CRTDBG_MAP_ALLOC 30 | #include 31 | #include 32 | #else 33 | #include 34 | #endif 35 | 36 | 37 | TGA::~TGA( void ) 38 | { 39 | if( m_nImageData != NULL ) 40 | { 41 | free( m_nImageData ); 42 | m_nImageData = NULL; 43 | } 44 | } 45 | 46 | int TGA::returnError( FILE *s, int error ) 47 | { 48 | // Called when there is an error loading the .tga texture file. 49 | fclose( s ); 50 | return error; 51 | } 52 | 53 | unsigned char *TGA::getRGBA( FILE *s, int size ) 54 | { 55 | // Read in RGBA data for a 32bit image. 56 | unsigned char *rgba; 57 | unsigned char temp; 58 | int bread; 59 | int i; 60 | 61 | rgba = (unsigned char *) malloc( size * 4 ); 62 | 63 | if( rgba == NULL ) 64 | return 0; 65 | 66 | bread = (int) fread( rgba, sizeof (unsigned char), size * 4, s ); 67 | 68 | // TGA is stored in BGRA, make it RGBA 69 | if( bread != size * 4 ) 70 | { 71 | free( rgba ); 72 | return 0; 73 | } 74 | 75 | for( i = 0; i < size * 4; i += 4 ) 76 | { 77 | temp = rgba[i]; 78 | rgba[i] = rgba[i + 2]; 79 | rgba[i + 2] = temp; 80 | } 81 | 82 | m_texFormat = TGA::RGBA; 83 | return rgba; 84 | } 85 | 86 | unsigned char *TGA::getRGB( FILE *s, int size ) 87 | { 88 | // Read in RGB data for a 24bit image. 89 | unsigned char *rgb; 90 | unsigned char temp; 91 | int bread; 92 | int i; 93 | 94 | rgb = (unsigned char*)malloc( size * 3 ); 95 | 96 | if( rgb == NULL ) 97 | return 0; 98 | 99 | bread = (int)fread( rgb, sizeof (unsigned char), size * 3, s ); 100 | 101 | if(bread != size * 3) 102 | { 103 | free( rgb ); 104 | return 0; 105 | } 106 | 107 | // TGA is stored in BGR, make it RGB 108 | for( i = 0; i < size * 3; i += 3 ) 109 | { 110 | temp = rgb[i]; 111 | rgb[i] = rgb[i + 2]; 112 | rgb[i + 2] = temp; 113 | } 114 | 115 | m_texFormat = TGA::RGB; 116 | 117 | return rgb; 118 | } 119 | 120 | unsigned char *TGA::getGray( FILE *s, int size ) 121 | { 122 | // Gets the grayscale image data. Used as an alpha channel. 123 | unsigned char *grayData; 124 | int bread; 125 | 126 | grayData = (unsigned char*)malloc( size ); 127 | 128 | if( grayData == NULL ) 129 | return 0; 130 | 131 | bread = (int)fread( grayData, sizeof (unsigned char), size, s ); 132 | 133 | if( bread != size ) 134 | { 135 | free( grayData ); 136 | return 0; 137 | } 138 | 139 | m_texFormat = TGA::ALPHA; 140 | 141 | return grayData; 142 | } 143 | 144 | #pragma warning(disable: 4996) 145 | 146 | TGA::TGAError TGA::load( const char *name ) 147 | { 148 | // Loads up a targa file. Supported types are 8,24 and 32 149 | // uncompressed images. 150 | unsigned char type[4]; 151 | unsigned char info[7]; 152 | FILE *s = NULL; 153 | int size = 0; 154 | 155 | if( !(s = fopen( name, "rb" )) ) 156 | return TGA_FILE_NOT_FOUND; 157 | 158 | fread( &type, sizeof (char), 3, s ); // Read in colormap info and image type, byte 0 ignored 159 | fseek( s, 12, SEEK_SET); // Seek past the header and useless info 160 | fread( &info, sizeof (char), 6, s ); 161 | 162 | if( type[1] != 0 || (type[2] != 2 && type[2] != 3) ) 163 | returnError( s, TGA_BAD_IMAGE_TYPE ); 164 | 165 | m_nImageWidth = info[0] + info[1] * 256; 166 | m_nImageHeight = info[2] + info[3] * 256; 167 | m_nImageBits = info[4]; 168 | 169 | size = m_nImageWidth * m_nImageHeight; 170 | 171 | // Make sure we are loading a supported type 172 | if( m_nImageBits != 32 && m_nImageBits != 24 && m_nImageBits != 8 ) 173 | returnError( s, TGA_BAD_BITS ); 174 | 175 | if( m_nImageBits == 32 ) 176 | m_nImageData = getRGBA( s, size ); 177 | else if( m_nImageBits == 24 ) 178 | m_nImageData = getRGB( s, size ); 179 | else if( m_nImageBits == 8 ) 180 | m_nImageData = getGray( s, size ); 181 | 182 | // No image data 183 | if( m_nImageData == NULL ) 184 | returnError( s, TGA_BAD_DATA ); 185 | 186 | fclose( s ); 187 | 188 | return TGA_NO_ERROR; 189 | } 190 | 191 | void TGA::writeRGBA( FILE *s, const unsigned char *externalImage, int size ) 192 | { 193 | // Read in RGBA data for a 32bit image. 194 | unsigned char *rgba; 195 | int bread; 196 | int i; 197 | 198 | rgba = (unsigned char *)malloc( size * 4 ); 199 | 200 | // switch RGBA to BGRA 201 | for( i = 0; i < size * 4; i += 4 ) 202 | { 203 | rgba[i + 0] = externalImage[i + 2]; 204 | rgba[i + 1] = externalImage[i + 1]; 205 | rgba[i + 2] = externalImage[i + 0]; 206 | rgba[i + 3] = externalImage[i + 3]; 207 | } 208 | 209 | bread = (int)fwrite( rgba, sizeof (unsigned char), size * 4, s ); 210 | free( rgba ); 211 | } 212 | 213 | void TGA::writeRGB( FILE *s, const unsigned char *externalImage, int size ) 214 | { 215 | // Read in RGBA data for a 32bit image. 216 | unsigned char *rgb; 217 | int bread; 218 | int i; 219 | 220 | rgb = (unsigned char *)malloc( size * 3 ); 221 | 222 | // switch RGB to BGR 223 | for( i = 0; i < size * 3; i += 3 ) 224 | { 225 | rgb[i + 0] = externalImage[i + 2]; 226 | rgb[i + 1] = externalImage[i + 1]; 227 | rgb[i + 2] = externalImage[i + 0]; 228 | } 229 | 230 | bread = (int)fwrite( rgb, sizeof (unsigned char), size * 3, s ); 231 | free( rgb ); 232 | } 233 | 234 | void TGA::writeGrayAsRGB( FILE *s, const unsigned char *externalImage, int size ) 235 | { 236 | // Read in RGBA data for a 32bit image. 237 | unsigned char *rgb; 238 | int bread; 239 | int i; 240 | 241 | rgb = (unsigned char *)malloc( size * 3 ); 242 | 243 | // switch RGB to BGR 244 | int j = 0; 245 | for( i = 0; i < size * 3; i += 3, j++ ) 246 | { 247 | rgb[i + 0] = externalImage[j]; 248 | rgb[i + 1] = externalImage[j]; 249 | rgb[i + 2] = externalImage[j]; 250 | } 251 | 252 | bread = (int)fwrite( rgb, sizeof (unsigned char), size * 3, s ); 253 | free( rgb ); 254 | } 255 | 256 | void TGA::writeGray( FILE *s, const unsigned char *externalImage, int size ) 257 | { 258 | // Gets the grayscale image data. Used as an alpha channel. 259 | int bread; 260 | 261 | bread = (int)fwrite( externalImage, sizeof (unsigned char), size, s ); 262 | } 263 | 264 | TGA::TGAError TGA::saveFromExternalData( const char *name, int w, int h, TGA::TGAFormat fmt, const unsigned char *externalImage ) 265 | { 266 | static unsigned char type[] = {0,0,2}; 267 | static unsigned char dummy[] = {0,0,0,0,0,0,0,0,0}; 268 | static unsigned char info[] = {0,0,0,0,0,0}; 269 | FILE *s = NULL; 270 | int size = 0; 271 | 272 | if( !(s = fopen( name, "wb" )) ) 273 | return TGA_FILE_NOT_FOUND; 274 | 275 | fwrite( type, sizeof (char), 3, s ); // Read in colormap info and image type, byte 0 ignored 276 | fwrite( dummy, sizeof (char), 9, s ); // Read in colormap info and image type, byte 0 ignored 277 | 278 | info[0] = w & 0xFF; 279 | info[1] = (w>>8) & 0xFF; 280 | info[2] = h & 0xFF; 281 | info[3] = (h>>8) & 0xFF; 282 | switch(fmt) 283 | { 284 | case ALPHA: 285 | info[4] = 8; 286 | break; 287 | case RGB: 288 | info[4] = 24; 289 | break; 290 | case RGBA: 291 | info[4] = 32; 292 | break; 293 | } 294 | fwrite( info, sizeof (char), 6, s ); 295 | 296 | size = w*h; 297 | switch(fmt) 298 | { 299 | case ALPHA: 300 | writeGray(s, externalImage, size); 301 | break; 302 | case RGB: 303 | writeGrayAsRGB/*writeRGB*/(s, externalImage, size); 304 | break; 305 | case RGBA: 306 | writeRGBA(s, externalImage, size); 307 | break; 308 | } 309 | 310 | fclose( s ); 311 | 312 | return TGA_NO_ERROR; 313 | } 314 | 315 | #endif // #ifndef TGA_NOIMPL 316 | 317 | -------------------------------------------------------------------------------- /helpers/file_tga.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | #ifndef LOAD_TGA 23 | 24 | #include 25 | 26 | class TGA { 27 | public: 28 | enum TGAFormat 29 | { 30 | RGB = 0x1907, 31 | RGBA = 0x1908, 32 | ALPHA = 0x1906, 33 | UNKNOWN = -1 34 | }; 35 | 36 | enum TGAError 37 | { 38 | TGA_NO_ERROR = 1, // No error 39 | TGA_FILE_NOT_FOUND, // File was not found 40 | TGA_BAD_IMAGE_TYPE, // Color mapped image or image is not uncompressed 41 | TGA_BAD_DIMENSION, // Dimension is not a power of 2 42 | TGA_BAD_BITS, // Image bits is not 8, 24 or 32 43 | TGA_BAD_DATA // Image data could not be loaded 44 | }; 45 | 46 | TGA(void) : 47 | m_texFormat(TGA::UNKNOWN), 48 | m_nImageWidth(0), 49 | m_nImageHeight(0), 50 | m_nImageBits(0), 51 | m_nImageData(NULL) {} 52 | 53 | ~TGA(void); 54 | 55 | TGA::TGAError load( const char *name ); 56 | TGA::TGAError saveFromExternalData( const char *name, int w, int h, TGAFormat fmt, const unsigned char *externalImage ); 57 | 58 | TGAFormat m_texFormat; 59 | int m_nImageWidth; 60 | int m_nImageHeight; 61 | int m_nImageBits; 62 | unsigned char * m_nImageData; 63 | 64 | private: 65 | 66 | int returnError(FILE *s, int error); 67 | unsigned char *getRGBA(FILE *s, int size); 68 | unsigned char *getRGB(FILE *s, int size); 69 | unsigned char *getGray(FILE *s, int size); 70 | void writeRGBA(FILE *s, const unsigned char *externalImage, int size); 71 | void writeRGB(FILE *s, const unsigned char *externalImage, int size); 72 | void writeGrayAsRGB(FILE *s, const unsigned char *externalImage, int size); 73 | void writeGray(FILE *s, const unsigned char *externalImage, int size); 74 | }; 75 | 76 | #endif -------------------------------------------------------------------------------- /helpers/main.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | #ifndef __MAIN_H__ 23 | #define __MAIN_H__ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | 30 | #pragma warning(disable:4996) // preventing snprintf >> _snprintf_s 31 | 32 | // trick for pragma message so we can write: 33 | // #pragma message(__FILE__"("S__LINE__"): blah") 34 | #define S__(x) #x 35 | #define S_(x) S__(x) 36 | #define S__LINE__ S_(__LINE__) 37 | 38 | #ifdef DEBUG_HEAP 39 | #define _CRTDBG_MAP_ALLOC 40 | #include 41 | #include 42 | #else 43 | #include 44 | #endif 45 | 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #ifdef WIN32 52 | #ifdef MEMORY_LEAKS_CHECK 53 | # pragma message("build will Check for Memory Leaks!") 54 | # define _CRTDBG_MAP_ALLOC 55 | # include 56 | # include 57 | inline void* operator new(size_t size, const char *file, int line) 58 | { 59 | return ::operator new(size, 1, file, line); 60 | } 61 | 62 | inline void __cdecl operator delete(void *ptr, const char *file, int line) 63 | { 64 | ::operator delete(ptr, _NORMAL_BLOCK, file, line); 65 | } 66 | 67 | #define DEBUG_NEW new( __FILE__, __LINE__) 68 | #define MALLOC_DBG(x) _malloc_dbg(x, 1, __FILE__, __LINE__); 69 | #define malloc(x) MALLOC_DBG(x) 70 | #define new DEBUG_NEW 71 | #endif 72 | #endif 73 | 74 | //----------------- to be declared in the code of the sample: so the sample can decide how to display messages 75 | class NVPWindow { 76 | public: 77 | enum ButtonAction { 78 | BUTTON_RELEASE = 0, 79 | BUTTON_PRESS = 1, 80 | BUTTON_REPEAT = 2, 81 | }; 82 | enum MouseButton 83 | { 84 | MOUSE_BUTTON_LEFT = 0, 85 | MOUSE_BUTTON_RIGHT = 1, 86 | MOUSE_BUTTON_MIDDLE = 2, 87 | NUM_MOUSE_BUTTONIDX, 88 | }; 89 | enum MouseButtonFlag 90 | { 91 | MOUSE_BUTTONFLAG_NONE = 0, 92 | MOUSE_BUTTONFLAG_LEFT = (1 << MOUSE_BUTTON_LEFT), 93 | MOUSE_BUTTONFLAG_RIGHT = (1 << MOUSE_BUTTON_RIGHT), 94 | MOUSE_BUTTONFLAG_MIDDLE = (1 << MOUSE_BUTTON_MIDDLE) 95 | }; 96 | enum KeyCode { 97 | KEY_UNKNOWN = -1, 98 | KEY_SPACE = 32, 99 | KEY_APOSTROPHE = 39 /* ' */, 100 | KEY_LEFT_PARENTHESIS = 40 /* ( */, 101 | KEY_RIGHT_PARENTHESIS = 41 /* ) */, 102 | KEY_ASTERISK = 42 /* * */, 103 | KEY_PLUS = 43 /* + */, 104 | KEY_COMMA = 44 /* , */, 105 | KEY_MINUS = 45 /* - */, 106 | KEY_PERIOD = 46 /* . */, 107 | KEY_SLASH = 47 /* / */, 108 | KEY_0 = 48, 109 | KEY_1 = 49, 110 | KEY_2 = 50, 111 | KEY_3 = 51, 112 | KEY_4 = 52, 113 | KEY_5 = 53, 114 | KEY_6 = 54, 115 | KEY_7 = 55, 116 | KEY_8 = 56, 117 | KEY_9 = 57, 118 | KEY_COLON = 58 /* : */, 119 | KEY_SEMICOLON = 59 /* ; */, 120 | KEY_LESS = 60 /* < */, 121 | KEY_EQUAL = 61 /* = */, 122 | KEY_GREATER = 62 /* > */, 123 | KEY_A = 65, 124 | KEY_B = 66, 125 | KEY_C = 67, 126 | KEY_D = 68, 127 | KEY_E = 69, 128 | KEY_F = 70, 129 | KEY_G = 71, 130 | KEY_H = 72, 131 | KEY_I = 73, 132 | KEY_J = 74, 133 | KEY_K = 75, 134 | KEY_L = 76, 135 | KEY_M = 77, 136 | KEY_N = 78, 137 | KEY_O = 79, 138 | KEY_P = 80, 139 | KEY_Q = 81, 140 | KEY_R = 82, 141 | KEY_S = 83, 142 | KEY_T = 84, 143 | KEY_U = 85, 144 | KEY_V = 86, 145 | KEY_W = 87, 146 | KEY_X = 88, 147 | KEY_Y = 89, 148 | KEY_Z = 90, 149 | KEY_LEFT_BRACKET = 91 /* [ */, 150 | KEY_BACKSLASH = 92 /* \ */, 151 | KEY_RIGHT_BRACKET = 93 /* ] */, 152 | KEY_GRAVE_ACCENT = 96 /* ` */, 153 | KEY_WORLD_1 = 161 /* non-US #1 */, 154 | KEY_WORLD_2 = 162 /* non-US #2 */, 155 | /* Function keys */ 156 | KEY_ESCAPE = 256, 157 | KEY_ENTER = 257, 158 | KEY_TAB = 258, 159 | KEY_BACKSPACE = 259, 160 | KEY_INSERT = 260, 161 | KEY_DELETE = 261, 162 | KEY_RIGHT = 262, 163 | KEY_LEFT = 263, 164 | KEY_DOWN = 264, 165 | KEY_UP = 265, 166 | KEY_PAGE_UP = 266, 167 | KEY_PAGE_DOWN = 267, 168 | KEY_HOME = 268, 169 | KEY_END = 269, 170 | KEY_CAPS_LOCK = 280, 171 | KEY_SCROLL_LOCK = 281, 172 | KEY_NUM_LOCK = 282, 173 | KEY_PRINT_SCREEN = 283, 174 | KEY_PAUSE = 284, 175 | KEY_F1 = 290, 176 | KEY_F2 = 291, 177 | KEY_F3 = 292, 178 | KEY_F4 = 293, 179 | KEY_F5 = 294, 180 | KEY_F6 = 295, 181 | KEY_F7 = 296, 182 | KEY_F8 = 297, 183 | KEY_F9 = 298, 184 | KEY_F10 = 299, 185 | KEY_F11 = 300, 186 | KEY_F12 = 301, 187 | KEY_F13 = 302, 188 | KEY_F14 = 303, 189 | KEY_F15 = 304, 190 | KEY_F16 = 305, 191 | KEY_F17 = 306, 192 | KEY_F18 = 307, 193 | KEY_F19 = 308, 194 | KEY_F20 = 309, 195 | KEY_F21 = 310, 196 | KEY_F22 = 311, 197 | KEY_F23 = 312, 198 | KEY_F24 = 313, 199 | KEY_F25 = 314, 200 | KEY_KP_0 = 320, 201 | KEY_KP_1 = 321, 202 | KEY_KP_2 = 322, 203 | KEY_KP_3 = 323, 204 | KEY_KP_4 = 324, 205 | KEY_KP_5 = 325, 206 | KEY_KP_6 = 326, 207 | KEY_KP_7 = 327, 208 | KEY_KP_8 = 328, 209 | KEY_KP_9 = 329, 210 | KEY_KP_DECIMAL = 330, 211 | KEY_KP_DIVIDE = 331, 212 | KEY_KP_MULTIPLY = 332, 213 | KEY_KP_SUBTRACT = 333, 214 | KEY_KP_ADD = 334, 215 | KEY_KP_ENTER = 335, 216 | KEY_KP_EQUAL = 336, 217 | KEY_LEFT_SHIFT = 340, 218 | KEY_LEFT_CONTROL = 341, 219 | KEY_LEFT_ALT = 342, 220 | KEY_LEFT_SUPER = 343, 221 | KEY_RIGHT_SHIFT = 344, 222 | KEY_RIGHT_CONTROL = 345, 223 | KEY_RIGHT_ALT = 346, 224 | KEY_RIGHT_SUPER = 347, 225 | KEY_MENU = 348, 226 | KEY_LAST = KEY_MENU, 227 | }; 228 | enum KeyModifiers { 229 | KMOD_SHIFT = 0x0001, 230 | KMOD_CONTROL = 0x0002, 231 | KMOD_ALT = 0x0004, 232 | KMOD_SUPER = 0x0008, 233 | }; 234 | typedef struct WINinternal* WINhandle; 235 | typedef void (*NVPproc)(void); 236 | 237 | // OpenGL specific 238 | struct ContextFlags { 239 | int major; 240 | int minor; 241 | int MSAA; 242 | int depth; 243 | int stencil; 244 | bool debug; 245 | bool robust; 246 | bool core; 247 | bool forward; 248 | bool stereo; 249 | NVPWindow* share; 250 | 251 | ContextFlags(int _major=3, int _minor=0, bool _core=false, int _MSAA=1, int _depth=24, int _stencil=8,bool _debug=false, bool _robust=false, bool _forward=false, bool _stereo=false, NVPWindow* _share=0) 252 | { 253 | major = _major; 254 | minor = _minor; 255 | MSAA = _MSAA; 256 | depth = _depth; 257 | stencil = _stencil; 258 | core = _core; 259 | debug = _debug; 260 | robust = _robust; 261 | forward = _forward; 262 | stereo = _stereo; 263 | share = _share; 264 | } 265 | 266 | }; 267 | unsigned int m_debugFilter; 268 | std::string m_debugTitle; 269 | 270 | WINhandle m_internal; 271 | 272 | int m_renderCnt; 273 | int m_curX, m_curY; 274 | int m_wheel; 275 | int m_winSz[4]; 276 | int m_mods; 277 | ContextFlags m_cflags; 278 | bool m_doSwap; 279 | bool m_active; 280 | bool m_vsync; 281 | bool m_keyPressed[KEY_LAST+1]; 282 | bool m_keyToggled[KEY_LAST+1]; 283 | bool m_fullscreen; 284 | int m_display_frame; 285 | int m_golden_frame; 286 | int m_screenquad_prog; 287 | int m_screenquad_vshader; 288 | int m_screenquad_fshader; 289 | int m_screenquad_vbo[3]; 290 | int m_screenquad_utex1; 291 | int m_screenquad_utex2; 292 | int m_screenquad_utexflags; 293 | int m_screenquad_ucoords; 294 | int m_screenquad_uscreen; 295 | 296 | NVPWindow() 297 | : m_renderCnt(1) 298 | , m_internal(0) 299 | , m_debugFilter(0) 300 | { 301 | m_curX = -1; 302 | m_curY = -1; 303 | m_mods = 0; 304 | m_fullscreen = false; 305 | memset(m_keyPressed, 0, sizeof(m_keyPressed)); 306 | memset(m_keyToggled, 0, sizeof(m_keyToggled)); 307 | } 308 | bool isPressed(int key) { return m_keyPressed[key]; } 309 | bool onPress(int key) { return m_keyPressed[key] && m_keyToggled[key]; } 310 | 311 | // Accessors 312 | inline void setWinSz(int w, int h) { m_winSz[0]=w; m_winSz[1]=h; } 313 | inline const int* getWinSz() const { return m_winSz; } 314 | inline int getWidth() const { return m_winSz[0]; } 315 | inline int getHeight() const { return m_winSz[1]; } 316 | inline const int getWheel() const { return m_wheel; } 317 | inline int getMods() const { return m_mods; } 318 | inline void setMods(int m) { m_mods = m; } 319 | inline void setCurMouse(int x, int y) { m_curX = x; m_curY = y; } 320 | inline int getCurX() { return m_curX; } 321 | inline int getCurY() { return m_curY; } 322 | inline bool isFirstFrame() { return m_display_frame==0; } 323 | inline int getDisplayFrame() { return m_display_frame; } 324 | 325 | // activate and deactivate are not thread-safe, need to be wrapped in mutex if called from multiple threads 326 | // invisible windows will not have any active callbacks, nor will they be affected by sysEvents 327 | bool activate(int width, int height, const char* title, const ContextFlags* flags, int invisible=0); 328 | void deactivate(); 329 | 330 | // compatibility hack 331 | bool create(const char* title=NULL, const ContextFlags* cflags=0, int width=1024, int height=768); 332 | void setTitle(const char* title); 333 | void maximize(); 334 | void resize_window ( int w, int h ); 335 | void restore(); 336 | void minimize(); 337 | void postRedisplay(int n=1) { m_renderCnt=n; } 338 | void postQuit(); 339 | void makeContextCurrent(); 340 | void makeContextNonCurrent(); 341 | void swapBuffers(); 342 | void swapInterval(int i); 343 | bool isOpen(); 344 | void vsync (bool state); 345 | void setKeyPress ( int key, bool state ); 346 | void setFullscreen ( bool fullscreen ); 347 | void save_frame ( char* fname ); 348 | 349 | // from NVPWindow 350 | virtual bool init() { return true; } 351 | virtual void shutdown() {} 352 | virtual void reshape(int w, int h) { } 353 | virtual void motion(int x, int y, int dx, int dy) {} 354 | virtual void mousewheel(int delta) {} 355 | virtual void on_arg( std::string arg, std::string val ) {} 356 | virtual void mouse(MouseButton button, ButtonAction action, int mods, int x, int y) {} 357 | virtual void keyboard(KeyCode key, ButtonAction action, int mods, int x, int y) {} 358 | virtual void keyboardchar(unsigned char key, int mods, int x, int y) {} 359 | virtual void display() {} 360 | virtual bool begin() { return true; } 361 | virtual void end() {} 362 | virtual void checkpoint() {} 363 | 364 | // from WindowProfiler 365 | int run( const std::string &name, const std::string& shortname, int argc, const char** argv, int width, int height, int Major, int Minor, int MSAA, int GoldenFrame=0 ); 366 | void initGL (); 367 | void initScreenQuadGL (); 368 | void clearScreenGL (); 369 | void createScreenQuadGL ( int* glid, int w, int h ); 370 | void renderScreenQuadGL(int glid, char inv1 = 0); 371 | void compositeScreenQuadGL(int glid1, int glid2, char inv1 = 0, char inv2 = 0); 372 | void renderScreenQuadGL(int glid1, int glid2, float x1, float y1, float x2, float y2, char inv1 = 0, char inv2 = 0); 373 | 374 | ////////////////////////////////////////////////////////////////////////// 375 | // system related 376 | static void sysInit(); 377 | static void sysDeinit(); 378 | static bool sysPollEvents(bool bLoop); 379 | static void sysWaitEvents(); 380 | static NVPproc sysGetProcAddress(const char* name); 381 | static int sysExtensionSupported(const char* name); 382 | static double sysGetTime(); // in seconds 383 | static void sysSleep( double seconds ); 384 | static void sysVisibleConsole(); 385 | static std::string sysExePath(); 386 | }; 387 | 388 | extern int sample_main(int argc, const char**argv); 389 | 390 | // sample-specific implementation, called by nvprintfLevel. For example to redirect the message to a specific window or part of the viewport 391 | extern void sample_print(int level, const char * fmt2); 392 | 393 | extern void checkGL( char* msg ); 394 | 395 | // sample-specific implementation, called by nvprintf*. For example to redirect the message to a specific window or part of the viewport 396 | extern void sample_print(int level, const char * fmt); 397 | 398 | void nvprintf(const char * fmt, ...); 399 | void nvprintfLevel(int level, const char * fmt, ...); 400 | void nvprintSetLevel(int l); 401 | int nvprintGetLevel(); 402 | void nvprintSetLogging(bool b); 403 | void nverror(); 404 | 405 | // log files 406 | extern std::string mLogFile; 407 | extern FILE* mLogFP; 408 | void msgf(const char * fmt, ...); 409 | void logf(const char * fmt, ...); 410 | void errorf(const char * fmt, ...); 411 | void checkpointf ( ); 412 | void start_log(char* fname); 413 | 414 | #ifndef USE_STR_HELPER 415 | bool getFileLocation ( char* filename, char* outpath ); 416 | bool getFileLocation ( char* filename, char* outpath, std::vector paths ); 417 | #endif 418 | 419 | #endif 420 | -------------------------------------------------------------------------------- /helpers/nvToolsExt64_1.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ramakarl/invk/a2141ee9b8e5d4fe98d0093a23d396b682d87f21/helpers/nvToolsExt64_1.dll -------------------------------------------------------------------------------- /helpers/nv_gui.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | // 23 | // Functionality in this file: 24 | // - nvMesh: Construct, load, and render meshes. PLY format supported 25 | // - nvImg: Cosntruct, load, and render images. PNG and TGA format supported 26 | // - nvDraw: A lightweight, efficient, 2D drawing API. Uses VBOs to render 27 | // lines, circles, triangles, and text. Allows for both static and dynamic 28 | // groups (rewrite per frame), and immediate style (out-of-order) calling. 29 | // - nvGui: A lightweight class for creating on-screen GUIs. Currently only checkboxes 30 | // or sliders supported. Relies on nvDraw to render. 31 | // 32 | 33 | #ifndef DEF_NV_GUI 34 | #define DEF_NV_GUI 35 | 36 | #include 37 | #include 38 | #include 39 | typedef GLuint BUF; 40 | typedef GLuint TEX; 41 | typedef unsigned int uint; 42 | #define UINT_NULL 0xFFFFFFFF 43 | #define IDX_NULL 0xFF000000 // OpenGL - primitive restart index 44 | 45 | #ifdef USE_GVDB 46 | #include "gvdb_camera.h" 47 | using namespace nvdb; 48 | #else 49 | #include "camera.h" 50 | #endif 51 | 52 | 53 | //-------------------------------------- PLY FORMAT 54 | struct PlyProperty { // PLY Format structures 55 | char type; 56 | std::string name; 57 | }; 58 | struct PlyElement { 59 | int num; 60 | char type; // 0 = vert, 1 = face 61 | std::vector prop_list; 62 | }; 63 | typedef unsigned long xref; 64 | 65 | #define PLY_UINT 0 // PLY Format constants 66 | #define PLY_INT 1 67 | #define PLY_FLOAT 2 68 | #define PLY_LIST 3 69 | #define PLY_VERTS 4 70 | #define PLY_FACES 5 71 | 72 | #define attrPos 3 73 | #define attrClr 4 74 | #define attrUV 5 75 | #define attrPos2 6 76 | 77 | struct Vertex { 78 | float x, y, z; 79 | float nx, ny, nz; 80 | float tx, ty, tz; 81 | }; 82 | 83 | //------------------------------------ nvMesh 84 | class nvMesh { 85 | public: 86 | nvMesh(); 87 | 88 | void Clear (); 89 | void AddPlyElement ( char typ, int n ); 90 | void AddPlyProperty ( char typ, std::string name ); 91 | int AddVert ( float x, float y, float z, float tx, float ty, float tz ); 92 | void SetVert ( int n, float x, float y, float z, float tx, float ty, float tz ); 93 | void SetNormal ( int n, float x, float y, float z ); 94 | int AddFace ( int v0, int v1, int v2 ); 95 | int AddFace4 ( int v0, int v1, int v2, int v3 ); 96 | int FindPlyElem ( char typ ); 97 | int FindPlyProp ( int elem, std::string name ); 98 | bool LoadPly ( char* fname, float scal ); // Load PLY 99 | void ComputeNormals (); // Compute surface normals 100 | void UpdateVBO ( bool rebuild, int cnt = 1); // Update GPU buffers 101 | void SelectVBO (); 102 | void SelectVAO (); 103 | void Draw ( int inst ); // Draw 104 | void DrawPatches ( int inst ); 105 | 106 | int getNumFaces () { return (int) mNumFaces; } 107 | int getNumVerts () { return (int) mVertices.size(); } 108 | 109 | private: 110 | 111 | std::vector mVertices; // Vertices 112 | std::vector mFaceVN; // Face data (must be uniformly 3 or 4 sided) 113 | int mNumFaces; // Num faces 114 | int mNumSides; // Must be 3 or 4 115 | std::vector mVBO; // Buffers 116 | BUF mVAO; 117 | 118 | std::vector< PlyElement* > m_Ply; // Ply loading 119 | int m_PlyCurrElem; 120 | int localPos, localNorm, localUV, localPos2; 121 | }; 122 | 123 | //------------------------------------ nvImg 124 | #define IMG_RGB 0 125 | #define IMG_RGBA 1 126 | #define IMG_GREY16 2 127 | 128 | class nvImg { 129 | public: 130 | nvImg(); 131 | ~nvImg(); 132 | void Create ( int x, int y, int fmt ); 133 | void Fill ( float r, float g, float b, float a ); 134 | bool LoadPng ( char* fname, bool bGray=false ); // Load PNG 135 | bool LoadTga ( char* fname ); // Load TGA 136 | void FlipY (); 137 | void SavePng ( char* fname ); 138 | void UpdateTex (); // Update GPU texture 139 | void BindTex (); // Bind GPU texture 140 | TEX getTex() { return mTex;} 141 | int getSize () { return mSize;} 142 | int getWidth() { return mXres;} 143 | int getHeight() { return mYres;} 144 | unsigned char* getData() { return mData;} 145 | 146 | private: 147 | int mXres, mYres; 148 | int mSize, mFmt; 149 | unsigned char* mData; 150 | 151 | TEX mTex; 152 | #ifdef USE_DX 153 | ID3D11ShaderResourceView* mTexIV; 154 | #endif 155 | }; 156 | 157 | //-------------------------------------- Fonts (Tristan Lorach) 158 | // Read from .bin files 159 | struct GlyphInfo { 160 | struct Pix { // pixel oriented data 161 | int u, v; 162 | int width, height; 163 | int advance; 164 | int offX, offY; 165 | }; 166 | struct Norm { // normalized data 167 | float u, v; // position in the map in normalized coords 168 | float width, height; 169 | float advance; 170 | float offX, offY; 171 | }; 172 | Pix pix; 173 | Norm norm; 174 | }; 175 | struct FileHeader { 176 | int texwidth, texheight; 177 | struct Pix { 178 | int ascent; 179 | int descent; 180 | int linegap; 181 | }; 182 | struct Norm { 183 | float ascent; 184 | float descent; 185 | float linegap; 186 | }; 187 | Pix pix; 188 | Norm norm; 189 | GlyphInfo glyphs[256]; 190 | }; 191 | 192 | #define GRP_TRI 0 193 | #define GRP_TRITEX 1 194 | #define GRP_LINES 2 195 | #define GRP_IMG 3 196 | #define GRP_BOX 4 197 | #define GRP_MAX 5 198 | 199 | #ifndef xlong 200 | typedef signed long long xlong; // 64-bit integer 201 | #endif 202 | 203 | //-------------------------------------- 2D DRAW SETS 204 | struct nvVert { 205 | float x, y, z; 206 | float r, g, b, a; 207 | float tx, ty; 208 | }; 209 | struct nvSet { 210 | nvSet () { mImgs=0; for (int n=0; n mStatic; // static 2D draw - saved frame-to-frame 291 | std::vector mDynamic; // dynamic 2D draw - discarded each frame 292 | std::vector m3D; // 3D draw 293 | int mCurrZ; 294 | int mCurr, mDynNum, m3DNum; 295 | nvSet* mCurrSet; 296 | nvSet mBoxSet; // box geometry 297 | float mWidth, mHeight; 298 | float mTextScale, mTextKern; 299 | float mModelMtx[16], mViewMtx[16], mProjMtx[16]; 300 | double mZFactor; 301 | 302 | #ifdef USE_DX 303 | ID3D11Buffer* mpMatrixBuffer[3]; // 2D model/view/proj 304 | ID3D11VertexShader* mVS; // 2D shader 305 | ID3D11PixelShader* mPS; 306 | ID3D11InputLayout* mLO; // 2D layout 307 | 308 | #else 309 | GLuint mSH[3]; // Shaders 310 | GLint mModel[3], mProj[3]; // Shader parameters 311 | GLint mView[3], mFont[3]; 312 | GLint mLight[3]; 313 | GLuint mVAO; 314 | #endif 315 | 316 | nvImg mFontImg, mWhiteImg; 317 | FileHeader mGlyphInfos; 318 | }; 319 | 320 | //-------------------------------------- nvGui (2D GUIS) 321 | 322 | #define GUI_PRINT 0 323 | #define GUI_SLIDER 1 324 | #define GUI_CHECK 2 325 | #define GUI_TEXT 3 326 | #define GUI_COMBO 4 327 | #define GUI_TOOLBAR 5 328 | #define GUI_ICON 6 329 | 330 | #define GUI_NULL 255 331 | #define GUI_BOOL 0 332 | #define GUI_INT 1 333 | #define GUI_FLOAT 2 334 | #define GUI_STR 3 335 | #define GUI_VEC3 4 336 | 337 | struct Gui { 338 | float x, y, w, h; 339 | int gtype; 340 | std::string name; 341 | int dtype; 342 | void* data; 343 | float vmin, vmax; 344 | int size; 345 | bool changed; 346 | Vector3DF backclr; 347 | float backalpha; 348 | std::vector items; 349 | std::vector imgs; 350 | }; 351 | 352 | class nvGui { 353 | public: 354 | nvGui (); 355 | ~nvGui(); 356 | 357 | int AddGui ( float x, float y, float w, float h, char* name, int gtype, int dtype, void* data, float vmin, float vmax ); 358 | int AddItem ( char* name, char* imgname = 0x0 ); 359 | void SetBackclr ( float r, float g, float b, float a ); 360 | bool guiChanged ( int n ); 361 | std::string getItemName ( int g, int v ); 362 | bool MouseDown ( float x, float y ); 363 | bool MouseUp ( float x, float y ); 364 | bool MouseDrag ( float x, float y ); 365 | void Draw ( nvImg* img ); 366 | void Clear (); 367 | 368 | private: 369 | std::vector mGui; 370 | int mActiveGui; 371 | }; 372 | 373 | // Public functions 374 | // 375 | extern void enable_nvgui(); // start 376 | extern void disable_nvgui(); // stop 377 | 378 | extern nvDraw* g_2D; 379 | extern bool init2D ( const char* fontName ); 380 | extern void start2D (); 381 | extern void start2D (bool bStatic); 382 | extern void static2D (); 383 | extern void end2D (); 384 | extern void draw2D (); 385 | extern void setview2D ( int w, int h ); 386 | extern void setview2D ( float* model, float* view, float* proj ); 387 | extern void setorder2D ( bool zt, float zfactor ); 388 | extern void updatestatic2D ( int n ); 389 | extern void setText ( float scale, float kern ); 390 | extern float getTextX ( char* msg ); 391 | extern float getTextY ( char* msg ); 392 | extern void drawLine ( float x1, float y1, float x2, float y2, float r, float g, float b, float a ); 393 | extern void drawRect ( float x1, float y1, float x2, float y2, float r, float g, float b, float a ); 394 | extern void drawImg ( nvImg* img, float x1, float y1, float x2, float y2, float r, float g, float b, float a ); 395 | extern void drawFill ( float x1, float y1, float x2, float y2, float r, float g, float b, float a ); 396 | extern void drawTri ( float x1, float y1, float x2, float y2, float x3, float y3, float r, float g, float b, float a ); 397 | extern void drawCircle ( float x1, float y1, float radius, float r, float g, float b, float a ); 398 | extern void drawCircleDash ( float x1, float y1, float radius, float r, float g, float b, float a ); 399 | extern void drawCircleFill ( float x1, float y1, float radius, float r, float g, float b, float a ); 400 | extern void drawText ( float x1, float y1, char* msg, float r, float g, float b, float a ); 401 | 402 | extern void start3D ( Camera3D* cam ); 403 | extern void drawLine3D ( float x1, float y1, float z1, float x2, float y2, float z2, float r, float g, float b, float a ); 404 | extern void drawBox3D ( Vector3DF p, Vector3DF pa, Vector3DF pb, Matrix4F xform, float r, float g, float b, float a ); 405 | extern void drawBox3D ( float x1, float y1, float z1, float x2, float y2, float z2, float r, float g, float b, float a ); 406 | extern void end3D (); 407 | extern void draw3D (); 408 | extern void drawGL (); 409 | extern void setLight (int s, float x1, float y1, float z1 ); 410 | 411 | typedef void (*CallbackFunc)(int, float); 412 | extern nvGui* g_Gui; 413 | extern void drawGui ( nvImg* img ); 414 | extern void clearGuis (); 415 | extern int addGui ( int x, int y, int w, int h, char* name, int gtype, int dtype, void* data, float vmin, float vmax ); 416 | extern int addItem ( char* name, char* imgname = 0x0 ); 417 | extern void setBackclr ( float r, float g, float b, float a ); 418 | extern std::string guiItemName ( int n, int v ); 419 | extern bool guiHandler ( int button, int action, int x, int y ); 420 | extern bool guiMouseDown ( float x, float y ); 421 | extern bool guiMouseUp ( float x, float y ); 422 | extern bool guiMouseDrag ( float x, float y ); 423 | extern bool guiChanged ( int n ); 424 | extern void guiSetCallback ( CallbackFunc f ); 425 | 426 | #endif -------------------------------------------------------------------------------- /helpers/string_helper.cpp: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation. 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | // 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer 9 | // in the documentation and/or other materials provided with the distribution. 10 | // 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived 11 | // from this software without specific prior written permission. 12 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 13 | // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 14 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 15 | // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 16 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 17 | // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | // 19 | // Version 1.0: Rama Hoetzlein, 5/1/2017 20 | //---------------------------------------------------------------------------------- 21 | 22 | #include "vec.h" 23 | #include "string_helper.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include // needed for WideCharToMultiByte 30 | 31 | int strToI (std::string s) { 32 | //return ::atoi ( s.c_str() ); 33 | std::istringstream str_stream ( s ); 34 | int x; 35 | if (str_stream >> x) return x; // this is the correct way to convert std::string to int, do not use atoi 36 | return 0; 37 | }; 38 | float strToF (std::string s) { 39 | //return ::atof ( s.c_str() ); 40 | std::istringstream str_stream ( s ); 41 | float x; 42 | if (str_stream >> x) return x; // this is the correct way to convert std::string to float, do not use atof 43 | return 0; 44 | }; 45 | double strToD (std::string s) { 46 | //return ::atof ( s.c_str() ); 47 | std::istringstream str_stream ( s ); 48 | double x; 49 | if (str_stream >> x) return x; // this is the correct way to convert std::string to float, do not use atof 50 | return 0; 51 | }; 52 | unsigned char strToC ( std::string s ) { 53 | char c; 54 | memcpy ( &c, s.c_str(), 1 ); // cannot use atoi here. atoi only returns numbers for strings containing ascii numbers. 55 | return c; 56 | }; 57 | // Return 4-byte long int whose bytes 58 | // match the first 4 ASCII chars of string given. 59 | unsigned long strToID ( std::string str ) 60 | { 61 | str = str + " "; 62 | return (static_cast(str.at(0)) << 24) | 63 | (static_cast(str.at(1)) << 16) | 64 | (static_cast(str.at(2)) << 8) | 65 | (static_cast(str.at(3)) ); 66 | } 67 | 68 | /*Vector4DF strToV4 ( std::string val ) 69 | { 70 | Vector4DF v(0,0,0,0); 71 | size_t pos1 = val.find (','); 72 | size_t pos2 = val.find (',', pos1+1); 73 | size_t pos3 = val.find (',', pos2+1); 74 | 75 | if ( pos1 != std::string::npos && pos2 != std::string::npos && pos3 != std::string::npos ) { 76 | v.x = atof ( val.substr ( 0, pos1 ).c_str() ); 77 | v.y = atof ( val.substr ( pos1+1, pos2-pos1 ).c_str() ); 78 | v.z = atof ( val.substr ( pos2+1, pos3-pos2 ).c_str() ); 79 | v.w = atof ( val.substr( pos3+1 ).c_str() ); 80 | } 81 | return v; 82 | }*/ 83 | 84 | objType strToType ( std::string str ) 85 | { 86 | char buf[5]; 87 | strcpy ( buf, str.c_str() ); 88 | objType name; 89 | ((char*) &name)[3] = buf[0]; 90 | ((char*) &name)[2] = buf[1]; 91 | ((char*) &name)[1] = buf[2]; 92 | ((char*) &name)[0] = buf[3]; 93 | return name; 94 | } 95 | 96 | std::string typeToStr ( objType name ) // static function 97 | { 98 | char buf[5]; 99 | buf[0] = ((char*) &name)[3]; 100 | buf[1] = ((char*) &name)[2]; 101 | buf[2] = ((char*) &name)[1]; 102 | buf[3] = ((char*) &name)[0]; 103 | buf[4] = '\0'; 104 | return std::string ( buf ); 105 | } 106 | 107 | 108 | std::string cToStr ( char c ) 109 | { 110 | char buf[2]; 111 | buf[0] = c; 112 | buf[1] = '\0'; 113 | return std::string ( buf ); 114 | } 115 | 116 | std::string iToStr ( int i ) 117 | { 118 | std::ostringstream ss; 119 | ss << i; 120 | return ss.str(); 121 | } 122 | std::string fToStr ( float f ) 123 | { 124 | std::ostringstream ss; 125 | ss << f; 126 | return ss.str(); 127 | } 128 | 129 | 130 | std::string strFilebase ( std::string str ) 131 | { 132 | size_t pos = str.find_last_of ( '.' ); 133 | if ( pos != std::string::npos ) 134 | return str.substr ( 0, pos ); 135 | return str; 136 | } 137 | std::string strFilepath ( std::string str ) 138 | { 139 | size_t pos = str.find_last_of ( '\\' ); 140 | if ( pos != std::string::npos ) 141 | return str.substr ( 0, pos+1 ); 142 | return str; 143 | } 144 | 145 | // Parse string inside two separators. Input becomes output exterior. Return is interior. 146 | // e.g. object --> str: object, return: 'car' 147 | std::string strParse ( std::string& str, std::string lsep, std::string rsep ) 148 | { 149 | std::string result; 150 | size_t lfound, rfound; 151 | 152 | lfound = str.find_first_of ( lsep ); 153 | if ( lfound != std::string::npos) { 154 | rfound = str.find_first_of ( rsep, lfound+1 ); 155 | if ( rfound != std::string::npos ) { 156 | result = str.substr ( lfound+1, rfound-lfound-1 ); // return string strickly between lsep and rsep 157 | str = str.substr ( 0, lfound ) + str.substr ( rfound+1 ); 158 | return result; 159 | } 160 | } 161 | return str; 162 | } 163 | 164 | std::string strParse ( std::string str, std::string lstr, std::string rstr, std::string lsep, std::string rsep ) 165 | { 166 | std::string result; 167 | size_t lfound, rfound; 168 | 169 | lfound = str.find_first_of ( lsep ); 170 | if ( lfound != std::string::npos) { 171 | rfound = str.find_first_of ( rsep, lfound+1 ); 172 | if ( rfound != std::string::npos ) { 173 | result = str.substr ( lfound+1, rfound-lfound-1 ); // return string strickly between lsep and rsep 174 | lstr = str.substr ( 0, lfound ); 175 | rstr = str.substr ( rfound+1 ); 176 | return result; 177 | } 178 | } 179 | return ""; 180 | } 181 | 182 | 183 | // Get string from inside two separators. Input is unchanged. 184 | // e.g. object --> result: car 185 | bool strGet ( std::string str, std::string& result, std::string lsep, std::string rsep ) 186 | { 187 | size_t lfound, rfound; 188 | 189 | lfound = str.find_first_of ( lsep ); 190 | if ( lfound != std::string::npos) { 191 | rfound = str.find_first_of ( rsep, lfound+1 ); 192 | if ( rfound != std::string::npos ) { 193 | result = str.substr ( lfound+1, rfound-lfound-1 ); // return string strickly between lsep and rsep 194 | return true; 195 | } 196 | } 197 | return false; 198 | } 199 | 200 | // strGet - return a substring between two separators, without modifying input string. 201 | // input: strGet ( "hello(world)", "(", ")", result, pos ) 202 | // output: result = "world", pos=7 203 | bool strGet ( const std::string& str, std::string lsep, std::string rsep, std::string& result, size_t& pos ) 204 | { 205 | size_t lfound, rfound; 206 | lfound = str.find_first_of ( lsep ); 207 | if ( lfound != std::string::npos ) { 208 | rfound = str.find_first_of ( rsep, lfound+1 ); 209 | if ( rfound != std::string::npos ) { 210 | result = str.substr ( lfound, rfound-lfound+1 ); 211 | pos = lfound; 212 | return true; 213 | } 214 | } 215 | return false; 216 | } 217 | 218 | 219 | // Get string split on separator. Input becomes output right side. Return is left side. 220 | // e.g. object:car --> str: car, result: object 221 | std::string strSplit ( std::string& str, std::string sep ) 222 | { 223 | std::string result; 224 | size_t f1, f2; 225 | 226 | f1 = str.find_first_not_of ( sep ); 227 | if ( f1 == std::string::npos ) f1 = 0; 228 | f2 = str.find_first_of ( sep, f1 ); 229 | if ( f2 != std::string::npos) { 230 | result = str.substr ( f1, f2-f1 ); 231 | str = str.substr ( f2+1 ); 232 | } else { 233 | result = str; 234 | str = ""; 235 | } 236 | return result; 237 | } 238 | 239 | bool strSplit ( std::string& str, std::string result, std::string sep ) 240 | { 241 | size_t found; 242 | found = str.find_first_of ( sep ); 243 | if ( found == std::string::npos) 244 | return false; 245 | 246 | str = str.substr ( found+1 ); 247 | result = str.substr ( 0, found ); 248 | return true; 249 | } 250 | 251 | // Split a string into multiple words deliminated by sep 252 | std::vector strSplitMultiple ( std::string str, std::string sep ) 253 | { 254 | std::vector words; 255 | size_t f1; 256 | f1 = str.find_first_of ( sep ); 257 | 258 | while ( f1 != std::string::npos ) { 259 | words.push_back ( str.substr(0,f1) ); 260 | str = str.substr ( f1+1 ); 261 | f1 = str.find_first_of ( sep ); 262 | } 263 | return words; 264 | } 265 | 266 | bool strFileSplit ( std::string str, std::string& path, std::string& name, std::string& ext ) 267 | { 268 | size_t slash = str.find_last_of ( "/\\" ); 269 | if ( slash != std::string::npos ) { 270 | path = str.substr ( 0, slash ); 271 | str = str.substr ( slash+1 ); 272 | } else { 273 | path = ""; 274 | } 275 | size_t dot = str.find_last_of ( '.' ); 276 | if ( dot != std::string::npos ) { 277 | name = str.substr ( 0, dot ); 278 | ext = str.substr ( dot+1 ); 279 | } else { 280 | name = str; 281 | ext = ""; 282 | } 283 | return true; 284 | } 285 | 286 | // Get string split on separator. Input becomes output left side. Return is right side. 287 | std::string strSplitArg ( std::string& str, std::string sep ) 288 | { 289 | std::string result; 290 | size_t f1, f2; 291 | 292 | f1 = str.find_first_not_of ( sep ); 293 | if ( f1 == std::string::npos ) f1 = 0; 294 | f2 = str.find_first_of ( sep, f1 ); 295 | if ( f2 != std::string::npos) { 296 | result = str.substr ( f2+1 ); 297 | str = str.substr ( f1, f2-f1 ); 298 | } else { 299 | result = ""; 300 | } 301 | return result; 302 | } 303 | // Parse a list as a set of key-value arguments. Input becomes output right side. Return is arg value. Tag is arg tag. 304 | // e.g. object:cat, name:felix -> str=name:felix, return=cat, tag=object 305 | std::string strParseArg ( std::string& tag, std::string argsep, std::string sep, std::string& str ) 306 | { 307 | std::string result; 308 | size_t f1, f2, f3; 309 | f1 = str.find_first_not_of ( " \t" ); 310 | if ( f1 == std::string::npos ) f1=0; 311 | f2 = str.find_first_of ( argsep, f1 ); 312 | if ( f2 == std::string::npos ) f2=f1; 313 | f3 = str.find_first_of ( sep, f2 ); 314 | if ( f3 != std::string::npos ) { 315 | tag = strTrim ( str.substr ( f1, f2-f1 ) ); 316 | result = strTrim ( str.substr ( f2+1, f3-f2-1 ) ); 317 | str = str.substr ( f3+1 ); 318 | } else { 319 | tag = ""; 320 | str = ""; 321 | result = ""; 322 | } 323 | return result; 324 | } 325 | 326 | 327 | std::string strParseFirst ( std::string& str, std::string sep, std::string others, char& ch ) 328 | { 329 | std::string result; 330 | size_t lfound, ofound; 331 | 332 | lfound = str.find_first_of ( sep ); 333 | ofound = str.find_first_of ( others ); 334 | lfound = ( lfound==std::string::npos || ( ofound!=std::string::npos && ofound& list, int& pos ) 403 | { 404 | size_t posL; 405 | for (int n=0; n < list.size(); n++ ) { 406 | posL = str.find ( list[n] ); 407 | if ( posL != std::string::npos ) { 408 | pos = (int) posL; 409 | return n; 410 | } 411 | } 412 | return -1; // not found 413 | } 414 | 415 | 416 | bool strIsNum ( std::string str, float& f ) 417 | { 418 | if (str.empty()) return false; 419 | std::string::iterator it; 420 | char ch; 421 | for (it = str.begin(); it != str.end(); it++ ) { 422 | ch = (*it); 423 | if ( ch!='.' && ch!='-' && ch!='0' && ch!='1' && ch!='2' && ch!='3' && ch!='4' && ch!='5' && ch!='6' && ch!='7' && ch!='8' && ch!='9' ) 424 | break; 425 | } 426 | if ( it==str.end() ) { 427 | f = atof ( str.c_str() ); 428 | return true; 429 | } 430 | return false; 431 | } 432 | 433 | float strToNum ( std::string str ) 434 | { 435 | return (float) atof ( str.c_str() ); 436 | } 437 | bool strToVec ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec, int cpt ) 438 | { 439 | std::string val; 440 | char sep = insep.at(0); 441 | int vc = 0; 442 | 443 | std::string vstr = strParse ( str, lsep, rsep ); 444 | if ( vstr.empty() ) return false; 445 | 446 | for (int j=0; j < vstr.length(); j++ ) { 447 | if ( vstr[j]==sep ) { 448 | vec[vc++] = strToNum(val); 449 | if ( vc >= cpt ) return true; 450 | val = ""; 451 | } else { 452 | val += vstr[j]; 453 | } 454 | } 455 | if ( !val.empty() ) vec[vc] = strToNum(val); 456 | return true; 457 | } 458 | 459 | bool strToVec3 ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec ) 460 | { 461 | return strToVec ( str, lsep, insep, rsep, vec, 3 ); 462 | } 463 | bool strToVec4 ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec ) 464 | { 465 | return strToVec ( str, lsep, insep, rsep, vec, 4 ); 466 | } 467 | 468 | 469 | std::string wsToStr ( const std::wstring& str ) 470 | { 471 | #ifdef _MSC_VER 472 | int len = WideCharToMultiByte ( CP_ACP, 0, str.c_str(), (int) str.length(), 0, 0, 0, 0); 473 | char* buf = new char[ len+1 ]; 474 | memset ( buf, '\0', len+1 ); 475 | WideCharToMultiByte ( CP_ACP, 0, str.c_str(), (int) str.length(), buf, len+1, 0, 0); 476 | #else 477 | int len = wcstombs( NULL, str.c_str(), 0 ); 478 | char* buf = new char[ len ]; 479 | wcstombs( buf, str.c_str(), len ); 480 | #endif 481 | std::string r(buf); 482 | delete[] buf; 483 | return r; 484 | } 485 | 486 | std::wstring strToWs (const std::string& s) 487 | { 488 | wchar_t* buf = new wchar_t[ s.length()+1 ]; 489 | 490 | #ifdef _MSC_VER 491 | MultiByteToWideChar(CP_ACP, 0, s.c_str(), -1, buf, (int) s.length()+1 ); 492 | #else 493 | mbstowcs( buf, s.c_str(), s.length() + 1 ); 494 | #endif 495 | std::wstring r(buf); 496 | delete[] buf; 497 | return r; 498 | } 499 | 500 | 501 | // trim from start 502 | #include 503 | #include 504 | 505 | /*std::string strLTrim(std::string str) { 506 | str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun(std::isspace)))); 507 | return str; 508 | } 509 | 510 | // trim from end 511 | std::string strRTrim(std::string str) { 512 | str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun(std::isspace))).base(), str.end()); 513 | return str; 514 | } */ 515 | 516 | // trim from both ends 517 | std::string strTrim(std::string str) 518 | { 519 | size_t lft = str.find_first_not_of ( " \t\r\n" ); 520 | size_t rgt = str.find_last_not_of ( " \t\r\n" ); 521 | if ( lft == std::string::npos || rgt == std::string::npos ) return ""; 522 | return str.substr ( lft, rgt-lft+1 ); 523 | } 524 | 525 | std::string strTrim ( std::string str, std::string ch ) 526 | { 527 | size_t found1 = str.find_first_not_of ( ch ); 528 | size_t found2; 529 | if ( found1 != std::string::npos ) { 530 | str = str.substr ( found1 ); 531 | found2 = str.find_last_not_of ( ch ); 532 | if ( found2 != std::string::npos ) 533 | str = str.substr ( 0, found2+1 ); 534 | } else { 535 | found2 = str.find_last_not_of ( ch ); 536 | if ( found2 != std::string::npos ) 537 | str = str.substr ( 0, found2+1 ); 538 | else 539 | str = ""; 540 | } 541 | return str; 542 | } 543 | 544 | 545 | std::string strLeft ( std::string str, int n ) 546 | { 547 | return str.substr ( 0, n ); 548 | } 549 | std::string strRight ( std::string str, int n ) 550 | { 551 | if ( str.length() < n ) return ""; 552 | return str.substr ( str.length()-n, n ); 553 | } 554 | 555 | std::string strLeftOf ( std::string str, std::string sep ) 556 | { 557 | size_t f0 = str.find_first_of(sep); 558 | if ( f0 == std::string::npos ) return str; 559 | return str.substr(0, f0); 560 | } 561 | std::string strMidOf ( std::string str, std::string sep ) 562 | { 563 | size_t f0 = str.find_first_of(sep); 564 | size_t f1 = str.find_last_of(sep); 565 | if ( f0 == std::string::npos || f1==std::string::npos) return ""; 566 | return str.substr(f0+1, f1-f0-1); 567 | } 568 | std::string strRightOf ( std::string str, std::string sep ) 569 | { 570 | size_t f0 = str.find_last_of(sep); 571 | if ( f0 == std::string::npos ) return ""; 572 | return str.substr(f0+sep.length()); 573 | } 574 | 575 | int strExtract ( std::string& str, std::vector& list ) 576 | { 577 | size_t found ; 578 | for (int n=0; n < list.size(); n++) { 579 | found = str.find ( list[n] ); 580 | if ( found != std::string::npos ) { 581 | str = str.substr ( 0, found ) + str.substr ( found + list[n].length() ); 582 | return n; 583 | } 584 | } 585 | return -1; 586 | } 587 | 588 | unsigned long getFileSize ( char* fname ) 589 | { 590 | FILE* fp; 591 | fopen_s ( &fp, fname, "rb" ); 592 | fseek ( fp, 0, SEEK_END ); 593 | return ftell ( fp ); 594 | } 595 | unsigned long getFilePos ( FILE* fp ) 596 | { 597 | return ftell ( fp ); 598 | } 599 | 600 | bool getFileLocation ( std::string filename, std::string& outpath ) 601 | { 602 | char instr[2048]; 603 | char outstr[2048]; 604 | strncpy_s (instr, filename.c_str(), 2048 ); 605 | bool result = getFileLocation ( instr, outstr ); 606 | outpath = outstr; 607 | return result; 608 | } 609 | 610 | bool getFileLocation ( char* filename, char* outpath ) 611 | { 612 | std::vector paths; 613 | paths.push_back ( "./"); 614 | paths.push_back ( ASSET_PATH ); 615 | bool result = getFileLocation ( filename, outpath, paths ); 616 | return result; 617 | } 618 | 619 | bool getFileLocation ( char* filename, char* outpath, std::vector searchPaths ) 620 | { 621 | bool found = false; 622 | FILE* fp = 0; 623 | fopen_s ( &fp, filename, "rb" ); 624 | if (fp) { 625 | found = true; 626 | strcpy ( outpath, filename ); 627 | } else { 628 | for (int i=0; i < searchPaths.size(); i++) { 629 | if (searchPaths[i].empty() ) continue; 630 | sprintf ( outpath, "%s%s", searchPaths[i].c_str(), filename ); 631 | fp = fopen( outpath, "rb" ); 632 | if (fp) { found = true; break; } 633 | } 634 | } 635 | if ( found ) fclose ( fp ); 636 | return found; 637 | } 638 | 639 | 640 | 641 | std::string readword ( char* line, char delim ) 642 | { 643 | char word_buf[8192]; 644 | 645 | if ( readword ( line, delim, word_buf, 8192 ) ) return word_buf; 646 | 647 | return ""; 648 | } 649 | 650 | 651 | bool readword ( char *line, char delim, char *word, int max_size ) 652 | { 653 | char *buf_pos; 654 | char *start_pos; 655 | 656 | // read past spaces/tabs, or until end of line/string 657 | for (buf_pos=line; (*buf_pos==' ' || *buf_pos=='\t') && *buf_pos!='\n' && *buf_pos!='\0';) 658 | buf_pos++; 659 | 660 | // if end of line/string found, then no words found, return null 661 | if (*buf_pos=='\n' || *buf_pos=='\0') {*word = '\0'; return false;} 662 | 663 | // mark beginning of word, read until end of word 664 | for (start_pos = buf_pos; *buf_pos != delim && *buf_pos!='\t' && *buf_pos!='\n' && *buf_pos!='\0';) 665 | buf_pos++; 666 | 667 | if (*buf_pos=='\n' || *buf_pos=='\0') { // buf_pos now points to the end of buffer 668 | strncpy_s (word, max_size, start_pos, max_size); // copy word to output string 669 | if ( *buf_pos=='\n') *(word + strlen(word)-1) = '\0'; 670 | *line = '\0'; // clear input buffer 671 | } else { 672 | // buf_pos now points to the delimiter after word 673 | *buf_pos++ = '\0'; // replace delimiter with end-of-word marker 674 | strncpy_s ( word, max_size, start_pos, (long long) (buf_pos-line) ); // copy word(s) string to output string 675 | // move start_pos to beginning of entire buffer 676 | strcpy ( start_pos, buf_pos ); // copy remainder of buffer to beginning of buffer 677 | } 678 | return true; // return word(s) copied 679 | } 680 | 681 | 682 | 683 | -------------------------------------------------------------------------------- /helpers/string_helper.h: -------------------------------------------------------------------------------- 1 | //-------------------------------------------------------------------------------- 2 | // NVIDIA(R) GVDB VOXELS 3 | // Copyright 2017, NVIDIA Corporation 4 | // 5 | // Redistribution and use in source and binary forms, with or without modification, 6 | // are permitted provided that the following conditions are met: 7 | // 1. Redistributions of source code must retain the above copyright notice, this 8 | // list of conditions and the following disclaimer. 9 | // 2. Redistributions in binary form must reproduce the above copyright notice, this 10 | // list of conditions and the following disclaimer in the documentation and/or 11 | // other materials provided with the distribution. 12 | // 3. Neither the name of the copyright holder nor the names of its contributors may 13 | // be used to endorse or promote products derived from this software without specific 14 | // prior written permission. 15 | // 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 17 | // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 | // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 19 | // SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 21 | // OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 | // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 23 | // TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 24 | // EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | // 26 | // Version 1.0: Rama Hoetzlein, 5/1/2017 27 | //---------------------------------------------------------------------------------- 28 | 29 | #ifndef DEF_STRING_HELPER 30 | #define DEF_STRING_HELPER 31 | 32 | #include 33 | #include 34 | 35 | #ifndef DEF_OBJTYPE 36 | typedef uint32_t objType; 37 | #endif 38 | std::string strFilebase ( std::string str ); // basename of a file (minus ext) 39 | std::string strFilepath ( std::string str ); // path of a file 40 | 41 | // convert 42 | int strToI (std::string s); 43 | float strToF (std::string s); 44 | double strToD (std::string s); 45 | unsigned char strToC ( std::string s ); 46 | unsigned long strToID ( std::string str ); 47 | bool strToVec ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec, int cpt=3); 48 | bool strToVec3 ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec ); 49 | bool strToVec4 ( std::string& str, std::string lsep, std::string insep, std::string rsep, float* vec ); 50 | std::string cToStr ( char c ); 51 | std::string iToStr ( int i ); 52 | std::string fToStr ( float f ); 53 | objType strToType ( std::string str ); 54 | std::string typeToStr ( objType t ); 55 | std::string wsToStr ( const std::wstring& str ); 56 | std::wstring strToWs (const std::string& s); 57 | 58 | // parse 59 | std::string strParse ( std::string& str, std::string lsep, std::string rsep ); 60 | std::string strParse ( std::string str, std::string lstr, std::string rstr, std::string lsep, std::string rsep ); 61 | std::string strParseArg ( std::string& tag, std::string valsep, std::string sep, std::string& str ); 62 | std::string strParseFirst ( std::string& str, std::string sep, std::string others, char& ch ); 63 | bool strGet ( std::string str, std::string& result, std::string lsep, std::string rsep ); 64 | bool strGet ( const std::string& s, std::string lsep, std::string rsep, std::string& result, size_t& pos ); 65 | std::string strSplit ( std::string& str, std::string sep ); 66 | bool strSplit ( std::string& str, std::string dest, std::string sep ); 67 | std::string strSplitArg ( std::string& str, std::string sep ); 68 | bool strSub ( std::string str, int first, int cnt, std::string cmp ); 69 | std::string strReplace ( std::string str, std::string src, std::string dest ); 70 | bool strReplace ( std::string& str, std::string src, std::string dest, int& cnt ); 71 | int strExtract ( std::string& str, std::vector& list ); 72 | int strFindFromList ( std::string str, std::vector& list, int& pos ); 73 | bool strEmpty ( const std::string& s); 74 | std::vector strSplitMultiple ( std::string str, std::string sep ); 75 | bool strFileSplit ( std::string str, std::string& path, std::string& name, std::string& ext ); 76 | 77 | // trim 78 | std::string strLTrim ( std::string str ); 79 | std::string strRTrim ( std::string str ); 80 | std::string strTrim ( std::string str ); 81 | std::string strTrim ( std::string str, std::string ch ); 82 | std::string strLeft ( std::string str, int n ); 83 | std::string strRight ( std::string str, int n ); 84 | std::string strLeftOf ( std::string str, std::string sep ); 85 | std::string strMidOf ( std::string str, std::string sep ); 86 | std::string strRightOf ( std::string str, std::string sep ); 87 | 88 | // alphanumeric 89 | bool strIsNum ( std::string str, float& f ); 90 | float strToNum ( std::string str ); 91 | int strCount ( std::string& str, char ch ); 92 | 93 | bool readword ( char *line, char delim, char *word, int max_size ); 94 | std::string readword ( char *line, char delim ); 95 | 96 | // file helpers 97 | unsigned long getFileSize ( char* fname ); 98 | unsigned long getFilePos ( FILE* fp ); 99 | bool getFileLocation ( std::string filename, std::string& outpath ); 100 | bool getFileLocation ( char* filename, char* outpath ); 101 | bool getFileLocation ( char* filename, char* outpath, std::vector paths ); 102 | 103 | #define MAKECLR(r,g,b,a) ( (uint(a*255.0f)<<24) | (uint(b*255.0f)<<16) | (uint(g*255.0f)<<8) | uint(r*255.0f) ) 104 | #define ALPH(c) (float((c>>24) & 0xFF)/255.0) 105 | #define BLUE(c) (float((c>>16) & 0xFF)/255.0) 106 | #define GRN(c) (float((c>>8) & 0xFF)/255.0) 107 | #define RED(c) (float( c & 0xFF)/255.0) 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /joints.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "joints.h" 4 | #include "camera.h" 5 | #include "main.h" 6 | #include "nv_gui.h" 7 | 8 | #include 9 | #include 10 | 11 | #define JUNDEF -1 12 | 13 | Joints::Joints () 14 | { 15 | } 16 | 17 | // To Do: 18 | // - angle constraints 19 | // - ball joints 20 | // - 6-DOF end effector 21 | 22 | float circleDelta ( float a, float b ) 23 | { 24 | a = (float) fmod (a, 360 ); if ( a < 0 ) a += 360; 25 | 26 | float d = fabs( b - a); 27 | float r = d > 180 ? 360 - d : d; 28 | int sign = (b - a >= 0 && b - a <= 180) || (b - a <=-180 && b - a>= -360) ? 1 : -1; 29 | return r * sign; 30 | } 31 | 32 | void Joints::Sketch ( Camera3D* cam ) 33 | { 34 | Vector3DF dir, ang; 35 | Vector3DF a,b,c, p; 36 | Matrix4F world, local; 37 | 38 | if ( m_Joints.size()==0 ) { 39 | errorf ( "ERROR: No joints defined.\n" ); 40 | } 41 | 42 | Vector3DF angs; 43 | 44 | 45 | /* start2D(); 46 | char msg[512]; 47 | sprintf ( msg, "action: %s -> %s (%d of %d) %4.1f\n", m_CycleSet->getName(m_CCurr.cycle), m_CycleSet->getName(m_CNext.cycle), m_CFrame, m_CEnd, mTargetDist ); 48 | drawText( 10, 10, msg, 1,1,1,1); 49 | end2D();*/ 50 | 51 | start3D( cam ); 52 | 53 | //-------- draw end effector 54 | p = m_Effector; 55 | drawBox3D ( p.x-.05, p.y-.05, p.z-.05, p.x+.05, p.y+.05, p.z+.05, 1,1,1, 1 ); 56 | c = m_Goal - m_Effector; 57 | c.Normalize(); 58 | drawLine3D ( p.x, p.y, p.z, p.x+c.x, p.y+c.y, p.z+c.z, 1,0.5,1, 1 ); 59 | 60 | 61 | //-------- draw joints 62 | // 63 | m_Joints[0].orient.getMatrix ( world ); 64 | world.PostTranslate ( m_Joints[0].pos ); 65 | EvaluateJoints( m_Joints, world ); 66 | 67 | Matrix4F mtx; // N(n) = Oi^-1(n-1) Rn(n) Oi(n) (eqn 4.9) 68 | Matrix4F OiInv; 69 | Matrix4F Ri, Rj0, Ri2, pmtx, Mv; 70 | 71 | Vector3DF u, q, xangs; 72 | 73 | for (int n=0; n < m_Joints.size(); n++ ) { 74 | 75 | p = m_Joints[n].pos; 76 | 77 | // draw joint center 78 | drawBox3D ( p.x-.1, p.y-.1, p.z-.1, p.x+.1, p.y+.1, p.z+.1, 0.6,0.6,0.6, 1 ); 79 | 80 | // draw joint orientation 81 | local = m_Joints[n].Mworld; 82 | local.PostTranslate ( m_Joints[n].pos * -1.0f); 83 | a = Vector3DF(1,0,0); a *= local; 84 | b = Vector3DF(0,1,0); b *= local; 85 | c = Vector3DF(0,0,1); c *= local; 86 | drawLine3D ( p.x, p.y, p.z, p.x+a.x, p.y+a.y, p.z+a.z, 1, 0, 0, 1 ); // red, x-axis 87 | drawLine3D ( p.x, p.y, p.z, p.x+b.x, p.y+b.y, p.z+b.z, 0, 1, 0, 1 ); // green, y-axis 88 | drawLine3D ( p.x, p.y, p.z, p.x+c.x, p.y+c.y, p.z+c.z, 0, 0, 1, 1 ); // blue, z-axis 89 | 90 | c = b * (m_Joints[n].length - 0.1f); 91 | b *= 1.1f; 92 | drawLine3D ( p.x+b.x, p.y+b.y, p.z+b.z, p.x+c.x, p.y+c.y, p.z+c.z, 1, 1, 0, 1 ); // bone 93 | 94 | drawLine3D ( p.x+b.x, 0, p.z+b.z, p.x+c.x, 0, p.z+c.z, 0.5, 0.5, 0.3, 1 ); // bone shadow 95 | } 96 | 97 | end3D(); 98 | } 99 | 100 | int Joints::getLastChild ( int p ) 101 | { 102 | int child = m_Joints[p].child; 103 | if ( child == JUNDEF ) return JUNDEF; 104 | while ( m_Joints[child].next != JUNDEF ) { 105 | child = m_Joints[child].next; 106 | } 107 | return child; 108 | } 109 | void Joints::Clear () 110 | { 111 | m_Joints.clear (); 112 | } 113 | 114 | void Joints::SetLimits ( int j, Vector3DF lmin, Vector3DF lmax ) 115 | { 116 | m_Joints[j].min_limit = lmin; 117 | m_Joints[j].max_limit = lmax; 118 | } 119 | 120 | int Joints::AddJoint ( char* name, float length, Vector3DF angs, int cx, int cy, int cz ) 121 | { 122 | Joint jnt; 123 | int parent = m_Joints.size()-1; // use last joint added as parent 124 | 125 | strncpy ( jnt.name, name, 64 ); 126 | jnt.name[63] = '\0'; 127 | 128 | jnt.parent = parent; 129 | jnt.pos = Vector3DF(0,0,0); 130 | jnt.length = length; 131 | jnt.angs = angs; 132 | jnt.dof = Vector3DI(cx,cy,cz); 133 | jnt.orient.set ( angs ); // quaternion from euler angles 134 | jnt.min_limit.Set(-180.f,-180.f,-180.f); 135 | jnt.max_limit.Set( 180.f, 180.f, 180.f); 136 | jnt.clr = 0; 137 | jnt.child = JUNDEF; 138 | jnt.next = JUNDEF; 139 | 140 | int curr = (int) m_Joints.size(); 141 | 142 | if ( parent == JUNDEF ) { 143 | // this is root 144 | jnt.lev = 0; 145 | } else { 146 | Joint* pjnt = getJoint ( parent ); 147 | jnt.lev = pjnt->lev + 1; 148 | int plastchild = getLastChild ( parent ); // last child of parent 149 | if ( plastchild != JUNDEF ) { 150 | getJoint(plastchild)->next = curr; 151 | } else { 152 | pjnt->child = curr; 153 | } 154 | } 155 | 156 | m_Joints.push_back ( jnt ); // add joint 157 | 158 | return (int) m_Joints.size()-1; 159 | } 160 | 161 | void Joints::MoveJoint ( int j, int axis_id, float da ) 162 | { 163 | if ( j >= m_Joints.size() ) return; 164 | 165 | Vector3DF axis; 166 | switch ( axis_id ) { 167 | case 0: axis.Set(1,0,0); break; 168 | case 1: axis.Set(0,1,0); break; 169 | case 2: axis.Set(0,0,1); break; 170 | }; 171 | bool allow = (axis.Dot ( m_Joints[j].dof ) > 0 ); 172 | if ( !allow ) return; 173 | 174 | Quaternion delta; 175 | delta.fromAngleAxis ( da, axis ); 176 | delta.normalize(); 177 | 178 | m_Joints[j].orient = m_Joints[j].orient * delta; // local rotation 179 | m_Joints[j].orient.normalize(); 180 | 181 | m_Joints[j].orient.toEuler ( m_Joints[j].angs ); 182 | 183 | 184 | } 185 | 186 | Joint* Joints::FindJoint ( std::string name ) 187 | { 188 | for (int n=0; n < getNumJoints(); n++ ) { 189 | if ( name.compare(m_Joints[n].name) == 0 ) 190 | return &m_Joints[n]; 191 | } 192 | return 0x0; 193 | } 194 | 195 | void Joints::EvaluateJoints ( std::vector& joints, Matrix4F& world ) 196 | { 197 | EvaluateJointsRecurse ( joints, 0, world ); 198 | 199 | // Compute end effector 200 | int n = joints.size()-1; 201 | Matrix4F local = m_Joints[n].Mworld; 202 | Vector3DF b (0.f, m_Joints[n].length, 0.f); 203 | local.PostTranslate ( m_Joints[n].pos * -1.0f); 204 | b *= local; 205 | m_Effector = m_Joints[n].pos + b; 206 | } 207 | 208 | // recursive funcs 209 | void Joints::EvaluateJointsRecurse ( std::vector& joints, int curr_jnt, Matrix4F world ) 210 | { 211 | // Evaluation of joint chain 212 | // 213 | // local orientation 214 | Matrix4F orient; 215 | Vector3DF a; 216 | // joints[curr_jnt].orient.toEuler ( a ); // cast to Euler ZYX angles first 217 | // orient.RotateZYX ( a ); 218 | joints[curr_jnt].orient.getMatrix ( orient ); // Ri' = orientation angles (animated) 219 | 220 | // set world transform 221 | if ( curr_jnt > 0 ) 222 | world *= orient; // Mw = M(w-1) Ri' v'' 223 | 224 | joints[curr_jnt].Mworld = world; 225 | joints[curr_jnt].pos = world.getTrans(); // Tworld 226 | 227 | // translate children to end of bone 228 | world.PreTranslate ( Vector3DF(0.f, joints[curr_jnt].length, 0.f) ); // v'' = bone length 229 | 230 | // recurse 231 | int child_jnt = joints[curr_jnt].child; 232 | while ( child_jnt != JUNDEF ) { 233 | EvaluateJointsRecurse ( joints, child_jnt, world ); 234 | child_jnt = joints[child_jnt].next; 235 | } 236 | } 237 | 238 | void Joints::StartIK () 239 | { 240 | // Count degrees-of-freedom (DOFs) 241 | int M = 0; 242 | for (int n=0; n < m_Joints.size(); n++ ) { 243 | M += m_Joints[n].dof.x; 244 | M += m_Joints[n].dof.y; 245 | M += m_Joints[n].dof.z; 246 | } 247 | 248 | // Construct Jacobian 249 | m_Jacobian.Resize ( M, 3 ); 250 | } 251 | 252 | void Joints::InverseKinematics ( Vector3DF goal, int maxiter ) 253 | { 254 | Matrix4F world; 255 | float dE; 256 | float amt; 257 | int iter = 0; 258 | 259 | m_Goal = goal; 260 | dE = (m_Goal - m_Effector).Length(); 261 | 262 | while ( dE > 0.1f && iter++ < maxiter ) { 263 | 264 | // check convergence 265 | dE = (m_Goal - m_Effector).Length(); 266 | 267 | amt = pow(dE * 0.2f, 1.5); 268 | if ( amt > 0.5f ) amt = 0.5f; 269 | 270 | // compute jacobian 271 | ComputeJacobian (); 272 | 273 | // apply jacobian transpose 274 | ApplyJacobianTranspose ( amt ); 275 | 276 | // re-evaluate joints 277 | m_Joints[0].orient.getMatrix ( world ); 278 | world.PostTranslate ( m_Joints[0].pos ); 279 | EvaluateJoints( m_Joints, world ); 280 | } 281 | } 282 | 283 | void Joints::LimitQuaternion ( Quaternion& o, int limitaxis_id, float limitang ) 284 | { 285 | Vector3DF angs; 286 | Vector3DF a1, a2; 287 | 288 | o.toEuler ( angs ); 289 | a1 = angs; 290 | 291 | char c=' '; 292 | switch ( abs(limitaxis_id) ) { 293 | case 1: angs.x = limitang; c='X'; break; 294 | case 2: angs.y = limitang; c='Y'; break; 295 | case 3: angs.z = limitang; c='Z'; break; 296 | } 297 | o.set ( angs ); 298 | o.normalize(); 299 | o.toEuler( a2 ); 300 | 301 | /*printf ( "Limit %c%c\n", (limitaxis_id<0) ? '-' : '+', c ); 302 | printf ( " before: <%3.2f, %3.2f, %3.2f>\n", a1.x,a1.y,a1.z); 303 | printf ( " after: <%3.2f, %3.2f, %3.2f>\n", a2.x,a2.y,a2.z);*/ 304 | 305 | } 306 | 307 | void Joints::ApplyJacobianTranspose ( float amt ) 308 | { 309 | Vector3DF jT; 310 | Vector3DF dE; 311 | Quaternion dq, o1; 312 | Vector3DF angs, a1, a2, a3, a4, a5, a6; 313 | float dang; 314 | float lz; 315 | std::string msg; 316 | bool limit=false; 317 | 318 | // effector delta (dE) 319 | dE = m_Goal - m_Effector; 320 | dE.Normalize(); 321 | 322 | int M=0; 323 | for (int n=0; n < m_Joints.size(); n++ ) { 324 | 325 | m_Joints[n].orient.toEuler ( angs ); 326 | if (angs.z < -90) angs.z = 360 + angs.z; 327 | 328 | if ( m_Joints[n].dof.x==1 ) { 329 | jT.x = m_Jacobian(M, 0); 330 | jT.y = m_Jacobian(M, 1); 331 | jT.z = m_Jacobian(M, 2); 332 | dang = jT.x * dE.x + jT.y * dE.y + jT.z * dE.z; // multiply one row of J^T by dE vector 333 | dang *= amt; 334 | if ( angs.x + dang < m_Joints[n].min_limit.x ) { 335 | LimitQuaternion ( m_Joints[n].orient, -1, m_Joints[n].min_limit.x ); 336 | } else if ( angs.x + dang > m_Joints[n].max_limit.x ) { 337 | LimitQuaternion ( m_Joints[n].orient, 1, m_Joints[n].max_limit.x ); 338 | } else { 339 | dq.fromAngleAxis ( dang, Vector3DF(1,0,0) ); // rotate around local X-axis 340 | m_Joints[n].orient = m_Joints[n].orient * dq; 341 | m_Joints[n].orient.normalize(); 342 | } 343 | M++; 344 | } 345 | 346 | if ( m_Joints[n].dof.y==1 ) { 347 | jT.x = m_Jacobian(M, 0); 348 | jT.y = m_Jacobian(M, 1); 349 | jT.z = m_Jacobian(M, 2); 350 | dang = jT.x * dE.x + jT.y * dE.y + jT.z * dE.z; // multiply one row of J^T by dE vector 351 | dang *= amt; 352 | if ( angs.y + dang < m_Joints[n].min_limit.y ) { 353 | LimitQuaternion ( m_Joints[n].orient, -2, m_Joints[n].min_limit.y ); 354 | } else if ( angs.y + dang > m_Joints[n].max_limit.y ) { 355 | LimitQuaternion ( m_Joints[n].orient, 2, m_Joints[n].max_limit.y ); 356 | } else { 357 | dq.fromAngleAxis ( dang, Vector3DF(0,1,0) ); // rotate around local Y-axis 358 | m_Joints[n].orient = m_Joints[n].orient * dq; 359 | m_Joints[n].orient.normalize(); 360 | } 361 | M++; 362 | } 363 | 364 | if ( m_Joints[n].dof.z==1 ) { 365 | jT.x = m_Jacobian(M, 0); 366 | jT.y = m_Jacobian(M, 1); 367 | jT.z = m_Jacobian(M, 2); 368 | msg=""; 369 | dang = jT.x * dE.x + jT.y * dE.y + jT.z * dE.z; // multiply one row of J^T by dE vector 370 | dang *= amt; 371 | if ( angs.z + dang < m_Joints[n].min_limit.z ) { 372 | LimitQuaternion ( m_Joints[n].orient, -3, m_Joints[n].min_limit.z ); 373 | } else if ( angs.z + dang > m_Joints[n].max_limit.z ) { 374 | LimitQuaternion ( m_Joints[n].orient, 3, m_Joints[n].max_limit.z ); 375 | } else { 376 | dq.fromAngleAxis ( dang, Vector3DF(0,0,1) ); // rotate around local Z-axis 377 | m_Joints[n].orient = m_Joints[n].orient * dq; 378 | m_Joints[n].orient.toEuler(a4); 379 | m_Joints[n].orient.normalize(); 380 | m_Joints[n].orient.toEuler(a5); 381 | } 382 | M++; 383 | } 384 | 385 | m_Joints[n].orient.toEuler ( angs ); 386 | //printf ("J%d <%3.2f, %3.2f, %3.2f> %3.2f\n", n, angs.x, angs.y, angs.z, a1.z ); 387 | } 388 | 389 | } 390 | 391 | void Joints::ComputeJacobian () 392 | { 393 | Vector3DF r, c, axis, delta; 394 | Vector3DF dE; 395 | Matrix4F mtx; 396 | 397 | // effector delta (dE) 398 | dE = m_Goal - m_Effector; 399 | dE.Normalize(); 400 | 401 | // process each joint to find DOFs 402 | int M =0; 403 | for (int n=0; n < m_Joints.size(); n++ ) { 404 | 405 | r = m_Effector - m_Joints[n].pos; // r = e - joint_pos 406 | r.Normalize(); 407 | mtx = m_Joints[n].Mworld; // local orientation of joint 408 | mtx.PostTranslate ( m_Joints[n].pos * -1.0f ); 409 | 410 | if ( m_Joints[n].dof.x == 1) { 411 | // Use x-axis rotation on this joint 412 | axis.Set( 1, 0, 0 ); // get the joints x-axis 413 | axis *= mtx; // axis in world space 414 | axis.Normalize(); 415 | delta = axis.Cross ( r ); // J(phi) = axis X (E - p) // X=cross product, E=end effector, p=joint position, axis=joint axis (in world space) 416 | delta.Normalize(); 417 | m_Jacobian(M,0) = delta.x; // write to jacobian 418 | m_Jacobian(M,1) = delta.y; 419 | m_Jacobian(M,2) = delta.z; 420 | M++; 421 | } 422 | if ( m_Joints[n].dof.y == 1) { 423 | // Use y-axis rotation on this joint 424 | axis.Set( 0, 1, 0 ); // get the joints y-axis 425 | axis *= mtx; // rotation axis in world space 426 | axis.Normalize(); 427 | delta = axis.Cross ( r ); 428 | delta.Normalize(); 429 | m_Jacobian(M,0) = delta.x; // write to jacobian 430 | m_Jacobian(M,1) = delta.y; 431 | m_Jacobian(M,2) = delta.z; 432 | M++; 433 | } 434 | if ( m_Joints[n].dof.z == 1) { 435 | // Use z-axis rotation on this joint 436 | axis.Set( 0, 0, 1 ); // get the joints z-axis 437 | axis *= mtx; // rotation axis in world space 438 | axis.Normalize(); 439 | delta = axis.Cross ( r ); 440 | delta.Normalize(); 441 | m_Jacobian(M,0) = delta.x; // write to jacobian 442 | m_Jacobian(M,1) = delta.y; 443 | m_Jacobian(M,2) = delta.z; 444 | M++; 445 | } 446 | } 447 | 448 | } 449 | -------------------------------------------------------------------------------- /joints.h: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ifndef DEF_CHARACTER 4 | #define DEF_CHARACTER 5 | 6 | #include 7 | #include 8 | #include "quaternion.h" 9 | #include "vec.h" 10 | 11 | struct Joint { 12 | char name[64]; 13 | char lev; 14 | uint clr; 15 | float length; // bone length, v'' 16 | Vector3DF pos; // bone position, T 17 | Vector3DI dof; 18 | Quaternion orient; // orientation angles, Ri (converted to ideal format, Mv = T Ri' v'', see Meredith & Maddock paper) 19 | Matrix4F Mworld; // world transform (computed on-the-fly) 20 | int parent; 21 | int child; 22 | int next; 23 | 24 | Vector3DF min_limit; 25 | Vector3DF max_limit; 26 | 27 | //-- debugging 28 | Vector3DF angs; // BVH angles (debug purposes, will be removed in future) 29 | Vector3DF bonevec; // BVH bone vec 30 | }; 31 | 32 | class Camera3D; 33 | 34 | class Joints { 35 | public: 36 | Joints () ; 37 | 38 | virtual void Sketch ( Camera3D* cam ); 39 | 40 | void Clear (); 41 | 42 | int AddJoint ( char* name, float length, Vector3DF angs, int cx, int cy, int cz ); 43 | void SetLimits ( int j, Vector3DF lmin, Vector3DF lmax ); 44 | void MoveJoint ( int j, int axis_id, float da); 45 | void EvaluateJoints ( std::vector& joints, Matrix4F& world ); // Evaluate cycle to joints 46 | void EvaluateJointsRecurse ( std::vector& joints, int curr_jnt, Matrix4F tform ); // Update joint transforms 47 | void StartIK (); 48 | void InverseKinematics ( Vector3DF goal, int maxiter=100 ); 49 | void ComputeJacobian (); 50 | void ApplyJacobianTranspose ( float amt ); 51 | void LimitQuaternion ( Quaternion& o, int axis, float limitang ); 52 | 53 | Joint* FindJoint ( std::string name ); 54 | Joint* getJoint ( int i ) { return &m_Joints[i]; } 55 | std::vector& getJoints() { return m_Joints; } 56 | int getNumJoints () { return (int) m_Joints.size(); } 57 | int getLastChild ( int p ); 58 | 59 | private: 60 | 61 | std::vector m_Joints; // joint state 62 | 63 | Vector3DF m_Goal; 64 | Vector3DF m_Effector; 65 | MatrixF m_Jacobian; 66 | }; 67 | 68 | #endif -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | // Sample utils 4 | #include "main.h" // window system 5 | #include "nv_gui.h" // gui system 6 | #include 7 | #include 8 | 9 | #include "joints.h" 10 | 11 | #define MOVE_GOAL_XY 1 12 | #define MOVE_GOAL_XZ 2 13 | 14 | class Sample : public NVPWindow { 15 | public: 16 | virtual bool init(); 17 | virtual void display(); 18 | virtual void reshape(int w, int h); 19 | virtual void motion(int x, int y, int dx, int dy); 20 | virtual void keyboardchar(unsigned char key, int mods, int x, int y); 21 | virtual void mouse (NVPWindow::MouseButton button, NVPWindow::ButtonAction state, int mods, int x, int y); 22 | 23 | void Reset (); 24 | 25 | int mouse_down; 26 | int mouse_action; 27 | int m_adjust; 28 | bool m_bRunInv; 29 | 30 | Camera3D* cam; 31 | Vector3DF m_goal; 32 | 33 | Joints m_joints; 34 | }; 35 | 36 | 37 | /*void Sample::start_guis (int w, int h) 38 | { 39 | setview2D ( w, h ); 40 | guiSetCallback ( 0x0 ); 41 | addGui ( 10, h-30, 130, 20, "Overlay", GUI_CHECK, GUI_BOOL, &m_overlay, 0.f, 1.f ); 42 | }*/ 43 | 44 | void Sample::Reset () 45 | { 46 | // J1) Shoulder 47 | // Ball pivot: X-axis up, Y-axis outward, Z-axis forward 48 | // T-pose Minimum Maximum 49 | // X-axis, swing fwd/back: 0=out right, -60=back, +130=forward 50 | // Y-axis, shoulder twist: 0=out front, -80=down, +90=up ** +45 typical ** 51 | // Z-axis, swing up/down: 90=out right, -5=over, 200=down, 0=straight up, 90=out, 52 | // J2) Elbow: 53 | // Hinge jnt: X-axis up, Y-axis outward, Z-axis forward 54 | // X-axis, rotate: 0=out, +3=limit, +150=closed 55 | // J3) Wrist: 56 | // Ball pivot: X-axis up, Y-axis outward, Z-axis forward (X = thumb up) 57 | // X-axis, palm up/down: 0=out flat, -80=palm extend, +80=palm down 58 | // Y-axis, wrist twist: 0=thumb up, -90=thumb fwd, +60=thumb back 59 | // Z-axis, wrist yaw: 0=out strt, -25=cantor inwd, +25=cantor outwd 60 | 61 | // create joints 62 | m_joints.Clear (); 63 | 64 | //---- Human Arm 65 | m_joints.AddJoint ( "back", 5, Vector3DF(0,0,0), 0, 0, 0 ); 66 | m_joints.AddJoint ( "shoulder", 3, Vector3DF(0,0,90), 1, 1, 1 ); 67 | m_joints.AddJoint ( "elbow", 3, Vector3DF(0,0,0), 1, 0, 0 ); 68 | //m_joints.AddJoint ( "J3", 2, Vector3DF(0,0,0), 1, 1, 1 ); 69 | m_joints.SetLimits ( 1, Vector3DF(-60, -80, 5), Vector3DF(130, 45, 200) ); // shoulder 70 | m_joints.SetLimits ( 2, Vector3DF( +5, 0, 0), Vector3DF(150, 0, 0) ); // elbow 71 | //m_joints.SetLimits ( 3, Vector3DF(-80, -90, -25),Vector3DF(+80,+60,+25) ); // wrist 72 | 73 | 74 | //---- Robot Arm 75 | /*m_joints.AddJoint ( "J0", 1, Vector3DF(0,0,0), 0, 1, 0 ); 76 | m_joints.AddJoint ( "J1", 4, Vector3DF(0,0,0), 1, 0, 0 ); 77 | m_joints.AddJoint ( "J2", 4, Vector3DF(0,0,0), 1, 0, 0 ); 78 | m_joints.AddJoint ( "J3", 2, Vector3DF(0,0,0), 1, 0, 0 ); 79 | m_joints.SetLimits ( 1, Vector3DF(-45, 0, 0), Vector3DF(90, 0, 0) ); 80 | m_joints.SetLimits ( 2, Vector3DF(5, 0, 0), Vector3DF(170, 0, 0) ); 81 | m_joints.SetLimits ( 3, Vector3DF(5, 0, 0), Vector3DF(90, 0, 0) ); 82 | */ 83 | 84 | m_joints.StartIK(); 85 | 86 | m_goal.Set ( 2, 1, 4 ); 87 | } 88 | 89 | bool Sample::init () 90 | { 91 | int w = getWidth(), h = getHeight(); // window width & height 92 | 93 | start_log ( "log.txt" ); 94 | 95 | init2D ( "arial" ); 96 | setview2D ( w, h ); 97 | glViewport ( 0, 0, w, h ); 98 | 99 | cam = new Camera3D; 100 | cam->setNearFar ( 1, 2000 ); 101 | cam->setOrbit ( Vector3DF(40,30,0), Vector3DF(0,0,0), 70, 70 ); 102 | m_adjust = -1; 103 | 104 | // create joints 105 | Reset (); 106 | 107 | m_bRunInv = true; 108 | 109 | return true; 110 | } 111 | 112 | 113 | void Sample::display() 114 | { 115 | Vector3DF a,b,c,p; 116 | 117 | clearScreenGL(); 118 | 119 | if (m_bRunInv) 120 | m_joints.InverseKinematics ( m_goal ); 121 | 122 | // draw joints 123 | m_joints.Sketch( cam ); 124 | 125 | 126 | 127 | start3D(cam); 128 | 129 | // sketch a grid 130 | for (int i = -10; i <= 10; i++) { 131 | drawLine3D(float(i),-0.01f, -10.f, float(i), -0.01f, 10.f, .2f, .2f, .2f, 1.f); 132 | drawLine3D(-10.f, -0.01f, float(i), 10.f, -0.01f, float(i), .2f, .2f, .2f, 1.f); 133 | } 134 | // world axes 135 | a = Vector3DF(1,0,0); b = Vector3DF(0,1,0); c = Vector3DF(0,0,1); 136 | p = Vector3DF(-10.f,-0.01f,-10.f); 137 | drawLine3D ( p.x, p.y, p.z, p.x+a.x, p.y+a.y, p.z+a.z, 1, 0, 0, 1 ); 138 | drawLine3D ( p.x, p.y, p.z, p.x+b.x, p.y+b.y, p.z+b.z, 0, 1, 0, 1 ); 139 | drawLine3D ( p.x, p.y, p.z, p.x+c.x, p.y+c.y, p.z+c.z, 0, 0, 1, 1 ); 140 | 141 | // goal 142 | p = m_goal; 143 | drawBox3D ( p.x-0.1f, p.y-0.1f, p.z-0.1f, p.x+0.1f, p.y+0.1f, p.z+0.1f, 1, 0, 1, 1 ); 144 | drawBox3D ( p.x-0.1f, 0.f, p.z-0.1f, p.x+0.1f, 0.f, p.z+0.1f, .75f, 0, .75f, 1 ); 145 | if ( mouse_action == MOVE_GOAL_XY ) 146 | drawBox3D ( -10.f, 0.f, p.z-0.1f, 10.f, 8.f, p.z+0.1f, 1, 0, 1, 1 ); 147 | if ( mouse_action == MOVE_GOAL_XZ ) 148 | drawBox3D ( -10.f, p.y-0.1f, -10.f, 10.f, p.y+0.1f, 10.f, 1, 0, 1, 1 ); 149 | 150 | end3D(); 151 | 152 | 153 | 154 | // Use NvGui to draw in 2D/3D 155 | draw3D (); // Render the 3D drawing groups 156 | drawGui (0); // Render the GUI 157 | draw2D (); 158 | 159 | postRedisplay(); // Post redisplay since simulation is continuous 160 | } 161 | 162 | 163 | 164 | // Geometry utilities -------------------------------------------------------------------------------------- 165 | 166 | #define EPS 0.00001 167 | 168 | // Line A: p1 to p2 169 | // Line B: p3 to p4 170 | bool intersectLineLine ( Vector3DF p1, Vector3DF p2, Vector3DF p3, Vector3DF p4, Vector3DF& pa, Vector3DF& pb, double& mua, double& mub) 171 | { 172 | Vector3DF p13,p43,p21; 173 | double d1343,d4321,d1321,d4343,d2121; 174 | double numer,denom; 175 | 176 | p13 = p1; p13 -= p3; 177 | p43 = p4; p43 -= p3; 178 | if (fabs(p43.x) < EPS && fabs(p43.y) < EPS && fabs(p43.z) < EPS) return false; 179 | p21 = p2; p21 -= p1; 180 | if (fabs(p21.x) < EPS && fabs(p21.y) < EPS && fabs(p21.z) < EPS) return false; 181 | 182 | d1343 = p13.Dot ( p43 ); 183 | d4321 = p43.Dot ( p21 ); 184 | d1321 = p13.Dot ( p21 ); 185 | d4343 = p43.Dot ( p43 ); 186 | d2121 = p21.Dot ( p21 ); 187 | 188 | denom = d2121 * d4343 - d4321 * d4321; 189 | if (fabs(denom) < EPS) return false; 190 | numer = d1343 * d4321 - d1321 * d4343; 191 | 192 | mua = numer / denom; 193 | mub = (d1343 + d4321 * (mua)) / d4343; 194 | 195 | pa = p21; pa *= (float) mua; pa += p1; 196 | pb = p43; pb *= (float) mub; pb += p3; 197 | 198 | return true; 199 | } 200 | 201 | Vector3DF intersectLineLine ( Vector3DF p1, Vector3DF p2, Vector3DF p3, Vector3DF p4 ) 202 | { 203 | Vector3DF pa, pb; 204 | double ma, mb; 205 | if ( intersectLineLine ( p1, p2, p3, p4, pa, pb, ma, mb ) ) { 206 | return pa; 207 | } 208 | return p2; 209 | } 210 | 211 | float intersectLineBox ( Vector3DF p1, Vector3DF p2, Vector3DF bmin, Vector3DF bmax ) 212 | { 213 | Vector3DF p; 214 | Vector3DF nearp, farp; 215 | float t[6]; 216 | Vector3DF dir; 217 | dir = p2; dir -= p1; 218 | 219 | int bst1 = -1, bst2 = -1; // bst1 = front face hit, bst2 = back face hit 220 | 221 | t[0] = ( bmax.y - p1.y ) / dir.y; // 0 = max y 222 | t[1] = ( bmin.x - p1.x ) / dir.x; // 1 = min x 223 | t[2] = ( bmin.z - p1.z ) / dir.z; // 2 = min z 224 | t[3] = ( bmax.x - p1.x ) / dir.x; // 3 = max x 225 | t[4] = ( bmax.z - p1.z ) / dir.z; // 4 = max z 226 | t[5] = ( bmin.y - p1.y ) / dir.y; // 5 = min y 227 | 228 | p = dir * t[0]; p += p1; if ( p.x < bmin.x || p.x > bmax.x || p.z < bmin.z || p.z > bmax.z ) t[0] = -1; 229 | p = dir * t[1]; p += p1; if ( p.y < bmin.y || p.y > bmax.y || p.z < bmin.z || p.z > bmax.z ) t[1] = -1; 230 | p = dir * t[2]; p += p1; if ( p.x < bmin.x || p.x > bmax.x || p.y < bmin.y || p.y > bmax.y ) t[2] = -1; 231 | p = dir * t[3]; p += p1; if ( p.y < bmin.y || p.y > bmax.y || p.z < bmin.z || p.z > bmax.z ) t[3] = -1; 232 | p = dir * t[4]; p += p1; if ( p.x < bmin.x || p.x > bmax.x || p.y < bmin.y || p.y > bmax.y ) t[4] = -1; 233 | p = dir * t[5]; p += p1; if ( p.x < bmin.x || p.x > bmax.x || p.z < bmin.z || p.z > bmax.z ) t[5] = -1; 234 | 235 | for (int j=0; j < 6; j++) 236 | if ( t[j] > 0.0 && ( t[j] < t[bst1] || bst1==-1 ) ) bst1=j; 237 | for (int j=0; j < 6; j++) 238 | if ( t[j] > 0.0 && ( t[j] < t[bst2] || bst2==-1 ) && j!=bst1 ) bst2=j; 239 | 240 | if ( bst1 == -1 ) 241 | return 0.0f; 242 | 243 | if ( p1.x >= bmin.x && p1.y >= bmin.y && p1.z >= bmin.z && p1.x <= bmax.x && p1.y <= bmax.y && p1.z <= bmax.z ) { 244 | return t[bst2]; 245 | } else { 246 | return t[bst1]; 247 | } 248 | } 249 | 250 | Vector3DF intersectLinePlane ( Vector3DF p1, Vector3DF p2, Vector3DF p0, Vector3DF pnorm ) 251 | { 252 | Vector3DF u, w; 253 | u = p2; u -= p1; // ray direction 254 | w = p1; w -= p0; 255 | 256 | float dval = pnorm.Dot( u ); 257 | float nval = -pnorm.Dot( w ); 258 | 259 | if (fabs(dval) < EPS ) { // segment is parallel to plane 260 | if (nval == 0) return p1; // segment lies in plane 261 | else return p1; // no intersection 262 | } 263 | // they are not parallel, compute intersection 264 | float t = nval / dval; 265 | u *= t; 266 | u += p1; 267 | return u; 268 | } 269 | 270 | void Sample::motion(int x, int y, int dx, int dy) 271 | { 272 | // Get camera for GVDB Scene 273 | bool shift = (getMods() & NVPWindow::KMOD_SHIFT); // Shift-key to modify light 274 | 275 | float fine = 0.5; 276 | 277 | switch ( mouse_down ) { 278 | case NVPWindow::MOUSE_BUTTON_LEFT: { 279 | 280 | if ( mouse_action == MOVE_GOAL_XY ) { 281 | cam->setSize( getWidth(), getHeight() ); 282 | Vector3DF dir = cam->inverseRay( x, y ); 283 | Vector3DF hit = intersectLinePlane ( cam->getPos(), cam->getPos()+dir, m_goal, Vector3DF(0,0,1) ); 284 | m_goal = hit; 285 | } else { 286 | if ( m_adjust == -1 ) { 287 | // Adjust camera orbit 288 | Vector3DF angs = cam->getAng(); 289 | angs.x += dx*0.2f*fine; 290 | angs.y -= dy*0.2f*fine; 291 | cam->setOrbit ( angs, cam->getToPos(), cam->getOrbitDist(), cam->getDolly() ); 292 | } else { 293 | bool bUseXAxis = (fabs(dy) > 2 ); 294 | m_joints.MoveJoint ( m_adjust, bUseXAxis ? 0 : 1, bUseXAxis ? -dy*0.01f : dx*0.01f); 295 | } 296 | } 297 | postRedisplay(); // Update display 298 | } break; 299 | 300 | case NVPWindow::MOUSE_BUTTON_MIDDLE: { 301 | // Adjust target pos 302 | cam->moveRelative ( float(dx) * fine*cam->getOrbitDist()/1000, float(-dy) * fine*cam->getOrbitDist()/1000, 0 ); 303 | postRedisplay(); // Update display 304 | } break; 305 | 306 | case NVPWindow::MOUSE_BUTTON_RIGHT: { 307 | 308 | if ( mouse_action == MOVE_GOAL_XZ ) { 309 | cam->setSize( getWidth(), getHeight() ); 310 | Vector3DF dir = cam->inverseRay( x, y ); 311 | Vector3DF hit = intersectLinePlane ( cam->getPos(), cam->getPos()+dir, m_goal, Vector3DF(0,1,0) ); 312 | m_goal = hit; 313 | } else { 314 | if ( m_adjust == -1 ) { 315 | // Adjust dist 316 | float dist = cam->getOrbitDist(); 317 | dist -= dy*fine; 318 | cam->setOrbit ( cam->getAng(), cam->getToPos(), dist, cam->getDolly() ); 319 | } else { 320 | m_joints.MoveJoint ( m_adjust, 2, dx*0.01f ); 321 | } 322 | } 323 | postRedisplay(); // Update display 324 | } break; 325 | } 326 | } 327 | 328 | void Sample::mouse ( NVPWindow::MouseButton button, NVPWindow::ButtonAction state, int mods, int x, int y) 329 | { 330 | if ( guiHandler ( button, state, x, y ) ) return; 331 | 332 | mouse_down = (state == NVPWindow::BUTTON_PRESS) ? button : -1; // Track when we are in a mouse drag 333 | 334 | mouse_action = 0; 335 | 336 | cam->setSize( getWidth(), getHeight() ); 337 | Vector3DF dir = cam->inverseRay( x, y ); 338 | float t = intersectLineBox ( cam->getPos(), cam->getPos()+dir, m_goal-Vector3DF(0.1f,0.1f,0.1f), m_goal+Vector3DF(0.1f,0.1f,0.1f) ); 339 | if ( t != 0 && state==NVPWindow::BUTTON_PRESS) 340 | if (button==NVPWindow::MOUSE_BUTTON_LEFT) 341 | mouse_action = MOVE_GOAL_XY; 342 | else 343 | mouse_action = MOVE_GOAL_XZ; 344 | } 345 | 346 | void Sample::keyboardchar(unsigned char key, int mods, int x, int y) 347 | { 348 | switch ( key ) { 349 | case 's': 350 | m_joints.InverseKinematics ( m_goal, 1 ); 351 | break; 352 | case ' ': 353 | m_bRunInv = !m_bRunInv; 354 | break; 355 | case 'c': case 'C': m_adjust = -1; break; 356 | case 'r': 357 | Reset (); 358 | break; 359 | case '0': m_adjust = 0; break; 360 | case '1': m_adjust = 1; break; 361 | case '2': m_adjust = 2; break; 362 | case '3': m_adjust = 3; break; 363 | }; 364 | } 365 | 366 | 367 | void Sample::reshape (int w, int h) 368 | { 369 | postRedisplay(); 370 | } 371 | 372 | int sample_main ( int argc, const char** argv ) 373 | { 374 | Sample sample_obj; 375 | //int w=1280, h=1024; 376 | //int w=1920, h=1080; 377 | int w=1200, h=900; 378 | 379 | return sample_obj.run ( "Shapes", "Shapes", argc, argv, w, h, 4, 0, 1 ); 380 | } 381 | 382 | void sample_print( int argc, char const *argv) 383 | { 384 | } 385 | 386 | -------------------------------------------------------------------------------- /quaternion.h: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2002-2012 Nikolaus Gebhardt 2 | // This file is part of the "Irrlicht Engine". 3 | // For conditions of distribution and use, see copyright notice in irrlicht.h 4 | 5 | /* 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | 22 | Please note that the Irrlicht Engine is based in part on the work of the 23 | Independent JPEG Group, the zlib and the libPng. This means that if you use 24 | the Irrlicht Engine in your product, you must acknowledge somewhere in your 25 | documentation that you've used the IJG code. It would also be nice to mention 26 | that you use the Irrlicht Engine, the zlib and libPng. See the README files 27 | in the jpeglib, the zlib and libPng for further informations. 28 | */ 29 | 30 | #ifndef QUATERNION_H 31 | #define QUATERNION_H_ 32 | 33 | #include 34 | 35 | #include "vec.h" 36 | //#include "matrix.h" 37 | 38 | typedef float f32; 39 | typedef double f64; 40 | 41 | #define EPS 0.00001 42 | 43 | const f32 ROUNDING_ERROR_f32 = 0.000001f; 44 | const f64 ROUNDING_ERROR_f64 = 0.00000001; 45 | const f64 PI64 = 3.1415926535897932384626433832795028841971693993751; 46 | //const f32 PI = 3.14159265359f; 47 | 48 | #define min(a,b) ( (a < b ) ? a : b ) 49 | #define max(a,b) ( (a > b ) ? a : b ) 50 | 51 | inline f64 clamp (f64 value, f64 low, f64 high) 52 | { 53 | return min (max (value,low), high); 54 | } 55 | inline bool fequal ( double a, double b, double eps ) 56 | { 57 | return ( fabs(a-b)1.0) ? 0 : 2.0f * acosf(W) * RADtoDEG; } 230 | 231 | //! Output this Quaternion to an euler angle (radians) 232 | void toEuler(Vector3DF& euler) const; 233 | 234 | //! Set Quaternion to identity 235 | Quaternion& makeIdentity(); 236 | 237 | //! Set Quaternion to represent a rotation from one vector to another. 238 | Quaternion& rotationFromTo(const Vector3DF& from, const Vector3DF& to); 239 | 240 | //! Quaternion elements. 241 | f64 X; // vectorial (imaginary) part 242 | f64 Y; 243 | f64 Z; 244 | f64 W; // real part 245 | }; 246 | 247 | 248 | // Constructor which converts euler angles to a Quaternion 249 | inline Quaternion::Quaternion(f32 x, f32 y, f32 z) 250 | { 251 | set(x,y,z); 252 | } 253 | 254 | 255 | // Constructor which converts euler angles to a Quaternion 256 | inline Quaternion::Quaternion(const Vector3DF& vec) 257 | { 258 | set(vec.x,vec.y,vec.z); 259 | } 260 | 261 | // Constructor which converts a matrix to a Quaternion 262 | inline Quaternion::Quaternion(const Matrix4F& mat) 263 | { 264 | (*this) = mat; 265 | } 266 | 267 | // equal operator 268 | inline bool Quaternion::operator==(const Quaternion& other) const 269 | { 270 | return ((X == other.X) && 271 | (Y == other.Y) && 272 | (Z == other.Z) && 273 | (W == other.W)); 274 | } 275 | 276 | // inequality operator 277 | inline bool Quaternion::operator!=(const Quaternion& other) const 278 | { 279 | return !(*this == other); 280 | } 281 | 282 | // assignment operator 283 | inline Quaternion& Quaternion::operator=(const Quaternion& other) 284 | { 285 | X = other.X; 286 | Y = other.Y; 287 | Z = other.Z; 288 | W = other.W; 289 | return *this; 290 | } 291 | 292 | // matrix assignment operator 293 | inline Quaternion& Quaternion::operator=(const Matrix4F& m) 294 | { 295 | const f32 diag = m.data[0] + m.data[5] + m.data[10] + 1; 296 | 297 | if( diag > 0.0f ) 298 | { 299 | const f32 scale = sqrtf(diag) * 2.0f; // get scale from diagonal 300 | 301 | // TODO: speed this up 302 | X = (m.data[6] - m.data[9]) / scale; 303 | Y = (m.data[8] - m.data[2]) / scale; 304 | Z = (m.data[1] - m.data[4]) / scale; 305 | W = 0.25f * scale; 306 | } 307 | else 308 | { 309 | if (m.data[0]>m.data[5] && m.data[0]>m.data[10]) 310 | { 311 | // 1st element of diag is greatest value 312 | // find scale according to 1st element, and double it 313 | const f32 scale = sqrtf(1.0f + m.data[0] - m.data[5] - m.data[10]) * 2.0f; 314 | 315 | // TODO: speed this up 316 | X = 0.25f * scale; 317 | Y = (m.data[4] + m.data[1]) / scale; 318 | Z = (m.data[2] + m.data[8]) / scale; 319 | W = (m.data[6] - m.data[9]) / scale; 320 | } 321 | else if (m.data[5]>m.data[10]) 322 | { 323 | // 2nd element of diag is greatest value 324 | // find scale according to 2nd element, and double it 325 | const f32 scale = sqrtf(1.0f + m.data[5] - m.data[0] - m.data[10]) * 2.0f; 326 | 327 | // TODO: speed this up 328 | X = (m.data[4] + m.data[1]) / scale; 329 | Y = 0.25f * scale; 330 | Z = (m.data[9] + m.data[6]) / scale; 331 | W = (m.data[8] - m.data[2]) / scale; 332 | } 333 | else 334 | { 335 | // 3rd element of diag is greatest value 336 | // find scale according to 3rd element, and double it 337 | const f32 scale = sqrtf(1.0f + m.data[10] - m.data[0] - m.data[5]) * 2.0f; 338 | 339 | // TODO: speed this up 340 | X = (m.data[8] + m.data[2]) / scale; 341 | Y = (m.data[9] + m.data[6]) / scale; 342 | Z = 0.25f * scale; 343 | W = (m.data[1] - m.data[4]) / scale; 344 | } 345 | } 346 | 347 | return normalize(); 348 | } 349 | 350 | 351 | // multiplication operator 352 | inline Quaternion Quaternion::operator*(const Quaternion& other) const 353 | { 354 | Quaternion tmp; 355 | 356 | /*tmp.W = (other.W * W) - (other.X * X) - (other.Y * Y) - (other.Z * Z); 357 | tmp.X = (other.W * X) + (other.X * W) + (other.Y * Z) - (other.Z * Y); 358 | tmp.Y = (other.W * Y) + (other.Y * W) + (other.Z * X) - (other.X * Z); 359 | tmp.Z = (other.W * Z) + (other.Z * W) + (other.X * Y) - (other.Y * X);*/ 360 | 361 | tmp.W = (W * other.W) - (X * other.X) - (Y * other.Y) - (Z * other.Z); 362 | tmp.X = (W * other.X) + (X * other.W) + (Y * other.Z) - (Z * other.Y); 363 | tmp.Y = (W * other.Y) + (Y * other.W) + (Z * other.X) - (X * other.Z); 364 | tmp.Z = (W * other.Z) + (Z * other.W) + (X * other.Y) - (Y * other.X); 365 | 366 | return tmp; 367 | } 368 | 369 | 370 | // multiplication operator 371 | inline Quaternion Quaternion::operator*(f32 s) const 372 | { 373 | return Quaternion(s*X, s*Y, s*Z, s*W); 374 | } 375 | 376 | 377 | // multiplication operator 378 | inline Quaternion& Quaternion::operator*=(f32 s) 379 | { 380 | X*=s; 381 | Y*=s; 382 | Z*=s; 383 | W*=s; 384 | return *this; 385 | } 386 | 387 | // multiplication operator 388 | inline Quaternion& Quaternion::operator*=(const Quaternion& other) 389 | { 390 | return (*this = other * (*this)); 391 | } 392 | 393 | // add operator 394 | inline Quaternion Quaternion::operator+(const Quaternion& b) const 395 | { 396 | return Quaternion(X+b.X, Y+b.Y, Z+b.Z, W+b.W); 397 | } 398 | 399 | // Creates a matrix from this Quaternion 400 | inline Matrix4F Quaternion::getMatrix() const 401 | { 402 | Matrix4F m; 403 | getMatrix(m); 404 | return m; 405 | } 406 | 407 | /*! 408 | Creates a matrix from this Quaternion 409 | */ 410 | inline void Quaternion::getMatrix(Matrix4F &dest, 411 | const Vector3DF ¢er) const 412 | { 413 | dest.data[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z; 414 | dest.data[1] = 2.0f*X*Y + 2.0f*Z*W; 415 | dest.data[2] = 2.0f*X*Z - 2.0f*Y*W; 416 | dest.data[3] = 0.0f; 417 | 418 | dest.data[4] = 2.0f*X*Y - 2.0f*Z*W; 419 | dest.data[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z; 420 | dest.data[6] = 2.0f*Z*Y + 2.0f*X*W; 421 | dest.data[7] = 0.0f; 422 | 423 | dest.data[8] = 2.0f*X*Z + 2.0f*Y*W; 424 | dest.data[9] = 2.0f*Z*Y - 2.0f*X*W; 425 | dest.data[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y; 426 | dest.data[11] = 0.0f; 427 | 428 | dest.data[12] = center.x; 429 | dest.data[13] = center.y; 430 | dest.data[14] = center.z; 431 | dest.data[15] = 1.f; 432 | 433 | //dest.Identity(); 434 | } 435 | 436 | 437 | /*! 438 | Creates a matrix from this Quaternion 439 | Rotate about a center point 440 | shortcut for 441 | core::Quaternion q; 442 | q.rotationFromTo(vin[i].Normal, forward); 443 | q.getMatrix(lookat, center); 444 | 445 | core::Matrix4F m2; 446 | m2.setInverseTranslation(center); 447 | lookat *= m2; 448 | */ 449 | inline void Quaternion::getMatrixCenter(Matrix4F &dest, 450 | const Vector3DF ¢er, 451 | const Vector3DF &translation) const 452 | { 453 | dest.data[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z; 454 | dest.data[1] = 2.0f*X*Y + 2.0f*Z*W; 455 | dest.data[2] = 2.0f*X*Z - 2.0f*Y*W; 456 | dest.data[3] = 0.0f; 457 | 458 | dest.data[4] = 2.0f*X*Y - 2.0f*Z*W; 459 | dest.data[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z; 460 | dest.data[6] = 2.0f*Z*Y + 2.0f*X*W; 461 | dest.data[7] = 0.0f; 462 | 463 | dest.data[8] = 2.0f*X*Z + 2.0f*Y*W; 464 | dest.data[9] = 2.0f*Z*Y - 2.0f*X*W; 465 | dest.data[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y; 466 | dest.data[11] = 0.0f; 467 | 468 | //dest.setRotationCenter ( center, translation ); 469 | } 470 | 471 | // Creates a matrix from this Quaternion 472 | inline void Quaternion::getMatrix_transposed(Matrix4F &dest) const 473 | { 474 | dest.data[0] = 1.0f - 2.0f*Y*Y - 2.0f*Z*Z; 475 | dest.data[4] = 2.0f*X*Y + 2.0f*Z*W; 476 | dest.data[8] = 2.0f*X*Z - 2.0f*Y*W; 477 | dest.data[12] = 0.0f; 478 | 479 | dest.data[1] = 2.0f*X*Y - 2.0f*Z*W; 480 | dest.data[5] = 1.0f - 2.0f*X*X - 2.0f*Z*Z; 481 | dest.data[9] = 2.0f*Z*Y + 2.0f*X*W; 482 | dest.data[13] = 0.0f; 483 | 484 | dest.data[2] = 2.0f*X*Z + 2.0f*Y*W; 485 | dest.data[6] = 2.0f*Z*Y - 2.0f*X*W; 486 | dest.data[10] = 1.0f - 2.0f*X*X - 2.0f*Y*Y; 487 | dest.data[14] = 0.0f; 488 | 489 | dest.data[3] = 0.f; 490 | dest.data[7] = 0.f; 491 | dest.data[11] = 0.f; 492 | dest.data[15] = 1.f; 493 | 494 | //dest.setDefinitelyIdentityMatrix(false); 495 | } 496 | 497 | 498 | // Inverts this Quaternion 499 | inline Quaternion& Quaternion::makeInverse() 500 | { 501 | X = -X; Y = -Y; Z = -Z; 502 | return *this; 503 | } 504 | 505 | 506 | // sets new Quaternion 507 | inline Quaternion& Quaternion::set(f32 x, f32 y, f32 z, f32 w) 508 | { 509 | X = x; 510 | Y = y; 511 | Z = z; 512 | W = w; 513 | return *this; 514 | } 515 | 516 | // sets new Quaternion based on euler angles 517 | inline Quaternion& Quaternion::set(f32 x, f32 y, f32 z) 518 | { 519 | f64 angle; 520 | 521 | angle = x * 0.5; 522 | const f64 sr = sin(angle); 523 | const f64 cr = cos(angle); 524 | 525 | angle = y * 0.5; 526 | const f64 sp = sin(angle); 527 | const f64 cp = cos(angle); 528 | 529 | angle = z * 0.5; 530 | const f64 sy = sin(angle); 531 | const f64 cy = cos(angle); 532 | 533 | const f64 cpcy = cp * cy; 534 | const f64 spcy = sp * cy; 535 | const f64 cpsy = cp * sy; 536 | const f64 spsy = sp * sy; 537 | 538 | X = (f32)(sr * cpcy - cr * spsy); 539 | Y = (f32)(cr * spcy + sr * cpsy); 540 | Z = (f32)(cr * cpsy - sr * spcy); 541 | W = (f32)(cr * cpcy + sr * spsy); 542 | 543 | return normalize(); 544 | } 545 | 546 | // sets new Quaternion based on euler angles 547 | inline Quaternion& Quaternion::set(const Vector3DF& vec) 548 | { 549 | return set(vec.x*DEGtoRAD,vec.y*DEGtoRAD,vec.z*DEGtoRAD); 550 | } 551 | 552 | // sets new Quaternion based on other Quaternion 553 | inline Quaternion& Quaternion::set(const Quaternion& quat) 554 | { 555 | return (*this=quat); 556 | } 557 | 558 | 559 | //! returns if this Quaternion equals the other one, taking floating point rounding errors into account 560 | inline bool Quaternion::equals(const Quaternion& other, const f32 tolerance) const 561 | { 562 | return fequal(X, other.X, tolerance) && 563 | fequal(Y, other.Y, tolerance) && 564 | fequal(Z, other.Z, tolerance) && 565 | fequal(W, other.W, tolerance); 566 | } 567 | 568 | inline float reciprocal_squareroot ( float number ) 569 | { 570 | long i; 571 | float x2, y; 572 | const float threehalfs = 1.5F; 573 | 574 | x2 = number * 0.5F; 575 | y = number; 576 | i = * ( long * ) &y; 577 | i = 0x5f3759df - ( i >> 1 ); 578 | y = * ( float * ) &i; 579 | y = y * ( threehalfs - ( x2 * y * y ) ); 580 | 581 | #ifndef Q3_VM 582 | #ifdef __linux__ 583 | assert( !isnan(y) ); 584 | #endif 585 | #endif 586 | return y; 587 | } 588 | 589 | // normalizes the Quaternion 590 | inline Quaternion& Quaternion::normalize() 591 | { 592 | const f32 n = X*X + Y*Y + Z*Z + W*W; 593 | 594 | if (n == 1) 595 | return *this; 596 | 597 | //n = 1.0f / sqrtf(n); 598 | return (*this *= reciprocal_squareroot ( n )); 599 | } 600 | 601 | 602 | // set this Quaternion to the result of the linear interpolation between two Quaternions 603 | inline Quaternion& Quaternion::lerp(Quaternion q1, Quaternion q2, f32 time) 604 | { 605 | const f32 scale = 1.0f - time; 606 | return (*this = (q1*scale) + (q2*time)); 607 | } 608 | 609 | 610 | // set this Quaternion to the result of the interpolation between two Quaternions 611 | inline Quaternion& Quaternion::slerp(Quaternion q1, Quaternion q2, f32 time, f32 threshold) 612 | { 613 | f32 angle = q1.dotProduct(q2); 614 | 615 | // make sure we use the short rotation 616 | if (angle < 0.0f) 617 | { 618 | q1 *= -1.0f; 619 | angle *= -1.0f; 620 | } 621 | 622 | if (angle <= (1-threshold)) // spherical interpolation 623 | { 624 | const f32 theta = acosf(angle); 625 | const f32 invsintheta = 1.0f / sinf(theta); 626 | const f32 scale = sinf(theta * (1.0f-time)) * invsintheta; 627 | const f32 invscale = sinf(theta * time) * invsintheta; 628 | return (*this = (q1*scale) + (q2*invscale)); 629 | } 630 | else // linear interploation 631 | return lerp(q1,q2,time); 632 | } 633 | 634 | 635 | // calculates the dot product 636 | inline f32 Quaternion::dotProduct(const Quaternion& q2) const 637 | { 638 | return (X * q2.X) + (Y * q2.Y) + (Z * q2.Z) + (W * q2.W); 639 | } 640 | 641 | 642 | //! axis must be unit length, angle in radians 643 | inline Quaternion& Quaternion::fromAngleAxis(f32 angle, const Vector3DF& axis) 644 | { 645 | const f32 fHalfAngle = 0.5f*angle; 646 | const f32 fSin = sinf(fHalfAngle); 647 | W = cosf(fHalfAngle); 648 | X = fSin*axis.x; 649 | Y = fSin*axis.y; 650 | Z = fSin*axis.z; 651 | return *this; 652 | } 653 | 654 | 655 | inline void Quaternion::toAngleAxis(f32 &angle, Vector3DF &axis) const 656 | { 657 | const f32 scale = sqrtf(X*X + Y*Y + Z*Z); 658 | 659 | if (fabs(scale) 1.0f || W < -1.0f) 660 | { 661 | angle = 0.0f; 662 | axis.x = 0.0f; 663 | axis.y = 1.0f; 664 | axis.z = 0.0f; 665 | } 666 | else 667 | { 668 | const f32 invscale = 1.0 / scale; 669 | angle = 2.0f * acosf(W); 670 | axis.x = X * invscale; 671 | axis.y = Y * invscale; 672 | axis.z = Z * invscale; 673 | } 674 | } 675 | 676 | inline void Quaternion::toEuler(Vector3DF& euler) const 677 | { 678 | const f64 sqw = W*W; 679 | const f64 sqx = X*X; 680 | const f64 sqy = Y*Y; 681 | const f64 sqz = Z*Z; 682 | const f64 test = 2.0 * (Y*W - X*Z); 683 | 684 | if ( fequal (test, 1.0, 0.000001)) 685 | { 686 | // heading = rotation about z-axis 687 | euler.z = (f32) (-2.0*atan2(X, W)); 688 | // bank = rotation about x-axis 689 | euler.x = 0; 690 | // attitude = rotation about y-axis 691 | euler.y = (f32) (PI64/2.0); 692 | } 693 | else if ( fequal (test, -1.0, 0.000001)) 694 | { 695 | // heading = rotation about z-axis 696 | euler.z = (f32) (2.0*atan2(X, W)); 697 | // bank = rotation about x-axis 698 | euler.x = 0; 699 | // attitude = rotation about y-axis 700 | euler.y = (f32) (PI64/-2.0); 701 | } 702 | else 703 | { 704 | // heading = rotation about z-axis 705 | euler.z = (f32) atan2(2.0 * (X*Y +Z*W),(sqx - sqy - sqz + sqw)); 706 | // bank = rotation about x-axis 707 | euler.x = (f32) atan2(2.0 * (Y*Z +X*W),(-sqx - sqy + sqz + sqw)); 708 | // attitude = rotation about y-axis 709 | euler.y = (f32) asin( clamp(test, -1.0, 1.0) ); 710 | } 711 | 712 | euler.x *= RADtoDEG; 713 | euler.y *= RADtoDEG; 714 | euler.z *= RADtoDEG; 715 | } 716 | 717 | 718 | inline Vector3DF Quaternion::operator* (const Vector3DF& v) const 719 | { 720 | // nVidia SDK implementation 721 | 722 | Vector3DF uv, uuv; 723 | Vector3DF qvec; qvec.Set( float(X), float(Y), float(Z) ); 724 | uv = qvec.Cross (v); 725 | uuv = qvec.Cross (uv); 726 | uv *= (2.0f * float(W) ); 727 | uuv *= 2.0f; 728 | 729 | uuv += uv; 730 | uuv += v; 731 | return uuv; 732 | } 733 | 734 | // set Quaternion to identity 735 | inline Quaternion& Quaternion::makeIdentity() 736 | { 737 | W = 1.f; 738 | X = 0.f; 739 | Y = 0.f; 740 | Z = 0.f; 741 | return *this; 742 | } 743 | 744 | inline Quaternion& Quaternion::rotationFromTo(const Vector3DF& from, const Vector3DF& to) 745 | { 746 | // Based on Stan Melax's article in Game Programming Gems 747 | // Copy, since cannot modify local 748 | Vector3DF v0 = from; 749 | Vector3DF v1 = to; 750 | v0.Normalize(); 751 | v1.Normalize(); 752 | 753 | const f32 d = v0.Dot (v1); 754 | if (d >= 1.0f) // If dot == 1, vectors are the same 755 | { 756 | return makeIdentity(); 757 | } 758 | else if (d <= -1.0f) // exactly opposite 759 | { 760 | Vector3DF axis(1.0f, 0.f, 0.f); 761 | axis = axis.Cross(v0); 762 | if (axis.Length()==0) 763 | { 764 | axis.Set(0.f,1.f,0.f); 765 | axis.Cross(v0); 766 | } 767 | // same as fromAngleAxis(core::PI, axis).normalize(); 768 | return set(axis.x, axis.y, axis.z, 0).normalize(); 769 | } 770 | 771 | const f32 s = sqrtf( (1+d)*2 ); // optimize inv_sqrt 772 | const f32 invs = 1.f / s; 773 | const Vector3DF c = v0.Cross(v1)*invs; 774 | return set(c.x, c.y, c.z, s * 0.5f).normalize(); 775 | } 776 | 777 | #endif 778 | --------------------------------------------------------------------------------