diff options
-rw-r--r-- | CMakeLists.txt | 158 | ||||
-rw-r--r-- | contrib/cleanup/tab2spaces.sh (renamed from opt/cleanup/tab2spaces.sh) | 0 | ||||
-rw-r--r-- | contrib/cleanup/whitespace.sh (renamed from opt/cleanup/whitespace.sh) | 0 | ||||
-rw-r--r-- | contrib/conf_merge/README (renamed from opt/conf_merge/README) | 0 | ||||
-rw-r--r-- | contrib/conf_merge/index.php (renamed from opt/conf_merge/index.php) | 0 | ||||
-rw-r--r-- | contrib/conf_merge/merge.php (renamed from opt/conf_merge/merge.php) | 0 | ||||
-rw-r--r-- | docs/AuctionHouseBot.txt (renamed from doc/AuctionHouseBot.txt) | 0 | ||||
-rw-r--r-- | docs/DocStructure.dox (renamed from doc/DocStructure.dox) | 0 | ||||
-rw-r--r-- | docs/Doxyfile.in (renamed from doc/Doxyfile.in) | 0 | ||||
-rw-r--r-- | docs/EventAI.txt (renamed from doc/EventAI.txt) | 0 | ||||
-rw-r--r-- | docs/HowToScript.txt (renamed from doc/HowToScript.txt) | 0 | ||||
-rw-r--r-- | docs/TextTables.txt (renamed from doc/TextTables.txt) | 0 | ||||
-rw-r--r-- | docs/UnixInstall.txt (renamed from doc/UnixInstall.txt) | 0 | ||||
-rw-r--r-- | externals/CMakeLists.txt | 7 | ||||
-rw-r--r-- | externals/PackageList.txt | 17 | ||||
-rw-r--r-- | externals/bzip2/CHANGES | 319 | ||||
-rw-r--r-- | externals/bzip2/CMakeLists.txt | 10 | ||||
-rw-r--r-- | externals/bzip2/LICENSE | 42 | ||||
-rw-r--r-- | externals/g3dlite/AABox.cpp | 366 | ||||
-rw-r--r-- | externals/g3dlite/Any.cpp | 1237 | ||||
-rw-r--r-- | externals/g3dlite/BinaryFormat.cpp | 81 | ||||
-rw-r--r-- | externals/g3dlite/BinaryInput.cpp | 568 | ||||
-rw-r--r-- | externals/g3dlite/BinaryOutput.cpp | 522 | ||||
-rw-r--r-- | externals/g3dlite/Box.cpp | 393 | ||||
-rw-r--r-- | externals/g3dlite/CMakeLists.txt | 11 | ||||
-rw-r--r-- | externals/g3dlite/Capsule.cpp | 179 | ||||
-rw-r--r-- | externals/g3dlite/CollisionDetection.cpp | 2455 | ||||
-rw-r--r-- | externals/g3dlite/CoordinateFrame.cpp | 436 | ||||
-rw-r--r-- | externals/g3dlite/Crypto.cpp | 70 | ||||
-rw-r--r-- | externals/g3dlite/Cylinder.cpp | 176 | ||||
-rw-r--r-- | externals/g3dlite/Line.cpp | 89 | ||||
-rw-r--r-- | externals/g3dlite/LineSegment.cpp | 236 | ||||
-rw-r--r-- | externals/g3dlite/Log.cpp | 146 | ||||
-rw-r--r-- | externals/g3dlite/Matrix3.cpp | 1927 | ||||
-rw-r--r-- | externals/g3dlite/Matrix4.cpp | 523 | ||||
-rw-r--r-- | externals/g3dlite/MemoryManager.cpp | 91 | ||||
-rw-r--r-- | externals/g3dlite/Plane.cpp | 149 | ||||
-rw-r--r-- | externals/g3dlite/Quat.cpp | 583 | ||||
-rw-r--r-- | externals/g3dlite/Random.cpp | 212 | ||||
-rw-r--r-- | externals/g3dlite/Ray.cpp | 218 | ||||
-rw-r--r-- | externals/g3dlite/ReferenceCount.cpp | 61 | ||||
-rw-r--r-- | externals/g3dlite/Sphere.cpp | 223 | ||||
-rw-r--r-- | externals/g3dlite/System.cpp | 1746 | ||||
-rw-r--r-- | externals/g3dlite/TextInput.cpp | 1136 | ||||
-rw-r--r-- | externals/g3dlite/TextOutput.cpp | 452 | ||||
-rw-r--r-- | externals/g3dlite/Triangle.cpp | 186 | ||||
-rw-r--r-- | externals/g3dlite/UprightFrame.cpp | 132 | ||||
-rw-r--r-- | externals/g3dlite/Vector2.cpp | 224 | ||||
-rw-r--r-- | externals/g3dlite/Vector3.cpp | 507 | ||||
-rw-r--r-- | externals/g3dlite/Vector4.cpp | 520 | ||||
-rw-r--r-- | externals/g3dlite/debugAssert.cpp | 389 | ||||
-rw-r--r-- | externals/g3dlite/fileutils.cpp | 1165 | ||||
-rw-r--r-- | externals/g3dlite/format.cpp | 164 | ||||
-rw-r--r-- | externals/g3dlite/g3dfnmatch.cpp | 204 | ||||
-rw-r--r-- | externals/g3dlite/g3dmath.cpp | 108 | ||||
-rw-r--r-- | externals/g3dlite/prompt.cpp | 729 | ||||
-rw-r--r-- | externals/g3dlite/stringutils.cpp | 275 | ||||
-rw-r--r-- | externals/jemalloc/CMakeLists.txt | 32 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/arena.h (renamed from externals/jemalloc/include/internal/arena.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/base.h (renamed from externals/jemalloc/include/internal/base.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/chunk.h (renamed from externals/jemalloc/include/internal/chunk.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/chunk_dss.h (renamed from externals/jemalloc/include/internal/chunk_dss.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/chunk_mmap.h (renamed from externals/jemalloc/include/internal/chunk_mmap.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/chunk_swap.h (renamed from externals/jemalloc/include/internal/chunk_swap.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/ckh.h (renamed from externals/jemalloc/include/internal/ckh.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/ctl.h (renamed from externals/jemalloc/include/internal/ctl.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/extent.h (renamed from externals/jemalloc/include/internal/extent.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/hash.h (renamed from externals/jemalloc/include/internal/hash.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/huge.h (renamed from externals/jemalloc/include/internal/huge.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/jemalloc_internal.h (renamed from externals/jemalloc/include/internal/jemalloc_internal.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/jemalloc_internal.h.in (renamed from externals/jemalloc/include/internal/jemalloc_internal.h.in) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/mb.h (renamed from externals/jemalloc/include/internal/mb.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/mutex.h (renamed from externals/jemalloc/include/internal/mutex.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/prof.h (renamed from externals/jemalloc/include/internal/prof.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/ql.h (renamed from externals/jemalloc/include/internal/ql.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/qr.h (renamed from externals/jemalloc/include/internal/qr.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/rb.h (renamed from externals/jemalloc/include/internal/rb.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/stats.h (renamed from externals/jemalloc/include/internal/stats.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/tcache.h (renamed from externals/jemalloc/include/internal/tcache.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/internal/totally_not_p_r_n.h (renamed from externals/jemalloc/include/internal/totally_not_p_r_n.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/jemalloc.h (renamed from externals/jemalloc/include/jemalloc.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/jemalloc.h.in (renamed from externals/jemalloc/include/jemalloc.h.in) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/jemalloc_defs.h (renamed from externals/jemalloc/include/jemalloc_defs.h) | 0 | ||||
-rw-r--r-- | externals/jemalloc/jemalloc/jemalloc_defs.h.in (renamed from externals/jemalloc/include/jemalloc_defs.h.in) | 0 | ||||
-rw-r--r-- | externals/mersennetwister/delme | 0 | ||||
-rw-r--r-- | externals/sockets/CMakeLists.txt | 24 | ||||
-rw-r--r-- | externals/sockets/Makefile | 136 | ||||
-rw-r--r-- | externals/zlib/CMakeLists.txt | 12 | ||||
-rw-r--r-- | externals/zlib/ChangeLog | 1208 | ||||
-rw-r--r-- | externals/zlib/README | 115 | ||||
-rw-r--r-- | src/server/collision/CMakeLists.txt | 12 | ||||
-rw-r--r-- | src/server/game/CMakeLists.txt | 4 | ||||
-rw-r--r-- | src/server/worldserver/CMakeLists.txt | 4 |
93 files changed, 19176 insertions, 1813 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index e33a04eaaf6..e7384b93cae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,19 +12,22 @@ include(cmake/PCH.cmake) # Force out-of-source build string(COMPARE EQUAL "${CMAKE_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" BUILDING_IN_SOURCE) if(BUILDING_IN_SOURCE) - message(FATAL_ERROR "This project requires an out of source build. Remove the file 'CMakeCache.txt' found in this directory before continuing, and create a separate build directory and run 'cmake path_to_project [options]' from there.") + message(FATAL_ERROR "This project requires an out of source build. Remove the file 'CMakeCache.txt' found in this directory before continuing, and create a separate build directory and run 'cmake path_to_project [options]' from there.") endif(BUILDING_IN_SOURCE) # Select the Release build configuration by default. if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release") + set(CMAKE_BUILD_TYPE "Release") endif(NOT CMAKE_BUILD_TYPE) CONFIGURE_FILE( - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" - "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" - IMMEDIATE @ONLY) -add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake") + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + +add_custom_target(uninstall + "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" +) option(CENTOS "CENTOS" 0) option(DO_CLI "With CLI" 1) @@ -38,49 +41,49 @@ option(DO_TOOLS "Compile tools" 0) option(DO_WARN "Enable all compile warnings" 0) set(GENREV_SRC -src/genrevision/genrevision.cpp + src/genrevision/genrevision.cpp ) if(DO_DEBUG) - add_executable(genrev + add_executable(genrev ${GENREV_SRC} - ) - add_custom_target("revision.h" ALL - COMMAND "${CMAKE_BINARY_DIR}/genrev" - ${CMAKE_SOURCE_DIR} - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/shared" - DEPENDS genrev - ) + ) + add_custom_target("revision.h" ALL + COMMAND "${CMAKE_BINARY_DIR}/genrev" + ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/server/shared" + DEPENDS genrev + ) else (DO_DEBUG) - add_executable(genrev + add_executable(genrev ${GENREV_SRC} - ) - add_custom_target("revision.h" ALL - COMMAND "${CMAKE_BINARY_DIR}/genrev" - ${CMAKE_SOURCE_DIR} - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/server/shared" - DEPENDS genrev - ) + ) + add_custom_target("revision.h" ALL + COMMAND "${CMAKE_BINARY_DIR}/genrev" + ${CMAKE_SOURCE_DIR} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/src/server/shared" + DEPENDS genrev + ) endif(DO_DEBUG) - execute_process( - COMMAND hg tip --template {rev} - WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" - OUTPUT_VARIABLE HG_REVISION + COMMAND hg tip --template {rev} + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + OUTPUT_VARIABLE HG_REVISION ) message("* TrinityCore revision: ${HG_REVISION}") if(PREFIX) - set(CMAKE_INSTALL_PREFIX ${PREFIX}) + set(CMAKE_INSTALL_PREFIX ${PREFIX}) endif(PREFIX) if(CONF_DIR) else(CONF_DIR) - set(CONF_DIR ${PREFIX}/etc) + set(CONF_DIR ${PREFIX}/etc) endif(CONF_DIR) + set(LIBSDIR ${CMAKE_INSTALL_PREFIX}/lib) message("* Will install to: ${CMAKE_INSTALL_PREFIX}") @@ -91,98 +94,97 @@ find_library(SSLLIB NAMES ssl DOC "SSL library") find_library(ZLIB z "Zlib library") if(DO_MYSQL) - message("* With MySQL") - FIND_MYSQL() - ADD_DEFINITIONS(-DDO_MYSQL) + message("* With MySQL") + FIND_MYSQL() + ADD_DEFINITIONS(-DDO_MYSQL) endif(DO_MYSQL) if(DO_SCRIPTS) - message("* With Trinity Scripts") - ADD_DEFINITIONS(-DDO_SCRIPTS) - add_definitions(-D_TRINITY_SCRIPT_CONFIG='"${CONF_DIR}/trinitycore.conf"') + message("* With Trinity Scripts") + ADD_DEFINITIONS(-DDO_SCRIPTS) + add_definitions(-D_TRINITY_SCRIPT_CONFIG='"${CONF_DIR}/trinitycore.conf"') else (DO_SCRIPTS) - message("* Without Trinity Scripts") + message("* Without Trinity Scripts") endif(DO_SCRIPTS) message("-- Miscellaneus options:") if(DO_CLI) - message("* With CLI") - add_definitions(-DENABLE_CLI) + message("* With CLI") + add_definitions(-DENABLE_CLI) else (DO_CLI) - message(* Without CLI) + message(* Without CLI) endif(DO_CLI) if(DO_RA) - message("* With RA") - add_definitions(-DENABLE_RA) + message("* With RA") + add_definitions(-DENABLE_RA) else(DO_RA) - message("* Without RA") + message("* Without RA") endif(DO_RA) if(DO_DEBUG) - message("* Debug mode ON") - add_definitions(-g -DTRINITY_DEBUG) + message("* Debug mode ON") + add_definitions(-g -DTRINITY_DEBUG) endif(DO_DEBUG) if(DO_WARN) - message("* All warnings mode") - add_definitions(-Wall -Wfatal-errors -Wextra) + message("* All warnings mode") + add_definitions(-Wall -Wfatal-errors -Wextra) endif(DO_WARN) if(DO_SQL) - message("* Installing SQL files") + message("* Installing SQL files") else (DO_SQL) - message("* NOT installing SQL files") + message("* NOT installing SQL files") endif(DO_SQL) if(DO_PCH) - message("* Using precompiled headers") + message("* Using precompiled headers") else (DO_PCH) - message("* NOT using precompiled headers") + message("* NOT using precompiled headers") endif(DO_PCH) if(DO_TOOLS) - message("* With Tools") + message("* With Tools") else (DO_TOOLS) - message("* Without Tools") + message("* Without Tools") endif(DO_TOOLS) - if(UNIX) - if(CENTOS) - add_definitions(-DCENTOS) - message("* Building with termcap") - FIND_TERMCAP() - else(CENTOS) - message("* Building with readline") - FIND_READLINE() - endif(CENTOS) + if(CENTOS) + add_definitions(-DCENTOS) + message("* Building with termcap") + FIND_TERMCAP() + else(CENTOS) + message("* Building with readline") + FIND_READLINE() + endif(CENTOS) endif(UNIX) FIND_ACE(ACE) if(ACE_FOUND) - message(STATUS "Found ACE library: ${ACE_LIBRARY}") - message(STATUS "Include dir is: ${ACE_INCLUDE_DIR}") + message(STATUS "Found ACE library: ${ACE_LIBRARY}") + message(STATUS "Include dir is: ${ACE_INCLUDE_DIR}") else(ACE_FOUND) - message(SEND_ERROR "** ACE library not found! Trinity Core cannot be compiled!") - message(SEND_ERROR "** Please build ACE from http://www.cs.wustl.edu/~schmidt/ACE.html") - #For now remove msg about install from repo, as ubuntu/debian don't have needed ver in repos. - #message(SEND_ERROR "** your distro may provide a binary for ACE e.g. for ubuntu try apt-get install libace-dev") - return() - #set(BUILD_ACE 1) - #set(ACE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dep/ACE_wrappers ${CMAKE_BINARY_DIR}/dep/ACE_wrappers") - #set(ACE_LIBRARY ACE) - #message(STATUS "I will try to build ACE from: ${ACE_INCLUDE_DIR}") - #message(STATUS "And link using: ${ACE_LIBRARY}") + message(SEND_ERROR "** ACE library not found! Trinity Core cannot be compiled!") + message(SEND_ERROR "** Please build ACE from http://www.cs.wustl.edu/~schmidt/ACE.html") + #For now remove msg about install from repo, as ubuntu/debian don't have needed ver in repos. + #message(SEND_ERROR "** your distro may provide a binary for ACE e.g. for ubuntu try apt-get install libace-dev") + return() + #set(BUILD_ACE 1) + #set(ACE_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/dep/ACE_wrappers ${CMAKE_BINARY_DIR}/dep/ACE_wrappers") + #set(ACE_LIBRARY ACE) + #message(STATUS "I will try to build ACE from: ${ACE_INCLUDE_DIR}") + #message(STATUS "And link using: ${ACE_LIBRARY}") endif(ACE_FOUND) #somehow line below don't work. so for now change it to if exist #check_include_files(${ACE_INCLUDE_DIR}/ace/Stack_Trace.h HAVE_ACE_STACK_TRACE_H) if(EXISTS ${ACE_INCLUDE_DIR}/ace/Stack_Trace.h) - set(HAVE_ACE_STACK_TRACE_H 1) + set(HAVE_ACE_STACK_TRACE_H 1) else(EXISTS ${ACE_INCLUDE_DIR}/ace/Stack_Trace.h) - message(STATUS "** Your libace is out of date. Please update your libace!") + message(STATUS "** Your libace is out of date. Please update your libace!") endif(EXISTS ${ACE_INCLUDE_DIR}/ace/Stack_Trace.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h) @@ -201,8 +203,10 @@ set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) +add_subdirectory(externals) add_subdirectory(src) + if(DO_SQL) - message("* Copy SQL files ON") - add_subdirectory(sql) + message("* Copy SQL files ON") + add_subdirectory(sql) endif(DO_SQL) diff --git a/opt/cleanup/tab2spaces.sh b/contrib/cleanup/tab2spaces.sh index 1022be7905c..1022be7905c 100644 --- a/opt/cleanup/tab2spaces.sh +++ b/contrib/cleanup/tab2spaces.sh diff --git a/opt/cleanup/whitespace.sh b/contrib/cleanup/whitespace.sh index a351e25359b..a351e25359b 100644 --- a/opt/cleanup/whitespace.sh +++ b/contrib/cleanup/whitespace.sh diff --git a/opt/conf_merge/README b/contrib/conf_merge/README index 3d027b7ad42..3d027b7ad42 100644 --- a/opt/conf_merge/README +++ b/contrib/conf_merge/README diff --git a/opt/conf_merge/index.php b/contrib/conf_merge/index.php index 537937caf64..537937caf64 100644 --- a/opt/conf_merge/index.php +++ b/contrib/conf_merge/index.php diff --git a/opt/conf_merge/merge.php b/contrib/conf_merge/merge.php index 6d8bedbaa08..6d8bedbaa08 100644 --- a/opt/conf_merge/merge.php +++ b/contrib/conf_merge/merge.php diff --git a/doc/AuctionHouseBot.txt b/docs/AuctionHouseBot.txt index cc15a084c9a..cc15a084c9a 100644 --- a/doc/AuctionHouseBot.txt +++ b/docs/AuctionHouseBot.txt diff --git a/doc/DocStructure.dox b/docs/DocStructure.dox index dee8b5a0877..dee8b5a0877 100644 --- a/doc/DocStructure.dox +++ b/docs/DocStructure.dox diff --git a/doc/Doxyfile.in b/docs/Doxyfile.in index 9893593751c..9893593751c 100644 --- a/doc/Doxyfile.in +++ b/docs/Doxyfile.in diff --git a/doc/EventAI.txt b/docs/EventAI.txt index 7d0294da05f..7d0294da05f 100644 --- a/doc/EventAI.txt +++ b/docs/EventAI.txt diff --git a/doc/HowToScript.txt b/docs/HowToScript.txt index 73968a08b19..73968a08b19 100644 --- a/doc/HowToScript.txt +++ b/docs/HowToScript.txt diff --git a/doc/TextTables.txt b/docs/TextTables.txt index eac63384785..eac63384785 100644 --- a/doc/TextTables.txt +++ b/docs/TextTables.txt diff --git a/doc/UnixInstall.txt b/docs/UnixInstall.txt index a0bd7e07715..a0bd7e07715 100644 --- a/doc/UnixInstall.txt +++ b/docs/UnixInstall.txt diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt new file mode 100644 index 00000000000..f74c476ac4c --- /dev/null +++ b/externals/CMakeLists.txt @@ -0,0 +1,7 @@ +add_subdirectory(ace) +#add_subdirectory(bzip2) +add_subdirectory(g3dlite) +add_subdirectory(jemalloc) +#add_subdirectory(libmpq) +add_subdirectory(sockets) +add_subdirectory(zlib) diff --git a/externals/PackageList.txt b/externals/PackageList.txt index 168d0428983..5bb1c3a873a 100644 --- a/externals/PackageList.txt +++ b/externals/PackageList.txt @@ -2,39 +2,52 @@ TrinityCore uses (parts of or in whole) the following opensource software : ACE (ADAPTIVE Communication Environment) http://www.cs.wustl.edu/~schmidt/ACE.html + Version: UNKNOWN bzip2 (a freely available, patent free, high-quality data compressor) http://www.bzip.org/ + Version: 1.0.5 -G3D 6.09 (a commercial-grade C++ 3D engine available as Open Source (BSD License) +G3D (a commercial-grade C++ 3D engine available as Open Source (BSD License) http://g3d.sourceforge.net/ + Version: 6.09 jemalloc (a general-purpose scalable concurrent malloc-implementation) http://www.canonware.com/jemalloc/ - + Version: UNKNOWN + libMPQ (a library for reading MPQ files) https://libmpq.org/ + Version: 1.0.4 MersenneTwister (a very fast random number generator) http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + Version: 0.4.2 SFMT (SIMD-oriented Fast Mersenne Twister) http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/index.html + Version: 1.3.3 MySQL (the world's most popular open source database software) http://www.mysql.com/about/ + Version: UNKNOWN OpenSSL (an opensource toolkit implementing SSL v2/v3 and TLS v1 protocols) http://www.openssl.org/ + Version: UNKNOWN sockets (a GPL licensed C++ class library wrapping the berkeley sockets C API) http://www.alhem.net/Sockets/ + Version: UNKNOWN utf8-cpp (UTF-8 with C++ in a Portable Way) http://utfcpp.sourceforge.net/ + Version: 2.3 vld (a free open-source memory leak detection system for Visual C++) http://sites.google.com/site/dmoulding/vld + Version: 1.0 zlib (A Massively Spiffy Yet Delicately Unobtrusive Compression Library) http://www.zlib.net/ + Version: 1.2.5 diff --git a/externals/bzip2/CHANGES b/externals/bzip2/CHANGES deleted file mode 100644 index 6e4f65e2e0a..00000000000 --- a/externals/bzip2/CHANGES +++ /dev/null @@ -1,319 +0,0 @@ - ------------------------------------------------------------------ - This file is part of bzip2/libbzip2, a program and library for - lossless, block-sorting data compression. - - bzip2/libbzip2 version 1.0.5 of 10 December 2007 - Copyright (C) 1996-2007 Julian Seward <jseward@bzip.org> - - Please read the WARNING, DISCLAIMER and PATENTS sections in the - README file. - - This program is released under the terms of the license contained - in the file LICENSE. - ------------------------------------------------------------------ - - -0.9.0 -~~~~~ -First version. - - -0.9.0a -~~~~~~ -Removed 'ranlib' from Makefile, since most modern Unix-es -don't need it, or even know about it. - - -0.9.0b -~~~~~~ -Fixed a problem with error reporting in bzip2.c. This does not effect -the library in any way. Problem is: versions 0.9.0 and 0.9.0a (of the -program proper) compress and decompress correctly, but give misleading -error messages (internal panics) when an I/O error occurs, instead of -reporting the problem correctly. This shouldn't give any data loss -(as far as I can see), but is confusing. - -Made the inline declarations disappear for non-GCC compilers. - - -0.9.0c -~~~~~~ -Fixed some problems in the library pertaining to some boundary cases. -This makes the library behave more correctly in those situations. The -fixes apply only to features (calls and parameters) not used by -bzip2.c, so the non-fixedness of them in previous versions has no -effect on reliability of bzip2.c. - -In bzlib.c: - * made zero-length BZ_FLUSH work correctly in bzCompress(). - * fixed bzWrite/bzRead to ignore zero-length requests. - * fixed bzread to correctly handle read requests after EOF. - * wrong parameter order in call to bzDecompressInit in - bzBuffToBuffDecompress. Fixed. - -In compress.c: - * changed setting of nGroups in sendMTFValues() so as to - do a bit better on small files. This _does_ effect - bzip2.c. - - -0.9.5a -~~~~~~ -Major change: add a fallback sorting algorithm (blocksort.c) -to give reasonable behaviour even for very repetitive inputs. -Nuked --repetitive-best and --repetitive-fast since they are -no longer useful. - -Minor changes: mostly a whole bunch of small changes/ -bugfixes in the driver (bzip2.c). Changes pertaining to the -user interface are: - - allow decompression of symlink'd files to stdout - decompress/test files even without .bz2 extension - give more accurate error messages for I/O errors - when compressing/decompressing to stdout, don't catch control-C - read flags from BZIP2 and BZIP environment variables - decline to break hard links to a file unless forced with -f - allow -c flag even with no filenames - preserve file ownerships as far as possible - make -s -1 give the expected block size (100k) - add a flag -q --quiet to suppress nonessential warnings - stop decoding flags after --, so files beginning in - can be handled - resolved inconsistent naming: bzcat or bz2cat ? - bzip2 --help now returns 0 - -Programming-level changes are: - - fixed syntax error in GET_LL4 for Borland C++ 5.02 - let bzBuffToBuffDecompress return BZ_DATA_ERROR{_MAGIC} - fix overshoot of mode-string end in bzopen_or_bzdopen - wrapped bzlib.h in #ifdef __cplusplus ... extern "C" { ... } - close file handles under all error conditions - added minor mods so it compiles with DJGPP out of the box - fixed Makefile so it doesn't give problems with BSD make - fix uninitialised memory reads in dlltest.c - -0.9.5b -~~~~~~ -Open stdin/stdout in binary mode for DJGPP. - -0.9.5c -~~~~~~ -Changed BZ_N_OVERSHOOT to be ... + 2 instead of ... + 1. The + 1 -version could cause the sorted order to be wrong in some extremely -obscure cases. Also changed setting of quadrant in blocksort.c. - -0.9.5d -~~~~~~ -The only functional change is to make bzlibVersion() in the library -return the correct string. This has no effect whatsoever on the -functioning of the bzip2 program or library. Added a couple of casts -so the library compiles without warnings at level 3 in MS Visual -Studio 6.0. Included a Y2K statement in the file Y2K_INFO. All other -changes are minor documentation changes. - -1.0 -~~~ -Several minor bugfixes and enhancements: - -* Large file support. The library uses 64-bit counters to - count the volume of data passing through it. bzip2.c - is now compiled with -D_FILE_OFFSET_BITS=64 to get large - file support from the C library. -v correctly prints out - file sizes greater than 4 gigabytes. All these changes have - been made without assuming a 64-bit platform or a C compiler - which supports 64-bit ints, so, except for the C library - aspect, they are fully portable. - -* Decompression robustness. The library/program should be - robust to any corruption of compressed data, detecting and - handling _all_ corruption, instead of merely relying on - the CRCs. What this means is that the program should - never crash, given corrupted data, and the library should - always return BZ_DATA_ERROR. - -* Fixed an obscure race-condition bug only ever observed on - Solaris, in which, if you were very unlucky and issued - control-C at exactly the wrong time, both input and output - files would be deleted. - -* Don't run out of file handles on test/decompression when - large numbers of files have invalid magic numbers. - -* Avoid library namespace pollution. Prefix all exported - symbols with BZ2_. - -* Minor sorting enhancements from my DCC2000 paper. - -* Advance the version number to 1.0, so as to counteract the - (false-in-this-case) impression some people have that programs - with version numbers less than 1.0 are in some way, experimental, - pre-release versions. - -* Create an initial Makefile-libbz2_so to build a shared library. - Yes, I know I should really use libtool et al ... - -* Make the program exit with 2 instead of 0 when decompression - fails due to a bad magic number (ie, an invalid bzip2 header). - Also exit with 1 (as the manual claims :-) whenever a diagnostic - message would have been printed AND the corresponding operation - is aborted, for example - bzip2: Output file xx already exists. - When a diagnostic message is printed but the operation is not - aborted, for example - bzip2: Can't guess original name for wurble -- using wurble.out - then the exit value 0 is returned, unless some other problem is - also detected. - - I think it corresponds more closely to what the manual claims now. - - -1.0.1 -~~~~~ -* Modified dlltest.c so it uses the new BZ2_ naming scheme. -* Modified makefile-msc to fix minor build probs on Win2k. -* Updated README.COMPILATION.PROBLEMS. - -There are no functionality changes or bug fixes relative to version -1.0.0. This is just a documentation update + a fix for minor Win32 -build problems. For almost everyone, upgrading from 1.0.0 to 1.0.1 is -utterly pointless. Don't bother. - - -1.0.2 -~~~~~ -A bug fix release, addressing various minor issues which have appeared -in the 18 or so months since 1.0.1 was released. Most of the fixes -are to do with file-handling or documentation bugs. To the best of my -knowledge, there have been no data-loss-causing bugs reported in the -compression/decompression engine of 1.0.0 or 1.0.1. - -Note that this release does not improve the rather crude build system -for Unix platforms. The general plan here is to autoconfiscate/ -libtoolise 1.0.2 soon after release, and release the result as 1.1.0 -or perhaps 1.2.0. That, however, is still just a plan at this point. - -Here are the changes in 1.0.2. Bug-reporters and/or patch-senders in -parentheses. - -* Fix an infinite segfault loop in 1.0.1 when a directory is - encountered in -f (force) mode. - (Trond Eivind Glomsrod, Nicholas Nethercote, Volker Schmidt) - -* Avoid double fclose() of output file on certain I/O error paths. - (Solar Designer) - -* Don't fail with internal error 1007 when fed a long stream (> 48MB) - of byte 251. Also print useful message suggesting that 1007s may be - caused by bad memory. - (noticed by Juan Pedro Vallejo, fixed by me) - -* Fix uninitialised variable silly bug in demo prog dlltest.c. - (Jorj Bauer) - -* Remove 512-MB limitation on recovered file size for bzip2recover - on selected platforms which support 64-bit ints. At the moment - all GCC supported platforms, and Win32. - (me, Alson van der Meulen) - -* Hard-code header byte values, to give correct operation on platforms - using EBCDIC as their native character set (IBM's OS/390). - (Leland Lucius) - -* Copy file access times correctly. - (Marty Leisner) - -* Add distclean and check targets to Makefile. - (Michael Carmack) - -* Parameterise use of ar and ranlib in Makefile. Also add $(LDFLAGS). - (Rich Ireland, Bo Thorsen) - -* Pass -p (create parent dirs as needed) to mkdir during make install. - (Jeremy Fusco) - -* Dereference symlinks when copying file permissions in -f mode. - (Volker Schmidt) - -* Majorly simplify implementation of uInt64_qrm10. - (Bo Lindbergh) - -* Check the input file still exists before deleting the output one, - when aborting in cleanUpAndFail(). - (Joerg Prante, Robert Linden, Matthias Krings) - -Also a bunch of patches courtesy of Philippe Troin, the Debian maintainer -of bzip2: - -* Wrapper scripts (with manpages): bzdiff, bzgrep, bzmore. - -* Spelling changes and minor enhancements in bzip2.1. - -* Avoid race condition between creating the output file and setting its - interim permissions safely, by using fopen_output_safely(). - No changes to bzip2recover since there is no issue with file - permissions there. - -* do not print senseless report with -v when compressing an empty - file. - -* bzcat -f works on non-bzip2 files. - -* do not try to escape shell meta-characters on unix (the shell takes - care of these). - -* added --fast and --best aliases for -1 -9 for gzip compatibility. - - -1.0.3 (15 Feb 05) -~~~~~~~~~~~~~~~~~ -Fixes some minor bugs since the last version, 1.0.2. - -* Further robustification against corrupted compressed data. - There are currently no known bitstreams which can cause the - decompressor to crash, loop or access memory which does not - belong to it. If you are using bzip2 or the library to - decompress bitstreams from untrusted sources, an upgrade - to 1.0.3 is recommended. This fixes CAN-2005-1260. - -* The documentation has been converted to XML, from which html - and pdf can be derived. - -* Various minor bugs in the documentation have been fixed. - -* Fixes for various compilation warnings with newer versions of - gcc, and on 64-bit platforms. - -* The BZ_NO_STDIO cpp symbol was not properly observed in 1.0.2. - This has been fixed. - - -1.0.4 (20 Dec 06) -~~~~~~~~~~~~~~~~~ -Fixes some minor bugs since the last version, 1.0.3. - -* Fix file permissions race problem (CAN-2005-0953). - -* Avoid possible segfault in BZ2_bzclose. From Coverity's NetBSD - scan. - -* 'const'/prototype cleanups in the C code. - -* Change default install location to /usr/local, and handle multiple - 'make install's without error. - -* Sanitise file names more carefully in bzgrep. Fixes CAN-2005-0758 - to the extent that applies to bzgrep. - -* Use 'mktemp' rather than 'tempfile' in bzdiff. - -* Tighten up a couple of assertions in blocksort.c following automated - analysis. - -* Fix minor doc/comment bugs. - - -1.0.5 (10 Dec 07) -~~~~~~~~~~~~~~~~~ -Security fix only. Fixes CERT-FI 20469 as it applies to bzip2. - diff --git a/externals/bzip2/CMakeLists.txt b/externals/bzip2/CMakeLists.txt new file mode 100644 index 00000000000..d0d1ac04bcd --- /dev/null +++ b/externals/bzip2/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB sources *.cpp) +set(bzip2_STAT_SRCS + ${sources} +) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_library(bzip2 STATIC ${bzip2_STAT_SRCS}) diff --git a/externals/bzip2/LICENSE b/externals/bzip2/LICENSE deleted file mode 100644 index f420cffb67d..00000000000 --- a/externals/bzip2/LICENSE +++ /dev/null @@ -1,42 +0,0 @@ - --------------------------------------------------------------------------- - -This program, "bzip2", the associated library "libbzip2", and all -documentation, are copyright (C) 1996-2007 Julian R Seward. All -rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - -1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -2. The origin of this software must not be misrepresented; you must - not claim that you wrote the original software. If you use this - software in a product, an acknowledgment in the product - documentation would be appreciated but is not required. - -3. Altered source versions must be plainly marked as such, and must - not be misrepresented as being the original software. - -4. The name of the author may not be used to endorse or promote - products derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY -DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE -GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -Julian Seward, jseward@bzip.org -bzip2/libbzip2 version 1.0.5 of 10 December 2007 - --------------------------------------------------------------------------- diff --git a/externals/g3dlite/AABox.cpp b/externals/g3dlite/AABox.cpp new file mode 100644 index 00000000000..035497aa3c4 --- /dev/null +++ b/externals/g3dlite/AABox.cpp @@ -0,0 +1,366 @@ +/** + @file AABox.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2004-01-10 + @edited 2006-01-11 +*/ + +#include "G3D/platform.h" +#include "G3D/AABox.h" +#include "G3D/Box.h" +#include "G3D/Plane.h" +#include "G3D/Sphere.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + + +namespace G3D { + +const AABox& AABox::maxFinite() { + static const AABox b = AABox(Vector3::minFinite(), + Vector3::maxFinite()); + return b; +} + + +const AABox& AABox::large() { + static const AABox b = AABox(Vector3::minFinite() * 0.5f, + Vector3::maxFinite() * 0.5f); + return b; +} + + +const AABox& AABox::inf() { + static const AABox b = AABox(-Vector3::inf(), Vector3::inf()); + return b; +} + + +const AABox& AABox::zero() { + static const AABox b = AABox(Vector3::zero(), Vector3::zero()); + return b; +} + + +void AABox::serialize(class BinaryOutput& b) const { + b.writeVector3(lo); + b.writeVector3(hi); +} + + +void AABox::deserialize(class BinaryInput& b) { + lo = b.readVector3(); + hi = b.readVector3(); +} + + +void AABox::split(const Vector3::Axis& axis, float location, AABox& low, AABox& high) const { + // Low, medium, and high along the chosen axis + float L = G3D::min(location, lo[axis]); + float M = G3D::min(G3D::max(location, lo[axis]), hi[axis]); + float H = G3D::max(location, hi[axis]); + + // Copy over this box. + high = low = *this; + + // Now move the split points along the special axis + low.lo[axis] = L; + low.hi[axis] = M; + high.lo[axis] = M; + high.hi[axis] = H; +} + + +Vector3 AABox::randomSurfacePoint() const { + Vector3 extent = hi - lo; + float aXY = extent.x * extent.y; + float aYZ = extent.y * extent.z; + float aZX = extent.z * extent.x; + + float r = (float)uniformRandom(0.0f, aXY + aYZ + aZX); + + // Choose evenly between positive and negative face planes + float d = ((float)uniformRandom(0, 1) < 0.5f) ? 0.0f : 1.0f; + + // The probability of choosing a given face is proportional to + // its area. + if (r < aXY) { + return + lo + + Vector3( + (float)uniformRandom(0.0f, extent.x), + (float)uniformRandom(0.0f, extent.y), + d * extent.z); + } else if (r < aYZ) { + return + lo + + Vector3( + d * extent.x, + (float)uniformRandom(0, extent.y), + (float)uniformRandom(0, extent.z)); + } else { + return + lo + + Vector3( + (float)uniformRandom(0, extent.x), + d * extent.y, + (float)uniformRandom(0, extent.z)); + } +} + + +Vector3 AABox::randomInteriorPoint() const { + return Vector3( + (float)uniformRandom(lo.x, hi.x), + (float)uniformRandom(lo.y, hi.y), + (float)uniformRandom(lo.z, hi.z)); +} + + +bool AABox::intersects(const AABox& other) const { + // Must be overlap along all three axes. + // Try to find a separating axis. + + for (int a = 0; a < 3; ++a) { + + // |--------| + // |------| + + if ((lo[a] > other.hi[a]) || + (hi[a] < other.lo[a])) { + return false; + } + } + + return true; +} + +int AABox::dummy = 0; + +bool AABox::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + childMask = 0; + + const bool finite = + (abs(lo.x) < G3D::finf()) && + (abs(hi.x) < G3D::finf()) && + (abs(lo.y) < G3D::finf()) && + (abs(hi.y) < G3D::finf()) && + (abs(lo.z) < G3D::finf()) && + (abs(hi.z) < G3D::finf()); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + Vector3 corner; + + int numContained = 0; + int v = 0; + + // We can early-out only if we have found one point on each + // side of the plane (i.e. if we are straddling). That + // occurs when (numContained < v) && (numContained > 0) + for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) { + // Unrolling these 3 if's into a switch decreases performance + // by about 2x + corner.x = (v & 1) ? hi.x : lo.x; + corner.y = (v & 2) ? hi.y : lo.y; + corner.z = (v & 4) ? hi.z : lo.z; + + if (finite) { // this branch is highly predictable + if (plane[p].halfSpaceContainsFinite(corner)) { + ++numContained; + } + } else { + if (plane[p].halfSpaceContains(corner)) { + ++numContained; + } + } + } + + if (numContained == 0) { + // Plane p culled the box + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (numContained < v) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool AABox::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + const bool finite = + (abs(lo.x) < G3D::finf()) && + (abs(hi.x) < G3D::finf()) && + (abs(lo.y) < G3D::finf()) && + (abs(hi.y) < G3D::finf()) && + (abs(lo.z) < G3D::finf()) && + (abs(hi.z) < G3D::finf()); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culled = true; + Vector3 corner; + + int v; + + // Assume this plane culls all points. See if there is a point + // not culled by the plane... early out when at least one point + // is in the positive half space. + for (v = 0; (v < 8) && culled; ++v) { + + // Unrolling these 3 if's into a switch decreases performance + // by about 2x + corner.x = (v & 1) ? hi.x : lo.x; + corner.y = (v & 2) ? hi.y : lo.y; + corner.z = (v & 4) ? hi.z : lo.z; + + if (finite) { // this branch is highly predictable + culled = ! plane[p].halfSpaceContainsFinite(corner); + } else { + culled = ! plane[p].halfSpaceContains(corner); + } + } + + if (culled) { + // Plane p culled the box + cullingPlane = p; + + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool AABox::intersects(const class Sphere& sphere) const { + double d = 0; + + //find the square of the distance + //from the sphere to the box + for (int i = 0; i < 3; ++i) { + if (sphere.center[i] < lo[i]) { + d += square(sphere.center[i] - lo[i]); + } else if (sphere.center[i] > hi[i]) { + d += square(sphere.center[i] - hi[i]); + } + } + + return d <= square(sphere.radius); +} + +Vector3 AABox::corner(int index) const { + + // default constructor inits all components to 0 + Vector3 v; + + switch (index) + { + case 0: + v.x = lo.x; + v.y = lo.y; + v.z = hi.z; + break; + + case 1: + v.x = hi.x; + v.y = lo.y; + v.z = hi.z; + break; + + case 2: + v.x = hi.x; + v.y = hi.y; + v.z = hi.z; + break; + + case 3: + v.x = lo.x; + v.y = hi.y; + v.z = hi.z; + break; + + case 4: + v.x = lo.x; + v.y = lo.y; + v.z = lo.z; + break; + + case 5: + v.x = hi.x; + v.y = lo.y; + v.z = lo.z; + break; + + case 6: + v.x = hi.x; + v.y = hi.y; + v.z = lo.z; + break; + + case 7: + v.x = lo.x; + v.y = hi.y; + v.z = lo.z; + break; + + default: + debugAssertM(false, "Invalid corner index"); + break; + } + + return v; +} + + +} diff --git a/externals/g3dlite/Any.cpp b/externals/g3dlite/Any.cpp new file mode 100644 index 00000000000..de4d32e83ea --- /dev/null +++ b/externals/g3dlite/Any.cpp @@ -0,0 +1,1237 @@ +/** + @file Any.cpp + + @author Morgan McGuire + @author Shawn Yarbrough + + @created 2006-06-11 + @edited 2009-11-15 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/Any.h" +#include "G3D/TextOutput.h" +#include "G3D/TextInput.h" +#include "G3D/stringutils.h" +#include <deque> +#include <iostream> + +namespace G3D { + +void Any::beforeRead() const { + if (isPlaceholder()) { + // Tried to read from a placeholder--throw an exception as if + // the original operator[] had failed. + KeyNotFound e; + alwaysAssertM(m_data, "Corrupt placeholder"); + + e.filename = m_data->source.filename; + e.line = m_data->source.line; + e.character = m_data->source.character; + e.key = m_placeholderName; + e.message = + "This exception may have been thrown later than " + "the actual operator[] invocation."; + + throw e; + } +} + + +Any::Data* Any::Data::create(const Data* d) { + Data* p = create(d->type); + + p->comment = d->comment; + p->name = d->name; + + switch (d->type) { + case NONE: + case BOOLEAN: + case NUMBER: + // No clone needed + break; + + case STRING: + *(p->value.s) = *(d->value.s); + break; + + case ARRAY: + *(p->value.a) = *(d->value.a); + break; + + case TABLE: + *(p->value.t) = *(d->value.t); + // Note that placeholders may be copied; that is ok--they are still + // just placeholders. + break; + } + + return p; +} + + +Any::Data* Any::Data::create(Any::Type t) { + size_t s = sizeof(Data); + + switch (t) { + case NONE: + case BOOLEAN: + case NUMBER: + // No extra space needed + break; + + case STRING: + s += sizeof(std::string); + break; + + case ARRAY: + s += sizeof(AnyArray); + break; + + case TABLE: + s += sizeof(AnyTable); + break; + } + + // Allocate the data object + Data* p = new (MemoryManager::create()->alloc(s)) Data(t); + + // Create the (empyt) value object at the end of the Data object + switch (t) { + case NONE: + case BOOLEAN: + case NUMBER: + // No value + break; + + case STRING: + p->value.s = new (p + 1) std::string(); + break; + + case ARRAY: + p->value.a = new (p + 1) AnyArray(); + break; + + case TABLE: + p->value.t = new (p + 1) AnyTable(); + break; + } + + return p; +} + + +void Any::Data::destroy(Data* d) { + if (d != NULL) { + d->~Data(); + MemoryManager::create()->free(d); + } +} + + +Any::Data::~Data() { + debugAssertM(referenceCount.value() <= 0, "Deleted while still referenced."); + + // Destruct but do not deallocate children + switch (type) { + case STRING: + debugAssert(value.s != NULL); + value.s->~basic_string(); + break; + + case ARRAY: + debugAssert(value.a != NULL); + value.a->~Array(); + break; + + case TABLE: + debugAssert(value.t != NULL); + value.t->~Table(); + break; + + default: + // All other types should have a NULL value pointer (i.e., they were used just for name and comment fields) + debugAssertM(value.s == NULL, "Corrupt Any::Data::Value"); + } + + value.s = NULL; +} + + +////////////////////////////////////////////////////////////// + +bool Any::containsKey(const std::string& x) const { + beforeRead(); + verifyType(TABLE); + + Any* a = m_data->value.t->getPointer(x); + + // Don't return true for placeholder objects + return (a != NULL) && (! a->isPlaceholder()); +} + + +void Any::dropReference() { + if (m_data && m_data->referenceCount.decrement() <= 0) { + // This was the last reference to the shared data + Data::destroy(m_data); + } + m_data = NULL; +} + + +void Any::ensureMutable() { + if (m_data && (m_data->referenceCount.value() >= 1)) { + // Copy the data. We must do this before dropping the reference + // to avoid a race condition + Data* d = Data::create(m_data); + dropReference(); + m_data = d; + } +} + + +Any::Any() : m_type(NONE), m_data(NULL) { +} + + +Any::Any(TextInput& t) : m_type(NONE), m_data(NULL) { + deserialize(t); +} + + +Any::Any(const Any& x) : m_type(NONE), m_data(NULL) { + x.beforeRead(); + *this = x; +} + + +Any::Any(double x) : m_type(NUMBER), m_simpleValue(x), m_data(NULL) { +} + + +#ifdef G3D_32BIT +Any::Any(int64 x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) { +} +#endif // G3D_32BIT + + +Any::Any(long x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) { +} + + +Any::Any(int x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) { +} + + +Any::Any(short x) : m_type(NUMBER), m_simpleValue((double)x), m_data(NULL) { +} + + +Any::Any(bool x) : m_type(BOOLEAN), m_simpleValue(x), m_data(NULL) { +} + + +Any::Any(const std::string& s) : m_type(STRING), m_data(Data::create(STRING)) { + *(m_data->value.s) = s; +} + + +Any::Any(const char* s) : m_type(STRING), m_data(NULL) { + if (s == NULL) { + m_type = NONE; + } else { + ensureData(); + *(m_data->value.s) = s; + } +} + + +Any::Any(Type t, const std::string& name) : m_type(t), m_data(NULL) { + alwaysAssertM(t == ARRAY || t == TABLE, "Can only create ARRAY or TABLE from Type enum."); + + ensureData(); + if (name != "") { + m_data->name = name; + } +} + + +Any::~Any() { + dropReference(); +} + + +void Any::beforeWrite() { + if (isPlaceholder()) { + // This is no longer a placeholder + m_placeholderName = ""; + } +} + +Any& Any::operator=(const Any& x) { + x.beforeRead(); + + if (this == &x) { + return *this; + } + + beforeWrite(); + + dropReference(); + + m_type = x.m_type; + m_simpleValue = x.m_simpleValue; + + if (x.m_data != NULL) { + x.m_data->referenceCount.increment(); + m_data = x.m_data; + } + + return *this; +} + + +Any& Any::operator=(double x) { + *this = Any(x); + return *this; +} + + +Any& Any::operator=(int x) { + return (*this = Any(x)); +} + + +Any& Any::operator=(bool x) { + *this = Any(x); + return *this; +} + + +Any& Any::operator=(const std::string& x) { + *this = Any(x); + return *this; +} + + +Any& Any::operator=(const char* x) { + *this = Any(x); + return *this; +} + + +Any& Any::operator=(Type t) { + switch (t) { + case NONE: + *this = Any(); + break; + + case TABLE: + case ARRAY: + *this = Any(t); + break; + + default: + alwaysAssertM(false, "Can only assign NONE, TABLE, or ARRAY Type enum."); + } + + return *this; +} + + +Any::Type Any::type() const { + beforeRead(); + return m_type; +} + + +const std::string& Any::comment() const { + beforeRead(); + + static const std::string blank; + if (m_data != NULL) { + return m_data->comment; + } else { + return blank; + } +} + + +void Any::setComment(const std::string& c) { + beforeRead(); + ensureData(); + m_data->comment = c; +} + + +bool Any::isNone() const { + beforeRead(); + return (m_type == NONE); +} + + +double Any::number() const { + beforeRead(); + verifyType(NUMBER); + return m_simpleValue.n; +} + + +const std::string& Any::string() const { + beforeRead(); + verifyType(STRING); + return *(m_data->value.s); +} + + +bool Any::boolean() const { + beforeRead(); + verifyType(BOOLEAN); + return m_simpleValue.b; +} + + +const std::string& Any::name() const { + beforeRead(); + static const std::string blank; + if (m_data != NULL) { + return m_data->name; + } else { + return blank; + } +} + + +void Any::setName(const std::string& n) { + beforeRead(); + ensureData(); + m_data->name = n; +} + + +int Any::size() const { + beforeRead(); + verifyType(ARRAY, TABLE); + switch (m_type) { + case TABLE: + return m_data->value.t->size(); + + case ARRAY: + return m_data->value.a->size(); + + default:; + return 0; + } // switch (m_type) +} + + +int Any::length() const { + beforeRead(); + return size(); +} + + +void Any::resize(int n) { + beforeRead(); + alwaysAssertM(n >= 0, "Cannot resize less than 0."); + verifyType(ARRAY); + m_data->value.a->resize(n); +} + + +void Any::clear() { + beforeRead(); + verifyType(ARRAY, TABLE); + switch (m_type) { + case ARRAY: + m_data->value.a->clear(); + break; + + case TABLE: + m_data->value.t->clear(); + break; + + default:; + } +} + + +const Any& Any::operator[](int i) const { + beforeRead(); + verifyType(ARRAY); + debugAssert(m_data != NULL); + Array<Any>& array = *(m_data->value.a); + return array[i]; +} + + +Any& Any::next() { + beforeRead(); + verifyType(ARRAY); + int n = size(); + resize(n + 1); + return (*this)[n]; +} + + +Any& Any::operator[](int i) { + beforeRead(); + verifyType(ARRAY); + debugAssert(m_data != NULL); + Array<Any>& array = *(m_data->value.a); + return array[i]; +} + + +const Array<Any>& Any::array() const { + beforeRead(); + verifyType(ARRAY); + debugAssert(m_data != NULL); + return *(m_data->value.a); +} + + +void Any::append(const Any& x0) { + beforeRead(); + verifyType(ARRAY); + debugAssert(m_data != NULL); + m_data->value.a->append(x0); +} + + +void Any::append(const Any& x0, const Any& x1) { + beforeRead(); + append(x0); + append(x1); +} + + +void Any::append(const Any& x0, const Any& x1, const Any& x2) { + beforeRead(); + append(x0); + append(x1); + append(x2); +} + + +void Any::append(const Any& x0, const Any& x1, const Any& x2, const Any& x3) { + beforeRead(); + append(x0); + append(x1); + append(x2); + append(x3); +} + + +const Table<std::string, Any>& Any::table() const { + beforeRead(); + verifyType(TABLE); + debugAssert(m_data != NULL); + return *(m_data->value.t); +} + + +const Any& Any::operator[](const std::string& x) const { + beforeRead(); + verifyType(TABLE); + debugAssert(m_data != NULL); + const Table<std::string, Any>& table = *(m_data->value.t); + Any* value = table.getPointer(x); + if (value == NULL) { + KeyNotFound e; + if (m_data) { + e.filename = m_data->source.filename; + e.line = m_data->source.line; + e.character = m_data->source.character; + } + e.key = x; + throw e; + } + return *value; +} + + +Any& Any::operator[](const std::string& key) { + beforeRead(); + verifyType(TABLE); + + bool created = false; + Any& value = m_data->value.t->getCreate(key, created); + + if (created) { + // The entry was created by this method; do not allow it to be + // read before it is written. + value.m_placeholderName = key; + + // Write source data for the value + value.ensureData(); + value.m_data->source = source(); + } + + return value; +} + + +void Any::set(const std::string& k, const Any& v) { + beforeRead(); + v.beforeRead(); + verifyType(TABLE); + debugAssert(m_data != NULL); + Table<std::string, Any>& table = *(m_data->value.t); + table.set(k, v); +} + + +const Any& Any::get(const std::string& x, const Any& defaultVal) const { + beforeRead(); + defaultVal.beforeRead(); + try { + return operator[](x); + } catch(KeyNotFound) { + return defaultVal; + } +} + + +bool Any::operator==(const Any& x) const { + beforeRead(); + x.beforeRead(); + if (m_type != x.m_type) { + return false; + } + + switch (m_type) { + case NONE: + return true; + + case BOOLEAN: + return (m_simpleValue.b == x.m_simpleValue.b); + + case NUMBER: + return (m_simpleValue.n == x.m_simpleValue.n); + + case STRING: + debugAssert(m_data != NULL); + return (*(m_data->value.s) == *(x.m_data->value.s)); + + case TABLE: { + if (size() != x.size()) { + return false; + } + debugAssert(m_data != NULL); + if (m_data->name != x.m_data->name) { + return false; + } + Table<std::string, Any>& cmptable = *( m_data->value.t); + Table<std::string, Any>& xcmptable = *(x.m_data->value.t); + for (Table<std::string,Any>::Iterator it1 = cmptable.begin(), it2 = xcmptable.begin(); + it1 != cmptable.end() && it2 != xcmptable.end(); + ++it1, ++it2) { + if (*it1 != *it2) { + return false; + } + } + return true; + } + + case ARRAY: { + if (size() != x.size()) { + return false; + } + debugAssert(m_data != NULL); + if (m_data->name != x.m_data->name) { + return false; + } + + Array<Any>& cmparray = *( m_data->value.a); + Array<Any>& xcmparray = *(x.m_data->value.a); + + for (int ii = 0; ii < size(); ++ii) { + if (cmparray[ii] != xcmparray[ii]) { + return false; + } + } + return true; + } + + default: + alwaysAssertM(false, "Unknown type."); + return false; + } // switch (m_type) + +} + + +bool Any::operator!=(const Any& x) const { + beforeRead(); + x.beforeRead(); + return !operator==(x); +} + + +static void getDeserializeSettings(TextInput::Settings& settings) { + settings.cppBlockComments = true; + settings.cppLineComments = true; + settings.otherLineComments = true; + settings.otherCommentCharacter = '#'; + settings.generateCommentTokens = true; + settings.singleQuotedStrings = false; + settings.msvcSpecials = false; + settings.caseSensitive = false; +} + + +std::string Any::unparse() const { + beforeRead(); + TextOutput::Settings settings; + TextOutput to(settings); + serialize(to); + return to.commitString(); +} + + +void Any::parse(const std::string& src) { + beforeRead(); + TextInput::Settings settings; + getDeserializeSettings(settings); + + TextInput ti(TextInput::FROM_STRING, src, settings); + deserialize(ti); +} + + +void Any::load(const std::string& filename) { + beforeRead(); + TextInput::Settings settings; + getDeserializeSettings(settings); + + TextInput ti(filename, settings); + deserialize(ti); +} + + +void Any::save(const std::string& filename) const { + beforeRead(); + TextOutput::Settings settings; + settings.wordWrap = TextOutput::Settings::WRAP_NONE; + + TextOutput to(filename,settings); + serialize(to); + to.commit(); +} + + +static bool needsQuotes(const std::string& s) { + if (! isLetter(s[0]) && (s[0] != '_')) { + return true; + } + + for (int i = 0; i < (int)s.length(); ++i) { + char c = s[i]; + + // peek character + char p = (i == (int)s.length() - 1) ? '_' : s[i + 1]; + + // Identify separators + if ((c == '-' && p == '>') || + (c == ':' && p == ':')) { + // Skip over this symbol + ++i; + continue; + } + + if (! isDigit(c) && ! isLetter(c) & (c != '.')) { + // This is an illegal character for an identifier, so we need quotes + return true; + } + } + + return false; +} + + +// TODO: if the output will fit on one line, compress tables and arrays into a single line +void Any::serialize(TextOutput& to) const { + beforeRead(); + if (m_data && ! m_data->comment.empty()) { + to.printf("\n/* %s */\n", m_data->comment.c_str()); + } + + switch (m_type) { + case NONE: + to.writeSymbol("NONE"); + break; + + case BOOLEAN: + to.writeBoolean(m_simpleValue.b); + break; + + case NUMBER: + to.writeNumber(m_simpleValue.n); + break; + + case STRING: + debugAssert(m_data != NULL); + to.writeString(*(m_data->value.s)); + break; + + case TABLE: { + debugAssert(m_data != NULL); + if (! m_data->name.empty()) { + if (needsQuotes(m_data->name)) { + to.writeString(m_data->name); + } else { + to.writeSymbol(m_data->name); + } + } + to.writeSymbol("{"); + to.writeNewline(); + to.pushIndent(); + AnyTable& table = *(m_data->value.t); + Array<std::string> keys; + table.getKeys(keys); + keys.sort(); + + for (int i = 0; i < keys.size(); ++i) { + + to.writeSymbol(keys[i]); + to.writeSymbol("="); + table[keys[i]].serialize(to); + + if (i < keys.size() - 1) { + to.writeSymbol(","); + } + to.writeNewline(); + + // Skip a line between table entries + to.writeNewline(); + } + + to.popIndent(); + to.writeSymbol("}"); + break; + } + + case ARRAY: { + debugAssert(m_data != NULL); + if (! m_data->name.empty()) { + // For arrays, leave no trailing space between the name and the paren + to.writeSymbol(format("%s(", m_data->name.c_str())); + } else { + to.writeSymbol("("); + } + to.writeNewline(); + to.pushIndent(); + Array<Any>& array = *(m_data->value.a); + for (int ii = 0; ii < size(); ++ii) { + array[ii].serialize(to); + if (ii < size() - 1) { + to.writeSymbol(","); + to.writeNewline(); + } + + // Put the close paren on an array right behind the last element + } + to.popIndent(); + to.writeSymbol(")"); + break; + } + } +} + + +void Any::deserializeComment(TextInput& ti, Token& token, std::string& comment) { + // Parse comments + while (token.type() == Token::COMMENT) { + comment += trimWhitespace(token.string()) + "\n"; + + // Allow comments to contain newlines. + do { + token = ti.read(); + comment += "\n"; + } while (token.type() == Token::NEWLINE); + } + + comment = trimWhitespace(comment); +} + +/** True if \a c is an open paren of some form */ +static bool isOpen(const char c) { + return c == '(' || c == '[' || c == '{'; +} + + +/** True if \a c is an open paren of some form */ +static bool isClose(const char c) { + return c == ')' || c == ']' || c == '}'; +} + + +/** True if \a s is a C++ name operator */ +static bool isNameOperator(const std::string& s) { + return s == "." || s == "::" || s == "->"; +} + + +void Any::deserializeName(TextInput& ti, Token& token, std::string& name) { + debugAssert(token.type() == Token::SYMBOL); + std::string s = token.string(); + while (! isOpen(s[0])) { + name += s; + + // Skip newlines and comments + token = ti.readSignificant(); + + if (token.type() != Token::SYMBOL) { + throw ParseError(ti.filename(), token.line(), token.character(), + "Expected symbol while parsing Any"); + } + s = token.string(); + } +} + + +void Any::deserialize(TextInput& ti) { + beforeRead(); + Token token = ti.read(); + deserialize(ti, token); + // Restore the last token + ti.push(token); +} + + +void Any::deserialize(TextInput& ti, Token& token) { + // Deallocate old data + dropReference(); + m_type = NONE; + m_simpleValue.b = false; + + // Skip leading newlines + while (token.type() == Token::NEWLINE) { + token = ti.read(); + } + + std::string comment; + if (token.type() == Token::COMMENT) { + deserializeComment(ti, token, comment); + } + + if (token.type() == Token::END) { + // There should never be a comment without an Any following it; even + // if the file ends with some commented out stuff, + // that should not happen after a comma, so we'd never read that + // far in a proper file. + throw ParseError(ti.filename(), token.line(), token.character(), + "File ended without a properly formed Any"); + } + + switch (token.type()) { + case Token::STRING: + m_type = STRING; + ensureData(); + *(m_data->value.s) = token.string(); + m_data->source.set(ti, token); + break; + + case Token::NUMBER: + m_type = NUMBER; + m_simpleValue.n = token.number(); + ensureData(); + m_data->source.set(ti, token); + break; + + case Token::BOOLEAN: + m_type = BOOLEAN; + m_simpleValue.b = token.boolean(); + ensureData(); + m_data->source.set(ti, token); + break; + + case Token::SYMBOL: + // Named Array, Named Table, Array, Table, or NONE + if (toUpper(token.string()) == "NONE") { + // Nothing left to do; we initialized to NONE originally + ensureData(); + m_data->source.set(ti, token); + } else { + // Array or Table + + // Parse the name + + // s must have at least one element or this would not have + // been parsed as a symbol + std::string name; + deserializeName(ti, token, name); + if (token.type() != Token::SYMBOL) { + throw ParseError(ti.filename(), token.line(), token.character(), + "Malformed Any TABLE or ARRAY; must start with [, (, or {"); + } + + if (isOpen(token.string()[0])) { + // Array or table + deserializeBody(ti, token); + } else { + throw ParseError(ti.filename(), token.line(), token.character(), + "Malformed Any TABLE or ARRAY; must start with [, (, or {"); + } + + if (! name.empty()) { + ensureData(); + m_data->name = name; + } + } // if NONE + break; + + default: + throw ParseError(ti.filename(), token.line(), token.character(), + "Unexpected token"); + + } // switch + + if (! comment.empty()) { + ensureData(); + m_data->comment = comment; + } + + if (m_type != ARRAY && m_type != TABLE) { + // Array and table already consumed their last token + token = ti.read(); + } +} + + +void Any::ensureData() { + if (m_data == NULL) { + m_data = Data::create(m_type); + } +} + + +static bool isSeparator(char c) { + return c == ',' || c == ';'; +} + + +void Any::readUntilCommaOrClose(TextInput& ti, Token& token) { + while (! ((token.type() == Token::SYMBOL) && + (isClose(token.string()[0])) || + isSeparator(token.string()[0]))) { + switch (token.type()) { + case Token::NEWLINE: + case Token::COMMENT: + // Consume + token = ti.read(); + break; + + default: + throw ParseError(ti.filename(), token.line(), token.character(), + "Expected a comma or close paren"); + } + } +} + + +void Any::deserializeBody(TextInput& ti, Token& token) { + char closeSymbol = '}'; + m_type = TABLE; + + const char c = token.string()[0]; + + if (c != '{') { + m_type = ARRAY; + // Chose the appropriate close symbol + closeSymbol = (c == '(') ? ')' : ']'; + } + + // Allocate the underlying data structure + ensureData(); + m_data->source.set(ti, token); + + // Consume the open token + token = ti.read(); + + while (! ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol))) { + + // Read any leading comment. This must be done here (and not in the recursive deserialize + // call) in case the body contains only a comment. + std::string comment; + deserializeComment(ti, token, comment); + + if ((token.type() == Token::SYMBOL) && (token.string()[0] == closeSymbol)) { + // We're done; this catches the case where the array is empty + break; + } + + // Pointer the value being read + Any a = NULL; + std::string key; + + if (m_type == TABLE) { + // Read the key + if (token.type() != Token::SYMBOL && token.type() != Token::STRING) { + throw ParseError(ti.filename(), token.line(), token.character(), "Expected a name"); + } + + key = token.string(); + // Consume everything up to the = sign + token = ti.readSignificant(); + + if ((token.type() != Token::SYMBOL) || (token.string() != "=")) { + throw ParseError(ti.filename(), token.line(), token.character(), "Expected ="); + } else { + // Consume (don't consume comments--we want the value pointed to by a to get those). + token = ti.read(); + } + } + a.deserialize(ti, token); + + if (! comment.empty()) { + // Prepend the comment we read earlier + a.ensureData(); + a.m_data->comment = trimWhitespace(comment + "\n" + a.m_data->comment); + } + + if (m_type == TABLE) { + set(key, a); + } else { + append(a); + } + + // Read until the comma or close paren, discarding trailing comments and newlines + readUntilCommaOrClose(ti, token); + + // Consume the comma + if (isSeparator(token.string()[0])) { + token = ti.read(); + } + } + + // Consume the close paren (to match other deserialize methods) + token = ti.read(); +} + + +Any::operator int() const { + beforeRead(); + return iRound(number()); +} + + +Any::operator float() const { + beforeRead(); + return float(number()); +} + + +Any::operator double() const { + beforeRead(); + return number(); +} + + +Any::operator bool() const { + beforeRead(); + return boolean(); +} + + +Any::operator std::string() const { + beforeRead(); + return string(); +} + + +const Any::Source& Any::source() const { + static Source s; + if (m_data) { + return m_data->source; + } else { + return s; + } +} + + +void Any::verify(bool value, const std::string& message) const { + beforeRead(); + if (! value) { + ParseError p; + if (m_data) { + p.filename = m_data->source.filename; + p.line = m_data->source.line; + p.character = m_data->source.character; + } + + if (name().empty()) { + p.message = "Parse error"; + } else { + p.message = "Parse error while reading the contents of " + name(); + } + + if (! message.empty()) { + p.message = p.message + ": " + message; + } + + throw p; + } +} + + +void Any::verifyName(const std::string& n) const { + beforeRead(); + verify(beginsWith(toUpper(name()), toUpper(n)), "Name must begin with " + n); +} + + +void Any::verifyType(Type t) const { + beforeRead(); + if (type() != t) { + verify(false, "Must have type " + toString(t)); + } +} + + +void Any::verifyType(Type t0, Type t1) const { + beforeRead(); + if (type() != t0 && type() != t1) { + verify(false, "Must have type " + toString(t0) + " or " + toString(t1)); + } +} + + +void Any::verifySize(int low, int high) const { + beforeRead(); + verifyType(ARRAY, TABLE); + if (size() < low || size() > high) { + verify(false, format("Size must be between %d and %d", low, high)); + } +} + + +void Any::verifySize(int s) const { + beforeRead(); + verifyType(ARRAY, TABLE); + if (size() != s) { + verify(false, format("Size must be %d", s)); + } +} + + +std::string Any::toString(Type t) { + switch(t) { + case NONE: return "NONE"; + case BOOLEAN: return "BOOLEAN"; + case NUMBER: return "NUMBER"; + case STRING: return "STRING"; + case ARRAY: return "ARRAY"; + case TABLE: return "TABLE"; + default: + alwaysAssertM(false, "Illegal Any::Type"); + return ""; + } +} + +} // namespace G3D + diff --git a/externals/g3dlite/BinaryFormat.cpp b/externals/g3dlite/BinaryFormat.cpp new file mode 100644 index 00000000000..d3991378f45 --- /dev/null +++ b/externals/g3dlite/BinaryFormat.cpp @@ -0,0 +1,81 @@ +/** + @file BinaryFormat.cpp + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2005-06-10 + @edited 2005-06-10 + */ + +#include "G3D/BinaryFormat.h" + +namespace G3D { + +int32 byteSize(BinaryFormat f) { + switch (f) { + case BOOL8_BINFMT: + case UINT8_BINFMT: + case INT8_BINFMT: + return 1; + + case UINT16_BINFMT: + case INT16_BINFMT: + return 2; + + case FLOAT16_BINFMT: + return 2; + + case UINT32_BINFMT: + case INT32_BINFMT: + case FLOAT32_BINFMT: + return 4; + + case FLOAT64_BINFMT: + case UINT64_BINFMT: + case INT64_BINFMT: + return 8; + + case INT128_BINFMT: + case UINT128_BINFMT: + return 16; + + case VECTOR2_BINFMT: + return 2 * 4; + + case VECTOR2INT16_BINFMT: + return 2 * 2; + + case VECTOR3_BINFMT: + return 3 * 4; + + case VECTOR3INT16_BINFMT: + return 3 * 2; + + case VECTOR4_BINFMT: + return 4 * 4; + + case VECTOR4INT16_BINFMT: + return 4 * 4; + + case COLOR3_BINFMT: + return 3 * 4; + + case COLOR3UINT8_BINFMT: + return 3 * 1; + + case COLOR3INT16_BINFMT: + return 3 * 2; + + case COLOR4_BINFMT: + return 4 * 4; + + case COLOR4UINT8_BINFMT: + return 4 * 1; + + case COLOR4INT16_BINFMT: + return 4 * 2; + + default: + return -1; + } +} +} diff --git a/externals/g3dlite/BinaryInput.cpp b/externals/g3dlite/BinaryInput.cpp new file mode 100644 index 00000000000..65a9976fe04 --- /dev/null +++ b/externals/g3dlite/BinaryInput.cpp @@ -0,0 +1,568 @@ +/** + @file BinaryInput.cpp + + @author Morgan McGuire, graphics3d.com + Copyright 2001-2007, Morgan McGuire. All rights reserved. + + @created 2001-08-09 + @edited 2005-02-24 + + + <PRE> + { + BinaryOutput b("c:/tmp/test.b", BinaryOutput::LITTLE_ENDIAN); + + float f = 3.1415926; + int i = 1027221; + std::string s = "Hello World!"; + + b.writeFloat32(f); + b.writeInt32(i); + b.writeString(s); + b.commit(); + + + BinaryInput in("c:/tmp/test.b", BinaryInput::LITTLE_ENDIAN); + + debugAssert(f == in.readFloat32()); + int ii = in.readInt32(); + debugAssert(i == ii); + debugAssert(s == in.readString()); + } + </PRE> + */ + +#include "G3D/platform.h" +#include "G3D/BinaryInput.h" +#include "G3D/Array.h" +#include "G3D/fileutils.h" +#include "G3D/Log.h" +#include <zlib.h> + +#include <cstring> + +namespace G3D { + +void BinaryInput::readBool8(std::vector<bool>& out, int64 n) { + out.resize((int)n); + // std::vector optimizes bool in a way that prevents fast reading + for (int64 i = 0; i < n ; ++i) { + out[i] = readBool8(); + } +} + + +void BinaryInput::readBool8(Array<bool>& out, int64 n) { + out.resize(n); + readBool8(out.begin(), n); +} + + +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(std::vector<lcase>& out, int64 n) {\ + out.resize(n);\ + read##ucase(&out[0], n);\ +}\ +\ +\ +void BinaryInput::read##ucase(Array<lcase>& out, int64 n) {\ + out.resize(n);\ + read##ucase(out.begin(), n);\ +} + + +IMPLEMENT_READER(UInt8, uint8) +IMPLEMENT_READER(Int8, int8) +IMPLEMENT_READER(UInt16, uint16) +IMPLEMENT_READER(Int16, int16) +IMPLEMENT_READER(UInt32, uint32) +IMPLEMENT_READER(Int32, int32) +IMPLEMENT_READER(UInt64, uint64) +IMPLEMENT_READER(Int64, int64) +IMPLEMENT_READER(Float32, float32) +IMPLEMENT_READER(Float64, float64) + +#undef IMPLEMENT_READER + +// Data structures that are one byte per element can be +// directly copied, regardles of endian-ness. +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(lcase* out, int64 n) {\ + if (sizeof(lcase) == 1) {\ + readBytes(out, n);\ + } else {\ + for (int64 i = 0; i < n ; ++i) {\ + out[i] = read##ucase();\ + }\ + }\ +} + +IMPLEMENT_READER(Bool8, bool) +IMPLEMENT_READER(UInt8, uint8) +IMPLEMENT_READER(Int8, int8) + +#undef IMPLEMENT_READER + + +#define IMPLEMENT_READER(ucase, lcase)\ +void BinaryInput::read##ucase(lcase* out, int64 n) {\ + if (m_swapBytes) {\ + for (int64 i = 0; i < n; ++i) {\ + out[i] = read##ucase();\ + }\ + } else {\ + readBytes(out, sizeof(lcase) * n);\ + }\ +} + + +IMPLEMENT_READER(UInt16, uint16) +IMPLEMENT_READER(Int16, int16) +IMPLEMENT_READER(UInt32, uint32) +IMPLEMENT_READER(Int32, int32) +IMPLEMENT_READER(UInt64, uint64) +IMPLEMENT_READER(Int64, int64) +IMPLEMENT_READER(Float32, float32) +IMPLEMENT_READER(Float64, float64) + +#undef IMPLEMENT_READER + +void BinaryInput::loadIntoMemory(int64 startPosition, int64 minLength) { + // Load the next section of the file + debugAssertM(m_filename != "<memory>", "Read past end of file."); + + int64 absPos = m_alreadyRead + m_pos; + + if (m_bufferLength < minLength) { + // The current buffer isn't big enough to hold the chunk we want to read. + // This happens if there was little memory available during the initial constructor + // read but more memory has since been freed. + m_bufferLength = minLength; + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::realloc(m_buffer, m_bufferLength); + if (m_buffer == NULL) { + throw "Tried to read a larger memory chunk than could fit in memory. (2)"; + } + } + + m_alreadyRead = startPosition; + +# ifdef G3D_WIN32 + FILE* file = fopen(m_filename.c_str(), "rb"); + debugAssert(file); + int ret = fseek(file, (off_t)m_alreadyRead, SEEK_SET); + debugAssert(ret == 0); + size_t toRead = (size_t)G3D::min(m_bufferLength, m_length - m_alreadyRead); + ret = fread(m_buffer, 1, toRead, file); + debugAssert(ret == toRead); + fclose(file); + file = NULL; + +# else + FILE* file = fopen(m_filename.c_str(), "rb"); + debugAssert(file); + int ret = fseeko(file, (off_t)m_alreadyRead, SEEK_SET); + debugAssert(ret == 0); + size_t toRead = (size_t)G3D::min<int64>(m_bufferLength, m_length - m_alreadyRead); + ret = fread(m_buffer, 1, toRead, file); + debugAssert((size_t)ret == (size_t)toRead); + fclose(file); + file = NULL; +# endif + + m_pos = absPos - m_alreadyRead; + debugAssert(m_pos >= 0); +} + + + +const bool BinaryInput::NO_COPY = false; + +static bool needSwapBytes(G3DEndian fileEndian) { + return (fileEndian != System::machineEndian()); +} + + +/** Helper used by the constructors for decompression */ +static uint32 readUInt32(const uint8* data, bool swapBytes) { + if (swapBytes) { + uint8 out[4]; + out[0] = data[3]; + out[1] = data[2]; + out[2] = data[1]; + out[3] = data[0]; + return *((uint32*)out); + } else { + return *((uint32*)data); + } +} + + +void BinaryInput::setEndian(G3DEndian e) { + m_fileEndian = e; + m_swapBytes = needSwapBytes(m_fileEndian); +} + + +BinaryInput::BinaryInput( + const uint8* data, + int64 dataLen, + G3DEndian dataEndian, + bool compressed, + bool copyMemory) : + m_filename("<memory>"), + m_bitPos(0), + m_bitString(0), + m_beginEndBits(0), + m_alreadyRead(0), + m_bufferLength(0), + m_pos(0) { + + m_freeBuffer = copyMemory || compressed; + + setEndian(dataEndian); + + if (compressed) { + // Read the decompressed size from the first 4 bytes + m_length = G3D::readUInt32(data, m_swapBytes); + + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + + unsigned long L = m_length; + // Decompress with zlib + int64 result = uncompress(m_buffer, (unsigned long*)&L, data + 4, dataLen - 4); + m_length = L; + m_bufferLength = L; + debugAssert(result == Z_OK); (void)result; + + } else { + m_length = dataLen; + m_bufferLength = m_length; + if (! copyMemory) { + debugAssert(!m_freeBuffer); + m_buffer = const_cast<uint8*>(data); + } else { + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + System::memcpy(m_buffer, data, dataLen); + } + } +} + + +BinaryInput::BinaryInput( + const std::string& filename, + G3DEndian fileEndian, + bool compressed) : + m_filename(filename), + m_bitPos(0), + m_bitString(0), + m_beginEndBits(0), + m_alreadyRead(0), + m_length(0), + m_bufferLength(0), + m_buffer(NULL), + m_pos(0), + m_freeBuffer(true) { + + setEndian(fileEndian); + + // Update global file tracker + _internal::currentFilesUsed.insert(m_filename); + + + if (! fileExists(m_filename, false)) { + std::string zipfile; + std::string internalfile; + if (zipfileExists(m_filename, zipfile, internalfile)) { + // Load from zipfile + void* v; + size_t s; + zipRead(filename, v, s); + m_buffer = reinterpret_cast<uint8*>(v); + m_bufferLength = m_length = s; + if (compressed) { + decompress(); + } + m_freeBuffer = true; + } else { + Log::common()->printf("Warning: File not found: %s\n", m_filename.c_str()); + } + return; + } + + // Figure out how big the file is and verify that it exists. + m_length = fileLength(m_filename); + + // Read the file into memory + FILE* file = fopen(m_filename.c_str(), "rb"); + + if (! file || (m_length == -1)) { + throw format("File not found: \"%s\"", m_filename.c_str()); + return; + } + + if (! compressed && (m_length > INITIAL_BUFFER_LENGTH)) { + // Read only a subset of the file so we don't consume + // all available memory. + m_bufferLength = INITIAL_BUFFER_LENGTH; + } else { + // Either the length is fine or the file is compressed + // and requires us to read the whole thing for zlib. + m_bufferLength = m_length; + } + + debugAssert(m_freeBuffer); + m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); + if (m_buffer == NULL) { + if (compressed) { + throw "Not enough memory to load compressed file. (1)"; + } + + // Try to allocate a small array; not much memory is available. + // Give up if we can't allocate even 1k. + while ((m_buffer == NULL) && (m_bufferLength > 1024)) { + m_bufferLength /= 2; + m_buffer = (uint8*)System::alignedMalloc(m_bufferLength, 16); + } + } + debugAssert(m_buffer); + + fread(m_buffer, m_bufferLength, sizeof(int8), file); + fclose(file); + file = NULL; + + if (compressed) { + if (m_bufferLength != m_length) { + throw "Not enough memory to load compressed file. (2)"; + } + + decompress(); + } +} + +void BinaryInput::decompress() { + // Decompress + // Use the existing buffer as the source, allocate + // a new buffer to use as the destination. + + int64 tempLength = m_length; + m_length = G3D::readUInt32(m_buffer, m_swapBytes); + + // The file couldn't have better than 500:1 compression + alwaysAssertM(m_length < m_bufferLength * 500, "Compressed file header is corrupted"); + + uint8* tempBuffer = m_buffer; + m_buffer = (uint8*)System::alignedMalloc(m_length, 16); + + debugAssert(m_buffer); + debugAssert(isValidHeapPointer(tempBuffer)); + debugAssert(isValidHeapPointer(m_buffer)); + + unsigned long L = m_length; + int64 result = uncompress(m_buffer, &L, tempBuffer + 4, tempLength - 4); + m_length = L; + m_bufferLength = m_length; + + debugAssertM(result == Z_OK, "BinaryInput/zlib detected corruption in " + m_filename); + (void)result; + + System::alignedFree(tempBuffer); +} + + +void BinaryInput::readBytes(void* bytes, int64 n) { + prepareToRead(n); + debugAssert(isValidPointer(bytes)); + + memcpy(bytes, m_buffer + m_pos, n); + m_pos += n; +} + + +BinaryInput::~BinaryInput() { + + if (m_freeBuffer) { + System::alignedFree(m_buffer); + } + m_buffer = NULL; +} + + +uint64 BinaryInput::readUInt64() { + prepareToRead(8); + uint8 out[8]; + + if (m_swapBytes) { + out[0] = m_buffer[m_pos + 7]; + out[1] = m_buffer[m_pos + 6]; + out[2] = m_buffer[m_pos + 5]; + out[3] = m_buffer[m_pos + 4]; + out[4] = m_buffer[m_pos + 3]; + out[5] = m_buffer[m_pos + 2]; + out[6] = m_buffer[m_pos + 1]; + out[7] = m_buffer[m_pos + 0]; + } else { + *(uint64*)out = *(uint64*)(m_buffer + m_pos); + } + + m_pos += 8; + return *(uint64*)out; +} + + +std::string BinaryInput::readString(int64 n) { + prepareToRead(n); + debugAssertM((m_pos + n) <= m_length, "Read past end of file"); + + char *s = (char*)System::alignedMalloc(n + 1, 16); + assert(s != NULL); + + memcpy(s, m_buffer + m_pos, n); + // There may not be a null, so make sure + // we add one. + s[n] = '\0'; + + std::string out = s; + System::alignedFree(s); + s = NULL; + + m_pos += n; + + return out; + +} + + +std::string BinaryInput::readString() { + int64 n = 0; + + if ((m_pos + m_alreadyRead + n) < (m_length - 1)) { + prepareToRead(1); + } + + if ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) && + (m_buffer[m_pos + n] != '\0')) { + + ++n; + while ( ((m_pos + m_alreadyRead + n) < (m_length - 1)) && + (m_buffer[m_pos + n] != '\0')) { + + prepareToRead(1); + ++n; + } + } + + // Consume NULL + ++n; + + return readString(n); +} + + +std::string BinaryInput::readStringEven() { + std::string x = readString(); + if (hasMore() && (G3D::isOdd(x.length() + 1))) { + skip(1); + } + return x; +} + + +std::string BinaryInput::readString32() { + int len = readUInt32(); + return readString(len); +} + + +Vector4 BinaryInput::readVector4() { + float x = readFloat32(); + float y = readFloat32(); + float z = readFloat32(); + float w = readFloat32(); + return Vector4(x, y, z, w); +} + + +Vector3 BinaryInput::readVector3() { + float x = readFloat32(); + float y = readFloat32(); + float z = readFloat32(); + return Vector3(x, y, z); +} + + +Vector2 BinaryInput::readVector2() { + float x = readFloat32(); + float y = readFloat32(); + return Vector2(x, y); +} + + +Color4 BinaryInput::readColor4() { + float r = readFloat32(); + float g = readFloat32(); + float b = readFloat32(); + float a = readFloat32(); + return Color4(r, g, b, a); +} + + +Color3 BinaryInput::readColor3() { + float r = readFloat32(); + float g = readFloat32(); + float b = readFloat32(); + return Color3(r, g, b); +} + + +void BinaryInput::beginBits() { + debugAssert(m_beginEndBits == 0); + m_beginEndBits = 1; + m_bitPos = 0; + + debugAssertM(hasMore(), "Can't call beginBits when at the end of a file"); + m_bitString = readUInt8(); +} + + +uint32 BinaryInput::readBits(int numBits) { + debugAssert(m_beginEndBits == 1); + + uint32 out = 0; + + const int total = numBits; + while (numBits > 0) { + if (m_bitPos > 7) { + // Consume a new byte for reading. We do this at the beginning + // of the loop so that we don't try to read past the end of the file. + m_bitPos = 0; + m_bitString = readUInt8(); + } + + // Slide the lowest bit of the bitString into + // the correct position. + out |= (m_bitString & 1) << (total - numBits); + + // Shift over to the next bit + m_bitString = m_bitString >> 1; + ++m_bitPos; + --numBits; + } + + return out; +} + + +void BinaryInput::endBits() { + debugAssert(m_beginEndBits == 1); + if (m_bitPos == 0) { + // Put back the last byte we read + --m_pos; + } + m_beginEndBits = 0; + m_bitPos = 0; +} + +} diff --git a/externals/g3dlite/BinaryOutput.cpp b/externals/g3dlite/BinaryOutput.cpp new file mode 100644 index 00000000000..2de46c6d4bb --- /dev/null +++ b/externals/g3dlite/BinaryOutput.cpp @@ -0,0 +1,522 @@ +/** + @file BinaryOutput.cpp + + @author Morgan McGuire, graphics3d.com + Copyright 2002-2007, Morgan McGuire, All rights reserved. + + @created 2002-02-20 + @edited 2008-01-07 + */ + +#include "G3D/platform.h" +#include "G3D/BinaryOutput.h" +#include "G3D/fileutils.h" +#include "G3D/stringutils.h" +#include "G3D/Array.h" +#include <zlib.h> + +#include <cstring> + +// Largest memory buffer that the system will use for writing to +// disk. After this (or if the system runs out of memory) +// chunks of the file will be dumped to disk. +// +// Currently 400 MB +#define MAX_BINARYOUTPUT_BUFFER_SIZE 400000000 + +namespace G3D { + +void BinaryOutput::writeBool8(const std::vector<bool>& out, int n) { + for (int i = 0; i < n; ++i) { + writeBool8(out[i]); + } +} + + +void BinaryOutput::writeBool8(const Array<bool>& out, int n) { + writeBool8(out.getCArray(), n); +} + +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const std::vector<lcase>& out, int n) {\ + write##ucase(&out[0], n);\ +}\ +\ +\ +void BinaryOutput::write##ucase(const Array<lcase>& out, int n) {\ + write##ucase(out.getCArray(), n);\ +} + + +IMPLEMENT_WRITER(UInt8, uint8) +IMPLEMENT_WRITER(Int8, int8) +IMPLEMENT_WRITER(UInt16, uint16) +IMPLEMENT_WRITER(Int16, int16) +IMPLEMENT_WRITER(UInt32, uint32) +IMPLEMENT_WRITER(Int32, int32) +IMPLEMENT_WRITER(UInt64, uint64) +IMPLEMENT_WRITER(Int64, int64) +IMPLEMENT_WRITER(Float32, float32) +IMPLEMENT_WRITER(Float64, float64) + +#undef IMPLEMENT_WRITER + +// Data structures that are one byte per element can be +// directly copied, regardles of endian-ness. +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const lcase* out, int n) {\ + if (sizeof(lcase) == 1) {\ + writeBytes((void*)out, n);\ + } else {\ + for (int i = 0; i < n ; ++i) {\ + write##ucase(out[i]);\ + }\ + }\ +} + +IMPLEMENT_WRITER(Bool8, bool) +IMPLEMENT_WRITER(UInt8, uint8) +IMPLEMENT_WRITER(Int8, int8) + +#undef IMPLEMENT_WRITER + + +#define IMPLEMENT_WRITER(ucase, lcase)\ +void BinaryOutput::write##ucase(const lcase* out, int n) {\ + if (m_swapBytes) {\ + for (int i = 0; i < n; ++i) {\ + write##ucase(out[i]);\ + }\ + } else {\ + writeBytes((const void*)out, sizeof(lcase) * n);\ + }\ +} + + +IMPLEMENT_WRITER(UInt16, uint16) +IMPLEMENT_WRITER(Int16, int16) +IMPLEMENT_WRITER(UInt32, uint32) +IMPLEMENT_WRITER(Int32, int32) +IMPLEMENT_WRITER(UInt64, uint64) +IMPLEMENT_WRITER(Int64, int64) +IMPLEMENT_WRITER(Float32, float32) +IMPLEMENT_WRITER(Float64, float64) + +#undef IMPLEMENT_WRITER + + +void BinaryOutput::reallocBuffer(size_t bytes, size_t oldBufferLen) { + //debugPrintf("reallocBuffer(%d, %d)\n", bytes, oldBufferLen); + + size_t newBufferLen = (int)(m_bufferLen * 1.5) + 100; + uint8* newBuffer = NULL; + + if ((m_filename == "<memory>") || (newBufferLen < MAX_BINARYOUTPUT_BUFFER_SIZE)) { + // We're either writing to memory (in which case we *have* to try and allocate) + // or we've been asked to allocate a reasonable size buffer. + + //debugPrintf(" realloc(%d)\n", newBufferLen); + newBuffer = (uint8*)System::realloc(m_buffer, newBufferLen); + if (newBuffer != NULL) { + m_maxBufferLen = newBufferLen; + } + } + + if ((newBuffer == NULL) && (bytes > 0)) { + // Realloc failed; we're probably out of memory. Back out + // the entire call and try to dump some data to disk. + m_bufferLen = oldBufferLen; + reserveBytesWhenOutOfMemory(bytes); + } else { + m_buffer = newBuffer; + debugAssert(isValidHeapPointer(m_buffer)); + } +} + + +void BinaryOutput::reserveBytesWhenOutOfMemory(size_t bytes) { + if (m_filename == "<memory>") { + throw "Out of memory while writing to memory in BinaryOutput (no RAM left)."; + } else if ((int)bytes > (int)m_maxBufferLen) { + throw "Out of memory while writing to disk in BinaryOutput (could not create a large enough buffer)."; + } else { + + // Dump the contents to disk. In order to enable seeking backwards, + // we keep the last 10 MB in memory. + int writeBytes = m_bufferLen - 10 * 1024 * 1024; + + if (writeBytes < m_bufferLen / 3) { + // We're going to write less than 1/3 of the file; + // give up and just write the whole thing. + writeBytes = m_bufferLen; + } + debugAssert(writeBytes > 0); + + //debugPrintf("Writing %d bytes to disk\n", writeBytes); + + const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb"; + FILE* file = fopen(m_filename.c_str(), mode); + debugAssert(file); + + size_t count = fwrite(m_buffer, 1, writeBytes, file); + debugAssert((int)count == writeBytes); (void)count; + + fclose(file); + file = NULL; + + // Record that we saved this data. + m_alreadyWritten += writeBytes; + m_bufferLen -= writeBytes; + m_pos -= writeBytes; + + debugAssert(m_bufferLen < m_maxBufferLen); + debugAssert(m_bufferLen >= 0); + debugAssert(m_pos >= 0); + debugAssert(m_pos <= m_bufferLen); + + // Shift the unwritten data back appropriately in the buffer. + debugAssert(isValidHeapPointer(m_buffer)); + System::memcpy(m_buffer, m_buffer + writeBytes, m_bufferLen); + debugAssert(isValidHeapPointer(m_buffer)); + + // *now* we allocate bytes (there should presumably be enough + // space in the buffer; if not, we'll come back through this + // code and dump the last 10MB to disk as well. Note that the + // bytes > maxBufferLen case above would already have triggered + // if this call couldn't succeed. + reserveBytes(bytes); + } +} + + +BinaryOutput::BinaryOutput() { + m_alreadyWritten = 0; + m_swapBytes = false; + m_pos = 0; + m_filename = "<memory>"; + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_ok = true; + m_committed = false; +} + + +BinaryOutput::BinaryOutput( + const std::string& filename, + G3DEndian fileEndian) { + + m_pos = 0; + m_alreadyWritten = 0; + setEndian(fileEndian); + m_filename = filename; + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_committed = false; + + m_ok = true; + /** Verify ability to write to disk */ + commit(false); + m_committed = false; +} + + +void BinaryOutput::reset() { + debugAssert(m_beginEndBits == 0); + alwaysAssertM(m_filename == "<memory>", + "Can only reset a BinaryOutput that writes to memory."); + + // Do not reallocate, just clear the size of the buffer. + m_pos = 0; + m_alreadyWritten = 0; + m_bufferLen = 0; + m_beginEndBits = 0; + m_bitString = 0; + m_bitPos = 0; + m_committed = false; +} + + +BinaryOutput::~BinaryOutput() { + debugAssert((m_buffer == NULL) || isValidHeapPointer(m_buffer)); + System::free(m_buffer); + m_buffer = NULL; + m_bufferLen = 0; + m_maxBufferLen = 0; +} + + +void BinaryOutput::setEndian(G3DEndian fileEndian) { + m_fileEndian = fileEndian; + m_swapBytes = (fileEndian != System::machineEndian()); +} + + +bool BinaryOutput::ok() const { + return m_ok; +} + + +void BinaryOutput::compress() { + if (m_alreadyWritten > 0) { + throw "Cannot compress huge files (part of this file has already been written to disk)."; + } + + // Old buffer size + int L = m_bufferLen; + uint8* convert = (uint8*)&L; + + // Zlib requires the output buffer to be this big + unsigned long newSize = iCeil(m_bufferLen * 1.01) + 12; + uint8* temp = (uint8*)System::malloc(newSize); + int result = compress2(temp, &newSize, m_buffer, m_bufferLen, 9); + + debugAssert(result == Z_OK); (void)result; + + // Write the header + if (m_swapBytes) { + m_buffer[0] = convert[3]; + m_buffer[1] = convert[2]; + m_buffer[2] = convert[1]; + m_buffer[3] = convert[0]; + } else { + m_buffer[0] = convert[0]; + m_buffer[1] = convert[1]; + m_buffer[2] = convert[2]; + m_buffer[3] = convert[3]; + } + + // Write the data + if ((int64)newSize + 4 > (int64)m_maxBufferLen) { + m_maxBufferLen = newSize + 4; + m_buffer = (uint8*)System::realloc(m_buffer, m_maxBufferLen); + } + m_bufferLen = newSize + 4; + System::memcpy(m_buffer + 4, temp, newSize); + m_pos = m_bufferLen; + + System::free(temp); +} + + +void BinaryOutput::commit(bool flush) { + debugAssertM(! m_committed, "Cannot commit twice"); + m_committed = true; + debugAssertM(m_beginEndBits == 0, "Missing endBits before commit"); + + // Make sure the directory exists. + std::string root, base, ext, path; + Array<std::string> pathArray; + parseFilename(m_filename, root, pathArray, base, ext); + + path = root + stringJoin(pathArray, '/'); + if (! fileExists(path, false)) { + createDirectory(path); + } + + const char* mode = (m_alreadyWritten > 0) ? "ab" : "wb"; + + FILE* file = fopen(m_filename.c_str(), mode); + + m_ok = (file != NULL) && m_ok; + + if (m_ok) { + debugAssertM(file, std::string("Could not open '") + m_filename + "'"); + + if (m_buffer != NULL) { + m_alreadyWritten += m_bufferLen; + + int success = fwrite(m_buffer, m_bufferLen, 1, file); + (void)success; + debugAssertM(success == 1, std::string("Could not write to '") + m_filename + "'"); + } + if (flush) { + fflush(file); + } + fclose(file); + file = NULL; + } +} + + +void BinaryOutput::commit( + uint8* out) { + debugAssertM(! m_committed, "Cannot commit twice"); + m_committed = true; + + System::memcpy(out, m_buffer, m_bufferLen); +} + + +void BinaryOutput::writeUInt16(uint16 u) { + reserveBytes(2); + + uint8* convert = (uint8*)&u; + + if (m_swapBytes) { + m_buffer[m_pos] = convert[1]; + m_buffer[m_pos + 1] = convert[0]; + } else { + *(uint16*)(m_buffer + m_pos) = u; + } + + m_pos += 2; +} + + +void BinaryOutput::writeUInt32(uint32 u) { + reserveBytes(4); + + uint8* convert = (uint8*)&u; + + debugAssert(m_beginEndBits == 0); + + if (m_swapBytes) { + m_buffer[m_pos] = convert[3]; + m_buffer[m_pos + 1] = convert[2]; + m_buffer[m_pos + 2] = convert[1]; + m_buffer[m_pos + 3] = convert[0]; + } else { + *(uint32*)(m_buffer + m_pos) = u; + } + + m_pos += 4; +} + + +void BinaryOutput::writeUInt64(uint64 u) { + reserveBytes(8); + + uint8* convert = (uint8*)&u; + + if (m_swapBytes) { + m_buffer[m_pos] = convert[7]; + m_buffer[m_pos + 1] = convert[6]; + m_buffer[m_pos + 2] = convert[5]; + m_buffer[m_pos + 3] = convert[4]; + m_buffer[m_pos + 4] = convert[3]; + m_buffer[m_pos + 5] = convert[2]; + m_buffer[m_pos + 6] = convert[1]; + m_buffer[m_pos + 7] = convert[0]; + } else { + *(uint64*)(m_buffer + m_pos) = u; + } + + m_pos += 8; +} + + +void BinaryOutput::writeString(const char* s) { + // +1 is because strlen doesn't count the null + int len = strlen(s) + 1; + + debugAssert(m_beginEndBits == 0); + reserveBytes(len); + System::memcpy(m_buffer + m_pos, s, len); + m_pos += len; +} + + +void BinaryOutput::writeStringEven(const char* s) { + // +1 is because strlen doesn't count the null + int len = strlen(s) + 1; + + reserveBytes(len); + System::memcpy(m_buffer + m_pos, s, len); + m_pos += len; + + // Pad with another NULL + if ((len % 2) == 1) { + writeUInt8(0); + } +} + + +void BinaryOutput::writeString32(const char* s) { + writeUInt32(strlen(s) + 1); + writeString(s); +} + + +void BinaryOutput::writeVector4(const Vector4& v) { + writeFloat32(v.x); + writeFloat32(v.y); + writeFloat32(v.z); + writeFloat32(v.w); +} + + +void BinaryOutput::writeVector3(const Vector3& v) { + writeFloat32(v.x); + writeFloat32(v.y); + writeFloat32(v.z); +} + + +void BinaryOutput::writeVector2(const Vector2& v) { + writeFloat32(v.x); + writeFloat32(v.y); +} + + +void BinaryOutput::writeColor4(const Color4& v) { + writeFloat32(v.r); + writeFloat32(v.g); + writeFloat32(v.b); + writeFloat32(v.a); +} + + +void BinaryOutput::writeColor3(const Color3& v) { + writeFloat32(v.r); + writeFloat32(v.g); + writeFloat32(v.b); +} + + +void BinaryOutput::beginBits() { + debugAssertM(m_beginEndBits == 0, "Already in beginBits...endBits"); + m_bitString = 0x00; + m_bitPos = 0; + m_beginEndBits = 1; +} + + +void BinaryOutput::writeBits(uint32 value, int numBits) { + + while (numBits > 0) { + // Extract the current bit of value and + // insert it into the current byte + m_bitString |= (value & 1) << m_bitPos; + ++m_bitPos; + value = value >> 1; + --numBits; + + if (m_bitPos > 7) { + // We've reached the end of this byte + writeUInt8(m_bitString); + m_bitString = 0x00; + m_bitPos = 0; + } + } +} + + +void BinaryOutput::endBits() { + debugAssertM(m_beginEndBits == 1, "Not in beginBits...endBits"); + if (m_bitPos > 0) { + writeUInt8(m_bitString); + } + m_bitString = 0; + m_bitPos = 0; + m_beginEndBits = 0; +} + +} diff --git a/externals/g3dlite/Box.cpp b/externals/g3dlite/Box.cpp new file mode 100644 index 00000000000..f7c112ae3a5 --- /dev/null +++ b/externals/g3dlite/Box.cpp @@ -0,0 +1,393 @@ +/** + @file Box.cpp + Box class + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2001-06-02 + @edited 2006-02-05 +*/ + +#include "G3D/Box.h" +#include "G3D/debug.h" +#include "G3D/Plane.h" +#include "G3D/AABox.h" +#include "G3D/CoordinateFrame.h" + +namespace G3D { + +/** + Sets a field on four vertices. Used by the constructor. + */ +#define setMany(i0, i1, i2, i3, field, extreme) \ + _corner[i0].field = _corner[i1].field = \ + _corner[i2].field = _corner[i3].field = \ + (extreme).field + +Box::Box() { +} + + +Box::Box(const AABox& b) { + init(b.low(), b.high()); +} + +Box::Box(class BinaryInput& b) { + deserialize(b); +} + + +void Box::serialize(class BinaryOutput& b) const { + int i; + for (i = 0; i < 8; ++i) { + _corner[i].serialize(b); + } + + // Other state can be reconstructed +} + + +void Box::deserialize(class BinaryInput& b) { + int i; + + _center = Vector3::zero(); + for (i = 0; i < 8; ++i) { + _corner[i].deserialize(b); + _center += _corner[i]; + } + + _center = _center / 8; + + // Reconstruct other state from the corners + _axis[0] = _corner[5] - _corner[4]; + _axis[1] = _corner[7] - _corner[4]; + _axis[2] = _corner[0] - _corner[4]; + + for (i = 0; i < 3; ++i) { + _extent[i] = _axis[i].magnitude(); + _axis[i] /= _extent[i]; + } + + _volume = _extent.x * _extent.y * _extent.z; + + _area = 2 * + (_extent.x * _extent.y + + _extent.y * _extent.z + + _extent.z * _extent.x); +} + + +Box::Box( + const Vector3& min, + const Vector3& max) { + + init(min.min(max), min.max(max)); + +} + +void Box::init( + const Vector3& min, + const Vector3& max) { + + debugAssert( + (min.x <= max.x) && + (min.y <= max.y) && + (min.z <= max.z)); + + setMany(0, 1, 2, 3, z, max); + setMany(4, 5, 6, 7, z, min); + + setMany(1, 2, 5, 6, x, max); + setMany(0, 3, 4, 7, x, min); + + setMany(3, 2, 6, 7, y, max); + setMany(0, 1, 5, 4, y, min); + + _extent = max - min; + + _axis[0] = Vector3::unitX(); + _axis[1] = Vector3::unitY(); + _axis[2] = Vector3::unitZ(); + + if (_extent.isFinite()) { + _volume = _extent.x * _extent.y * _extent.z; + } else { + _volume = G3D::finf(); + } + + debugAssert(! isNaN(_extent.x)); + + _area = 2 * + (_extent.x * _extent.y + + _extent.y * _extent.z + + _extent.z * _extent.x); + + _center = (max + min) * 0.5f; + + // If the extent is infinite along an axis, make the center zero to avoid NaNs + for (int i = 0; i < 3; ++i) { + if (! G3D::isFinite(_extent[i])) { + _center[i] = 0.0f; + } + } +} + + +float Box::volume() const { + return _volume; +} + + +float Box::area() const { + return _area; +} + + +void Box::getLocalFrame(CoordinateFrame& frame) const { + + frame.rotation = Matrix3( + _axis[0][0], _axis[1][0], _axis[2][0], + _axis[0][1], _axis[1][1], _axis[2][1], + _axis[0][2], _axis[1][2], _axis[2][2]); + + frame.translation = _center; +} + + +CoordinateFrame Box::localFrame() const { + CoordinateFrame out; + getLocalFrame(out); + return out; +} + + +void Box::getFaceCorners(int f, Vector3& v0, Vector3& v1, Vector3& v2, Vector3& v3) const { + switch (f) { + case 0: + v0 = _corner[0]; v1 = _corner[1]; v2 = _corner[2]; v3 = _corner[3]; + break; + + case 1: + v0 = _corner[1]; v1 = _corner[5]; v2 = _corner[6]; v3 = _corner[2]; + break; + + case 2: + v0 = _corner[7]; v1 = _corner[6]; v2 = _corner[5]; v3 = _corner[4]; + break; + + case 3: + v0 = _corner[2]; v1 = _corner[6]; v2 = _corner[7]; v3 = _corner[3]; + break; + + case 4: + v0 = _corner[3]; v1 = _corner[7]; v2 = _corner[4]; v3 = _corner[0]; + break; + + case 5: + v0 = _corner[1]; v1 = _corner[0]; v2 = _corner[4]; v3 = _corner[5]; + break; + + default: + debugAssert((f >= 0) && (f < 6)); + } +} + + + +int Box::dummy = 0; + +bool Box::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + childMask = 0; + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + Vector3 corner; + + int numContained = 0; + int v = 0; + + // We can early-out only if we have found one point on each + // side of the plane (i.e. if we are straddling). That + // occurs when (numContained < v) && (numContained > 0) + for (v = 0; (v < 8) && ((numContained == v) || (numContained == 0)); ++v) { + if (plane[p].halfSpaceContains(_corner[v])) { + ++numContained; + } + } + + if (numContained == 0) { + // Plane p culled the box + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (numContained < v) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Box::culledBy( + const Array<Plane>& plane, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(plane.size() < 31); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < plane.size(); ++p) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culled = true; + + int v; + + // Assume this plane culls all points. See if there is a point + // not culled by the plane... early out when at least one point + // is in the positive half space. + for (v = 0; (v < 8) && culled; ++v) { + culled = ! plane[p].halfSpaceContains(corner(v)); + } + + if (culled) { + // Plane p culled the box + cullingPlane = p; + + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Box::contains( + const Vector3& point) const { + + // Form axes from three edges, transform the point into that + // space, and perform 3 interval tests + + Vector3 u = _corner[4] - _corner[0]; + Vector3 v = _corner[3] - _corner[0]; + Vector3 w = _corner[1] - _corner[0]; + + Matrix3 M = Matrix3(u.x, v.x, w.x, + u.y, v.y, w.y, + u.z, v.z, w.z); + + // M^-1 * (point - _corner[0]) = point in unit cube's object space + // compute the inverse of M + Vector3 osPoint = M.inverse() * (point - _corner[0]); + + return + (osPoint.x >= 0) && + (osPoint.y >= 0) && + (osPoint.z >= 0) && + (osPoint.x <= 1) && + (osPoint.y <= 1) && + (osPoint.z <= 1); +} + +#undef setMany + + +void Box::getRandomSurfacePoint(Vector3& P, Vector3& N) const { + float aXY = _extent.x * _extent.y; + float aYZ = _extent.y * _extent.z; + float aZX = _extent.z * _extent.x; + + float r = (float)uniformRandom(0, aXY + aYZ + aZX); + + // Choose evenly between positive and negative face planes + float d = (uniformRandom(0, 1) < 0.5f) ? -1.0f : 1.0f; + + // The probability of choosing a given face is proportional to + // its area. + if (r < aXY) { + P = _axis[0] * (float)uniformRandom(-0.5, 0.5) * _extent.x + + _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y + + _center + _axis[2] * d * _extent.z * 0.5f; + N = _axis[2] * d; + } else if (r < aYZ) { + P = _axis[1] * (float)uniformRandom(-0.5, 0.5) * _extent.y + + _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z + + _center + _axis[0] * d * _extent.x * 0.5f; + N = _axis[0] * d; + } else { + P = _axis[2] * (float)uniformRandom(-0.5, 0.5) * _extent.z + + _axis[0] *(float) uniformRandom(-0.5, 0.5) * _extent.x + + _center + _axis[1] * d * _extent.y * 0.5f; + N = _axis[1] * d; + } +} + + +Vector3 Box::randomInteriorPoint() const { + Vector3 sum = _center; + + for (int a = 0; a < 3; ++a) { + sum += _axis[a] * (float)uniformRandom(-0.5, 0.5) * _extent[a]; + } + + return sum; +} + +Box Box::inf() { + return Box(-Vector3::inf(), Vector3::inf()); +} + +void Box::getBounds(class AABox& aabb) const { + + Vector3 lo = _corner[0]; + Vector3 hi = lo; + + for (int v = 1; v < 8; ++v) { + const Vector3& C = _corner[v]; + lo = lo.min(C); + hi = hi.max(C); + } + + aabb = AABox(lo, hi); +} + + +} // namespace diff --git a/externals/g3dlite/CMakeLists.txt b/externals/g3dlite/CMakeLists.txt new file mode 100644 index 00000000000..fa80bb75445 --- /dev/null +++ b/externals/g3dlite/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB sources *.cpp) + +SET(g3dlib_STAT_SRCS + ${sources} + ) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +add_library(g3dlib STATIC ${g3dlib_STAT_SRCS}) diff --git a/externals/g3dlite/Capsule.cpp b/externals/g3dlite/Capsule.cpp new file mode 100644 index 00000000000..2ad3891c960 --- /dev/null +++ b/externals/g3dlite/Capsule.cpp @@ -0,0 +1,179 @@ +/** + @file Capsule.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-02-07 + @edited 2005-08-18 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/Capsule.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Line.h" +#include "G3D/AABox.h" + +namespace G3D { + +Capsule::Capsule(class BinaryInput& b) { + deserialize(b); +} + + +Capsule::Capsule() { +} + + +Capsule::Capsule(const Vector3& _p1, const Vector3& _p2, float _r) + : p1(_p1), p2(_p2), _radius(_r) { +} + + +void Capsule::serialize(class BinaryOutput& b) const { + p1.serialize(b); + p2.serialize(b); + b.writeFloat64(_radius); +} + + +void Capsule::deserialize(class BinaryInput& b) { + p1.deserialize(b); + p2.deserialize(b); + _radius = b.readFloat64(); +} + + +Line Capsule::axis() const { + return Line::fromTwoPoints(p1, p2); +} + + +float Capsule::volume() const { + return + // Sphere volume + pow(_radius, 3) * pi() * 4 / 3 + + + // Cylinder volume + pow(_radius, 2) * (p1 - p2).magnitude(); +} + + +float Capsule::area() const { + + return + // Sphere area + pow(_radius, 2) * 4 * pi() + + + // Cylinder area + twoPi() * _radius * (p1 - p2).magnitude(); +} + + +void Capsule::getBounds(AABox& out) const { + Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius); + Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius); + + out = AABox(min, max); +} + + +bool Capsule::contains(const Vector3& p) const { + return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(radius()); +} + + +void Capsule::getRandomSurfacePoint(Vector3& p, Vector3& N) const { + float h = height(); + float r = radius(); + + // Create a random point on a standard capsule and then rotate to the global frame. + + // Relative areas + float capRelArea = sqrt(r) / 2.0f; + float sideRelArea = r * h; + + float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea); + + if (r1 < capRelArea * 2) { + + // Select a point uniformly at random on a sphere + N = Sphere(Vector3::zero(), 1).randomSurfacePoint(); + p = N * r; + p.y += sign(p.y) * h / 2.0f; + } else { + // Side + float a = uniformRandom(0, (float)twoPi()); + N.x = cos(a); + N.y = 0; + N.z = sin(a); + p.x = N.x * r; + p.z = N.y * r; + p.y = uniformRandom(-h / 2.0f, h / 2.0f); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + p = cframe.pointToWorldSpace(p); + N = cframe.normalToWorldSpace(N); +} + + +void Capsule::getReferenceFrame(CoordinateFrame& cframe) const { + cframe.translation = center(); + + Vector3 Y = (p1 - p2).direction(); + Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX(); + Vector3 Z = X.cross(Y).direction(); + X = Y.cross(Z); + cframe.rotation.setColumn(0, X); + cframe.rotation.setColumn(1, Y); + cframe.rotation.setColumn(2, Z); +} + + +Vector3 Capsule::randomInteriorPoint() const { + float h = height(); + float r = radius(); + + // Create a random point in a standard capsule and then rotate to the global frame. + + Vector3 p; + + float hemiVolume = pi() * (r*r*r) * 4 / 6.0; + float cylVolume = pi() * square(r) * h; + + float r1 = uniformRandom(0, 2.0 * hemiVolume + cylVolume); + + if (r1 < 2.0 * hemiVolume) { + + p = Sphere(Vector3::zero(), r).randomInteriorPoint(); + + p.y += sign(p.y) * h / 2.0f; + + } else { + + // Select a point uniformly at random on a disk + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + + p = Vector3(cos(a) * r2, + uniformRandom(-h / 2.0f, h / 2.0f), + sin(a) * r2); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + return cframe.pointToWorldSpace(p); +} + +} // namespace diff --git a/externals/g3dlite/CollisionDetection.cpp b/externals/g3dlite/CollisionDetection.cpp new file mode 100644 index 00000000000..77eef0a5500 --- /dev/null +++ b/externals/g3dlite/CollisionDetection.cpp @@ -0,0 +1,2455 @@ +/** + @file CollisionDetection.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @cite Bounce direction based on Paul Nettle's ftp://ftp.3dmaileffects.com/pub/FluidStudios/CollisionDetection/Fluid_Studios_Generic_Collision_Detection_for_Games_Using_Ellipsoids.pdf and comments by Max McGuire. Ray-sphere code by Eric Haines. + + @created 2001-11-24 + @edited 2008-12-29 + */ + +#include "G3D/CoordinateFrame.h" +#include "G3D/platform.h" +#include "G3D/CollisionDetection.h" +#include "G3D/debugAssert.h" +#include "G3D/vectorMath.h" +#include "G3D/Capsule.h" +#include "G3D/Plane.h" +#include "G3D/Line.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/Box.h" +#include "G3D/Triangle.h" +#include "G3D/Vector3.h" +#include "G3D/AABox.h" + +#ifdef _MSC_VER +// Turn on fast floating-point optimizations +#pragma float_control( push ) +#pragma fp_contract( on ) +#pragma fenv_access( off ) +#pragma float_control( except, off ) +#pragma float_control( precise, off ) +#endif + + +namespace G3D { + +bool CollisionDetection::ignoreBool; +Vector3 CollisionDetection::ignore; +Array<Vector3> CollisionDetection::ignoreArray; + + + +Vector3 CollisionDetection::separatingAxisForSolidBoxSolidBox( + const int separatingAxisIndex, + const Box & box1, + const Box & box2) { + debugAssert(separatingAxisIndex >= 0); + debugAssert(separatingAxisIndex < 15); + Vector3 axis; + if (separatingAxisIndex < 3) { + axis = box1.axis(separatingAxisIndex); + } else if (separatingAxisIndex < 6) { + axis = box2.axis(separatingAxisIndex - 3); + } else { + int box1Index = (separatingAxisIndex - 6) / 3; + int box2Index = (separatingAxisIndex - 6) % 3; + axis = cross(box1.axis(box1Index), box2.axis(box2Index)); + } + return axis; +} + +#ifdef _MSC_VER +# pragma warning (push) +# pragma warning (disable : 4244) +#endif + +float CollisionDetection::projectedDistanceForSolidBoxSolidBox( + const int separatingAxisIndex, + const Vector3 & a, + const Vector3 & b, + const Vector3 & D, + const double* c, + const double* ca, + const double* ad, + const double* bd) +{ + (void)D; + + float R0 = 0.0f; + float R1 = 0.0f; + float R = 0.0f; + switch (separatingAxisIndex) { + case 0: + // A0 + R0 = a[0]; + R1 = b[0] * ca[0] + b[1] * ca[1] + b[2] * ca[2]; + R = fabs(ad[0]); + break; + case 1: + // A1 + R0 = a[1]; + R1 = b[0] * ca[3] + b[1] * ca[4] + b[2] * ca[5]; + R = fabs(ad[1]); + break; + case 2: + // A2 + R0 = a[2]; + R1 = b[0] * ca[6] + b[1] * ca[7] + b[2] * ca[8]; + R = fabs(ad[2]); + break; + case 3: + // B0 + R0 = a[0] * ca[0] + a[1] * ca[3] + a[2] * ca[6]; + R1 = b[0]; + R = fabs(bd[0]); + break; + case 4: + // B1 + R0 = a[0] * ca[1] + a[1] * ca[4] + a[2] * ca[7]; + R1 = b[1]; + R = fabs(bd[1]); + break; + case 5: + // B2 + R0 = a[0] * ca[2] + a[1] * ca[5] + a[2] * ca[8]; + R1 = b[2]; + R = fabs(bd[2]); + break; + case 6: + // A0 x B0 + R0 = a[1] * ca[6] + a[2] * ca[3]; + R1 = b[1] * ca[2] + b[2] * ca[1]; + R = fabs(c[3] * ad[2] - c[6] * ad[1]); + break; + case 7: + // A0 x B1 + R0 = a[1] * ca[7] + a[2] * ca[4]; + R1 = b[0] * ca[2] + b[2] * ca[0]; + R = fabs(c[4] * ad[2] - c[7] * ad[1]); + break; + case 8: + // A0 x B2 + R0 = a[1] * ca[8] + a[2] * ca[5]; + R1 = b[0] * ca[1] + b[1] * ca[0]; + R = fabs(c[5] * ad[2] - c[8] * ad[1]); + break; + case 9: + // A1 x B0 + R0 = a[0] * ca[6] + a[2] * ca[0]; + R1 = b[1] * ca[5] + b[2] * ca[4]; + R = fabs(c[6] * ad[0] - c[0] * ad[2]); + break; + case 10: + // A1 x B1 + R0 = a[0] * ca[7] + a[2] * ca[1]; + R1 = b[0] * ca[5] + b[2] * ca[3]; + R = fabs(c[7] * ad[0] - c[1] * ad[2]); + break; + case 11: + // A1 x B2 + R0 = a[0] * ca[8] + a[2] * ca[2]; + R1 = b[0] * ca[4] + b[1] * ca[3]; + R = fabs(c[8] * ad[0] - c[2] * ad[2]); + break; + case 12: + // A2 x B0 + R0 = a[0] * ca[3] + a[1] * ca[0]; + R1 = b[1] * ca[8] + b[2] * ca[7]; + R = fabs(c[0] * ad[1] - c[3] * ad[0]); + break; + case 13: + // A2 x B1 + R0 = a[0] * ca[4] + a[1] * ca[1]; + R1 = b[0] * ca[8] + b[2] * ca[6]; + R = fabs(c[1] * ad[1] - c[4] * ad[0]); + break; + case 14: + // A2 x B2 + R0 = a[0] * ca[5] + a[1] * ca[2]; + R1 = b[0] * ca[7] + b[1] * ca[6]; + R = fabs(c[2] * ad[1] - c[5] * ad[0]); + break; + default: + debugAssertM(false, "fell through switch statement"); + } + + return (R - (R0 + R1)); +} + + +bool CollisionDetection::parallelAxisForSolidBoxSolidBox( + const double* ca, + const double epsilon, + int & axis1, + int & axis2) { + const double parallelDot = 1.0 - epsilon; + for (int i = 0; i < 9; i++) { + if (ca[i] >= parallelDot) { + axis1 = i / 3; + axis2 = i % 3; + return true; + } + } + return false; +} + + + + +void CollisionDetection::fillSolidBoxSolidBoxInfo( + const Box & box1, + const Box & box2, + Vector3 & a, + Vector3 & b, + Vector3 & D, + double* c, + double* ca, + double* ad, + double* bd) { + // length between center and each side of box1 and box2 + a = box1.extent() * 0.5; + b = box2.extent() * 0.5; + + // difference between centers of box1 and box2 + D = box2.center() - box1.center(); + + // store the value of all possible dot products between the + // axes of box1 and box2, c_{row, col} in the Eberly paper + // corresponds to c[row * 3 + col] for this 9 element array. + // + // c[] holds signed values, ca[] hold absolute values + for (int i = 0; i < 9; i++) { + c[i] = dot(box1.axis(i / 3), box2.axis(i % 3)); + ca[i] = fabs(c[i]); + } + + // store all possible dot products between the axes of box1 and D, + // as well as the axes of box2 and D + for (int i = 0; i < 3; i++) { + ad[i] = dot(box1.axis(i), D); + bd[i] = dot(box2.axis(i), D); + } +} + + + +bool CollisionDetection::conservativeBoxBoxTest( + const Vector3 & a, const Vector3 & b, const Vector3 & D) { + // do a quick bounding sphere test because it is relatively + // cheap, (three dot products, two sqrts, and a few others) + double boxRadius1 = a.magnitude(); + double boxRadius2 = b.magnitude(); + return (D.squaredMagnitude() < square(boxRadius1 + boxRadius2)); +} + + + + +bool CollisionDetection::fixedSolidBoxIntersectsFixedSolidBox( + const Box& box1, + const Box& box2, + const int lastSeparatingAxis) { + // for explanations of the variable please refer to the + // paper and fillSolidBoxSolidBoxInfo() + Vector3 a; + Vector3 b; + Vector3 D; + double c[9]; + double ca[9]; + double ad[3]; + double bd[3]; + + fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd); + + int dummy1, dummy2; + bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001, + dummy1, dummy2); + + // check the separating axis from the last time step + if (lastSeparatingAxis != -1 && + (lastSeparatingAxis < 6 || !parallelAxes)) { + double projectedDistance = projectedDistanceForSolidBoxSolidBox( + lastSeparatingAxis, a, b, D, c, ca, ad, bd); + + // the separating axis from the last time step is still + // valid, the boxes do not intersect + if (projectedDistance > 0.0) { + return false; + } + } + + // test if the boxes can be separated by a plane normal to + // any of the three axes of box1, any of the three axes of box2, + // or any of the 9 possible cross products of axes from box1 + // and box2 + for (int i = 0; i < 15; i++) { + // do not need to check edge-edge cases if any two of + // the axes are parallel + if (parallelAxes && i == 6) { + return true; + } + + double projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect + if (projectedDistance > 0.0) { + return false; + } + } + + return true; +} + + + +void CollisionDetection::closestPointsBetweenLineAndLine( + const Line & line1, + const Line & line2, + Vector3 & closest1, + Vector3 & closest2) { + // TODO make accessors for Line that don't make a copy of data + Vector3 P0 = line1.point(); + Vector3 u = line1.direction(); + Vector3 Q0 = line2.point(); + Vector3 v = line2.direction(); + Vector3 w0 = P0 - Q0; + + // a = 1.0, c = 1.0 + double b = dot(u, v); + double d = dot(u, w0); + double e = dot(v, w0); + double D = 1.0 - b * b; + double sc, tc; + + static const double epsilon = 0.00001; + + if (D < epsilon) { + // lines are parallel, choose P0 as one point, find the point + // on line2 that is closest to P0 + sc = 0.0; + tc = (b > 1.0) ? (d / b) : (e / 1.0); + } else { + // lines are not parallel + sc = (b * e - 1.0 * d) / D; + tc = (1.0 * e - b * d) / D; + } + + closest1 = P0 + (sc * u); + closest2 = Q0 + (tc * v); +} + + + +float CollisionDetection::penetrationDepthForFixedBoxFixedBox( + const Box& box1, + const Box& box2, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals, + const int lastSeparatingAxis) { + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + Vector3 a; + Vector3 b; + Vector3 D; + double c[9]; + double ca[9]; + double ad[3]; + double bd[3]; + + debugAssert(lastSeparatingAxis >= -1); + debugAssert(lastSeparatingAxis < 15); + + fillSolidBoxSolidBoxInfo(box1, box2, a, b, D, c, ca, ad, bd); + + int axis1, axis2; + bool parallelAxes = parallelAxisForSolidBoxSolidBox(ca, 0.00001, + axis1, axis2); + + + // check the separating axis from the last time step + if (lastSeparatingAxis != -1 && + (lastSeparatingAxis < 6 || !parallelAxes)) { + float projectedDistance = projectedDistanceForSolidBoxSolidBox( + lastSeparatingAxis, a, b, D, c, ca, ad, bd); + + // the separating axis from the last time step is still + // valid, the boxes do not intersect + if (projectedDistance > 0.0) { + return -projectedDistance; + } + } + + // test if the boxes can be separated by a plane normal to + // any of the three axes of box1, any of the three axes of box2, + // (test 9 possible cross products later) + float penetration = -finf(); + int penetrationAxisIndex = -1; + + for (int i = 0; i < 6; i++) { + float projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect + if (projectedDistance > 0.0) { + return -projectedDistance; + } + + // keep track of the axis that is least violated + if (projectedDistance > penetration) { + penetration = projectedDistance; + penetrationAxisIndex = i; + } + } + + + // for each edge-edge case we have to adjust the magnitude of + // penetration since we did not include the dot(L, L) denominator + // that can be smaller than 1.0 for the edge-edge cases. + if (!parallelAxes) { + double edgeDistances[9]; + + // run through edge-edge cases to see if we can find a separating axis + for (int i = 6; i < 15; i++) { + float projectedDistance = + projectedDistanceForSolidBoxSolidBox(i, a, b, D, c, ca, ad, bd); + + // found a separating axis, the boxes do not intersect, + // correct magnitude and return projected distance + if (projectedDistance > 0.0) { + Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2); + projectedDistance /= dot(L, L); + return -projectedDistance; + } + + edgeDistances[i - 6] = projectedDistance; + } + + // no separating axis found, the boxes do intersect, + // correct the magnitudes of the projectedDistance values + for (int i = 6; i < 15; i++) { + // find the negative penetration value with the smallest magnitude, + // the adjustment done for the edge-edge cases only increases + // magnitude by dividing by a number smaller than 1 and greater than 0 + float projectedDistance = (float)edgeDistances[i - 6]; + if (projectedDistance > penetration) { + Vector3 L = separatingAxisForSolidBoxSolidBox(i, box1, box2); + projectedDistance /= dot(L, L); + if (projectedDistance > penetration) { + penetration = projectedDistance; + penetrationAxisIndex = i; + } + } + } + } + + // get final separating axis vector + Vector3 L = separatingAxisForSolidBoxSolidBox(penetrationAxisIndex, + box1, box2); + + // set L to be the normal that faces away from box1 + if (dot(L, D) < 0) { + L = -L; + } + + Vector3 contactPoint; + + if (penetrationAxisIndex < 6) { + // vertex to face collision, find deepest colliding vertex + const Box* vertexBox; + const Box* faceBox; + Vector3 faceNormal = L; + + // L will be the outward facing normal for the faceBox + if (penetrationAxisIndex < 3) { + faceBox = & box1; + vertexBox = & box2; + if (dot(L, D) < 0) { + faceNormal = -L; + } + } else { + faceBox = & box2; + vertexBox = & box1; + if (dot(L, D) > 0) { + faceNormal = -L; + } + } + + // find the vertex that is farthest away in the direction + // face normal direction + int deepestPointIndex = 0; + float deepestPointDot = dot(faceNormal, vertexBox->corner(0)); + for (int i = 1; i < 8; i++) { + float dotProduct = dot(faceNormal, vertexBox->corner(i)); + if (dotProduct < deepestPointDot) { + deepestPointDot = dotProduct; + deepestPointIndex = i; + } + } + + // return the point half way between the deepest point and the + // contacting face + contactPoint = vertexBox->corner(deepestPointIndex) + + (-penetration * 0.5 * faceNormal); + } else { + // edge-edge case, find the two ege lines + int edge1 = (penetrationAxisIndex - 6) / 3; + int edge2 = (penetrationAxisIndex - 6) % 3; + Vector3 linePoint1 = box1.center(); + Vector3 linePoint2 = box2.center(); + Vector3 lineDir1; + Vector3 lineDir2; + + // find edge line by finding the edge axis, and the + // other two axes that are closest to the other box + for (int i = 0; i < 3; i++ ) { + if (i == edge1) { + lineDir1 = box1.axis(i); + } else { + Vector3 axis = box1.axis(i); + if (dot(axis, L) < 0) { + axis = -axis; + } + linePoint1 += axis * a[i]; + } + + if (i == edge2) { + lineDir2 = box2.axis(i); + } else { + Vector3 axis = box2.axis(i); + if (dot(axis, L) > 0) { + axis = -axis; + } + linePoint2 += axis * b[i]; + } + } + + // make lines from the two closest edges, and find + // the points that on each line that are closest to the other + Line line1 = Line::fromPointAndDirection(linePoint1, lineDir1); + Line line2 = Line::fromPointAndDirection(linePoint2, lineDir2); + Vector3 closest1; + Vector3 closest2; + + closestPointsBetweenLineAndLine(line1, line2, closest1, closest2); + + // take the average of the two closest edge points for the final + // contact point + contactPoint = (closest1 + closest2) * 0.5; + } + + contactPoints.push(contactPoint); + contactNormals.push(L); + + return -penetration; + +} + + + + +float CollisionDetection::penetrationDepthForFixedSphereFixedBox( + const Sphere& sphere, + const Box& box, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + // In its local coordinate frame, the box measures + // 2 * halfExtent[a] along dimesion a. + Vector3 halfExtent(box.extent(0), box.extent(1), box.extent(2)); + halfExtent *= 0.5f; + + CoordinateFrame boxFrame; + box.getLocalFrame(boxFrame); + + // Transform the sphere to the box's coordinate frame. + Vector3 center = boxFrame.pointToObjectSpace(sphere.center); + + // Find the square of the distance from the sphere to the box + + + // Distance along each axis from the closest side of the box + // to the sphere center. Negative values are *inside* the box. + Vector3 distOutsideBox; + + // Divide space up into the 27 regions corresponding + // to {+|-|0}X, {+|-|0}Y, {+|-|0}Z and classify the + // sphere center into one of them. + Vector3 centerRegion; + + // In the edge collision case, the edge is between vertices + // (constant + variable) and (constant - variable). + Vector3 constant, variable; + + int numNonZero = 0; + + // Iterate over axes + for (int a = 0; a < 3; ++a) { + // For each (box side), see which direction the sphere + // is outside the box (positive or negative). Add the + // square of that distance to the total distance from + // the box. + + float distanceFromLow = -halfExtent[a] - center[a]; + float distanceFromHigh = center[a] - halfExtent[a]; + + if (fabsf(distanceFromLow) < fabsf(distanceFromHigh)) { + distOutsideBox[a] = distanceFromLow; + } else { + distOutsideBox[a] = distanceFromHigh; + } + + if (distanceFromLow < 0.0) { + if (distanceFromHigh < 0.0) { + // Inside the box + centerRegion[a] = 0.0; + variable[a] = 1.0; + } else { + // Off the high side + centerRegion[a] = 1.0; + constant[a] = halfExtent[a]; + ++numNonZero; + } + } else if (distanceFromHigh < 0.0) { + // Off the low side + centerRegion[a] = -1.0; + constant[a] = -halfExtent[a]; + ++numNonZero; + } else { + debugAssertM(false, + "distanceFromLow and distanceFromHigh cannot both be positive"); + } + } + + // Squared distance between the outside of the box and the + // sphere center. + float d2 = Vector3::zero().max(distOutsideBox).squaredMagnitude(); + + if (d2 > square(sphere.radius)) { + // There is no penetration because the distance is greater + // than the radius of the sphere. This is the common case + // and we quickly exit. + return -1; + } + + // We know there is some penetration but need to classify it. + // + // Examine the region that contains the center of the sphere. If + // there is exactly one non-zero axis, the collision is with a + // plane. If there are exactly two non-zero axes, the collision + // is with an edge. If all three axes are non-zero, the collision is + // with a vertex. If there are no non-zero axes, the center is inside + // the box. + + double depth = -1; + switch (numNonZero) { + case 3: // Vertex collision + // The collision point is the vertex at constant, the normal + // is the vector from there to the sphere center. + contactNormals.append(boxFrame.normalToWorldSpace(constant - center)); + contactPoints.append(boxFrame.pointToWorldSpace(constant)); + depth = sphere.radius - sqrt(d2); + break; + + case 2: // Edge collision + { + // TODO: unwrapping the edge constructor and closest point + // code will probably make it faster. + + // Determine the edge + Line line = Line::fromPointAndDirection(constant, variable); + + // Penetration depth: + depth = sphere.radius - sqrt(d2); + + // The contact point is the closes point to the sphere on the line + Vector3 X = line.closestPoint(center); + contactNormals.append(boxFrame.normalToWorldSpace(X - center).direction()); + contactPoints.append(boxFrame.pointToWorldSpace(X)); + } + break; + + case 1: // Plane collision + { + // The plane normal is the centerRegion vector, + // so the sphere normal is the negative. Take + // it to world space from box-space. + + // Center region doesn't need to be normalized because + // it is known to contain only one non-zero value + // and that value is +/- 1. + Vector3 N = boxFrame.normalToWorldSpace(-centerRegion); + contactNormals.append(N); + + // Penetration depth: + depth = sphere.radius - sqrtf(d2); + + // Compute the contact point from the penetration depth + contactPoints.append(sphere.center + N * (sphere.radius - depth)); + } + break; + + case 0: // Volume collision + + // The sphere center is inside the box. This is an easy case + // to handle. Note that all axes of distOutsideBox must + // be negative. + + // Arbitratily choose the sphere center as a contact point + contactPoints.append(sphere.center); + + // Find the least-negative penetration axis. + // + // We could have computed this during the loop over the axes, + // but since volume collisions are rare (they only occur with + // large time steps), this case will seldom be executed and + // should not be optimized at the expense of the others. + if (distOutsideBox.x > distOutsideBox.y) { + if (distOutsideBox.x > distOutsideBox.z) { + // Smallest penetration on x-axis + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.x > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitX())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitX())); + } + depth = -distOutsideBox.x; + } else { + // Smallest penetration on z-axis + goto ZAXIS; + } + } else if (distOutsideBox.y > distOutsideBox.z) { + // Smallest penetration on y-axis + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.y > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitY())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitY())); + } + depth = -distOutsideBox.y; + } else { + // Smallest on z-axis +ZAXIS: + // Chose normal based on which side we're closest to. + // Keep in mind that this is a normal to the sphere, + // so it is the inverse of the box normal. + if (center.z > 0) { + contactNormals.append(boxFrame.normalToWorldSpace(-Vector3::unitZ())); + } else { + contactNormals.append(boxFrame.normalToWorldSpace(Vector3::unitZ())); + } + depth = -distOutsideBox.z; + } + break; + + default: + debugAssertM(false, "Fell through switch"); + break; + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedSphereFixedSphere( + const Sphere& sphereA, + const Sphere& sphereB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 axis = sphereB.center - sphereA.center; + double radius = sphereA.radius + sphereB.radius; + double mag = axis.magnitude(); + axis /= mag; + double depth = -(mag - radius); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + if (depth >= 0) { + contactPoints.append(sphereA.center + axis * (sphereA.radius - depth / 2)); + contactNormals.append(axis); + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedSphereFixedPlane( + const Sphere& sphereA, + const Plane& planeB, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 N; + double d; + + planeB.getEquation(N, d); + + double depth = -(sphereA.center.dot(N) + d - sphereA.radius); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + if (depth >= 0) { + contactPoints.append(N * (depth - sphereA.radius) + sphereA.center); + contactNormals.append(N); + } + + return depth; +} + + +float CollisionDetection::penetrationDepthForFixedBoxFixedPlane( + const Box& box, + const Plane& plane, + Array<Vector3>& contactPoints, + Array<Vector3>& contactNormals) { + + Vector3 N; + double d; + + plane.getEquation(N, d); + + contactPoints.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + contactNormals.resize(0, DONT_SHRINK_UNDERLYING_ARRAY); + + float lowest = finf(); + for (int i = 0; i < 8; ++i) { + const Vector3 vertex = box.corner(i); + + float x = vertex.dot(N) + (float)d; + + if (x <= 0) { + // All vertices below the plane should be contact points. + contactPoints.append(vertex); + contactNormals.append(-N); + } + + lowest = min(lowest, x); + } + + // Depth should be a positive number + return -lowest; +} + + +float CollisionDetection::collisionTimeForMovingPointFixedPlane( + const Vector3& point, + const Vector3& velocity, + const Plane& plane, + Vector3& location, + Vector3& outNormal) { + + // Solve for the time at which normal.dot(point + velocity) + d == 0. + double d; + Vector3 normal; + plane.getEquation(normal, d); + + float vdotN = velocity.dot(normal); + float pdotN = point.dot(normal); + + if (fuzzyEq(pdotN + d, 0)) { + // The point is *in* the plane. + location = point; + outNormal = normal; + return 0; + } + + if (vdotN >= 0) { + // no collision will occur + location = Vector3::inf(); + return finf(); + } + + float t = -(pdotN + d) / vdotN; + if (t < 0) { + location = Vector3::inf(); + return finf(); + } else { + location = point + velocity * t; + outNormal = normal; + return t; + } +} + +bool __fastcall CollisionDetection::rayAABox( + const Ray& ray, + const Vector3& invDir, + const AABox& box, + const Vector3& boxCenter, + float boundingRadiusSquared, + Vector3& location, + bool& inside) { + + debugAssertM(fabs(ray.direction().squaredLength() - 1.0f) < 0.01f, format("Length = %f", ray.direction().length())); + { + // Pre-emptive partial bounding sphere test + const Vector3 L(boxCenter - ray.origin()); + float d = L.dot(ray.direction()); + + float L2 = L.dot(L); + float D2 = square(d); + float M2 = L2 - D2; + + if (((d < 0) && (L2 > boundingRadiusSquared)) || (M2 > boundingRadiusSquared)) { + inside = false; + return false; + } + // Passing here does not mean that the ray hits the bounding sphere; + // we would still have to perform more expensive tests to determine + // that. + } + + inside = true; + const Vector3& MinB = box.low(); + const Vector3& MaxB = box.high(); + Vector3 MaxT(-1.0f, -1.0f, -1.0f); + + // Find candidate planes. + for (int i = 0; i < 3; ++i) { + if (ray.origin()[i] < MinB[i]) { + location[i] = MinB[i]; + inside = false; + + // Calculate T distances to candidate planes + if (ray.direction()[i] != 0) { + MaxT[i] = (MinB[i] - ray.origin()[i]) * invDir[i]; + } + } else if (ray.origin()[i] > MaxB[i]) { + location[i] = MaxB[i]; + inside = false; + + // Calculate T distances to candidate planes + if (ray.direction()[i] != 0) { + MaxT[i] = (MaxB[i] - ray.origin()[i]) * invDir[i]; + } + } + } + + if (inside) { + // Ray origin inside bounding box + location = ray.origin(); + return true; + } + + // Get largest of the maxT's for final choice of intersection + int WhichPlane = 0; + if (MaxT[1] > MaxT[WhichPlane]) { + WhichPlane = 1; + } + + if (MaxT[2] > MaxT[WhichPlane]) { + WhichPlane = 2; + } + + // Check final candidate actually inside box + if (MaxT[WhichPlane] < 0.0f) { + // Miss the box + return false; + } + + for (int i = 0; i < 3; ++i) { + if (i != WhichPlane) { + location[i] = ray.origin()[i] + MaxT[WhichPlane] * ray.direction()[i]; + if ((location[i] < MinB[i]) || + (location[i] > MaxB[i])) { + // On this plane we're outside the box extents, so + // we miss the box + return false; + } + } + } + + return true; +} + +float CollisionDetection::collisionTimeForMovingPointFixedSphere( + const Vector3& point, + const Vector3& velocity, + const Sphere& sphere, + Vector3& location, + Vector3& outNormal, + bool solid) { + + if (solid && sphere.contains(point)) { + location = point; + outNormal = (point - sphere.center).direction(); + return 0.0f; + } + + float speed = velocity.magnitude(); + const Vector3& direction = velocity / speed; + + // length of the axis between the start and the sphere + const Vector3& L = sphere.center - point; + float d = L.dot(direction); + + float L2 = L.dot(L); + float R2 = square(sphere.radius); + float D2 = square(d); + + if ((d < 0.0f) && (L2 > R2)) { + location = Vector3::inf(); + return finf(); + } + + const float M2 = L2 - D2; + + if (M2 > R2) { + location = Vector3::inf(); + return finf(); + } + + float q = sqrt(R2 - M2); + float time; + + if (L2 > R2) { + time = d - q; + } else { + time = d + q; + } + + time /= speed; + + location = point + velocity * time; + outNormal = (location - sphere.center).direction(); + + return time; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedSphere( + const Sphere& movingSphere, + const Vector3& velocity, + const Sphere& fixedSphere, + Vector3& location, + Vector3& outNormal) { + + const Vector3& sep = (fixedSphere.center - movingSphere.center); + float sepLen = sep.squaredLength(); + if (sepLen < square(movingSphere.radius + fixedSphere.radius)) { + // Interpenetrating + outNormal = sep.directionOrZero(); + location = fixedSphere.center - outNormal * fixedSphere.radius; + return 0; + } + + float time = collisionTimeForMovingPointFixedSphere + (movingSphere.center, velocity, + Sphere(fixedSphere.center, fixedSphere.radius + movingSphere.radius), + location, outNormal); + + if (time < finf()) { + // Location is now the center of the moving sphere at the collision time. + // Adjust for the size of the moving sphere. Two spheres always collide + // along a line between their centers. + location += (location - fixedSphere.center) * movingSphere.radius / fixedSphere.radius; + } + + return time; +} + + +/* +float CollisionDetection::collisionTimeForMovingPointFixedTriangle( + const Vector3& point, + const Vector3& velocity, + const Triangle& triangle, + Vector3& outLocation, + Vector3& outNormal) { + + double time = collisionTimeForMovingPointFixedPlane(point, velocity, triangle.plane(), outLocation, outNormal); + + if (time == finf()) { + // No collision with the plane of the triangle. + return finf(); + } + + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), outLocation, triangle.primaryAxis())) { + // Collision occured inside the triangle + return time; + } else { + // Missed the triangle + outLocation = Vector3::inf(); + return finf(); + } +}*/ + +/* +float CollisionDetection::collisionTimeForMovingPointFixedTriangle( + const Vector3& orig, + const Vector3& dir, + const Vector3& vert0, + const Vector3& vert1, + const Vector3& vert2) { + + // Barycenteric coords + double u, v; + #define EPSILON 0.000001 + #define CROSS(dest,v1,v2) \ + dest[0]=v1[1]*v2[2]-v1[2]*v2[1]; \ + dest[1]=v1[2]*v2[0]-v1[0]*v2[2]; \ + dest[2]=v1[0]*v2[1]-v1[1]*v2[0]; + + #define DOT(v1,v2) (v1[0]*v2[0]+v1[1]*v2[1]+v1[2]*v2[2]) + + #define SUB(dest,v1,v2) \ + dest[0]=v1[0]-v2[0]; \ + dest[1]=v1[1]-v2[1]; \ + dest[2]=v1[2]-v2[2]; + + double edge1[3], edge2[3], tvec[3], pvec[3], qvec[3]; + + // find vectors for two edges sharing vert0 + SUB(edge1, vert1, vert0); + SUB(edge2, vert2, vert0); + + // begin calculating determinant - also used to calculate U parameter + CROSS(pvec, dir, edge2); + + // if determinant is near zero, ray lies in plane of triangle + const double det = DOT(edge1, pvec); + + if (det < EPSILON) { + return finf(); + } + + // calculate distance from vert0 to ray origin + SUB(tvec, orig, vert0); + + // calculate U parameter and test bounds + u = DOT(tvec, pvec); + if ((u < 0.0) || (u > det)) { + // Hit the plane outside the triangle + return finf(); + } + + // prepare to test V parameter + CROSS(qvec, tvec, edge1); + + // calculate V parameter and test bounds + v = DOT(dir, qvec); + if ((v < 0.0) || (u + v > det)) { + // Hit the plane outside the triangle + return finf(); + } + + // calculate t, scale parameters, ray intersects triangle + // If we want u,v, we can compute this + // double t = DOT(edge2, qvec); + //const double inv_det = 1.0 / det; + //t *= inv_det; + //u *= inv_det; + //v *= inv_det; + // return t; + + // Case where we don't need correct (u, v): + + const double t = DOT(edge2, qvec); + + if (t >= 0) { + // Note that det must be positive + return t / det; + } else { + // We had to travel backwards in time to intersect + return finf(); + } + + #undef EPSILON + #undef CROSS + #undef DOT + #undef SUB +} +*/ + +float CollisionDetection::collisionTimeForMovingPointFixedBox( + const Vector3& point, + const Vector3& velocity, + const Box& box, + Vector3& location, + Vector3& outNormal) { + + double bestTime; + + Vector3 normal; + Vector3 v[4]; + + // Prime the loop + int f = 0; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + bestTime = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], location, normal); + outNormal = normal; + + // Check other faces + for (f = 1; f < 6; ++f) { + Vector3 pos; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + float time = collisionTimeForMovingPointFixedRectangle(point, velocity, v[0], v[1], v[2], v[3], pos, normal); + if (time < bestTime) { + bestTime = time; + outNormal = normal; + location = pos; + } + } + + return bestTime; +} + + +float CollisionDetection::collisionTimeForMovingPointFixedAABox( + const Vector3& origin, + const Vector3& dir, + const AABox& box, + Vector3& location, + bool& Inside, + Vector3& normal) { + + if (collisionLocationForMovingPointFixedAABox(origin, dir, box, location, Inside, normal)) { + return (location - origin).magnitude(); + } else { + return (float)finf(); + } +} + + +bool CollisionDetection::collisionLocationForMovingPointFixedAABox( + const Vector3& origin, + const Vector3& dir, + const AABox& box, + Vector3& location, + bool& Inside, + Vector3& normal) { + + // Integer representation of a floating-point value. + #define IR(x) ((uint32&)x) + + Inside = true; + const Vector3& MinB = box.low(); + const Vector3& MaxB = box.high(); + Vector3 MaxT(-1.0f, -1.0f, -1.0f); + + // Find candidate planes. + for (int i = 0; i < 3; ++i) { + if (origin[i] < MinB[i]) { + location[i] = MinB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) { + MaxT[i] = (MinB[i] - origin[i]) / dir[i]; + } + } else if (origin[i] > MaxB[i]) { + location[i] = MaxB[i]; + Inside = false; + + // Calculate T distances to candidate planes + if (IR(dir[i])) { + MaxT[i] = (MaxB[i] - origin[i]) / dir[i]; + } + } + } + + if (Inside) { + // Ray origin inside bounding box + location = origin; + return false; + } + + // Get largest of the maxT's for final choice of intersection + int WhichPlane = 0; + if (MaxT[1] > MaxT[WhichPlane]) { + WhichPlane = 1; + } + + if (MaxT[2] > MaxT[WhichPlane]) { + WhichPlane = 2; + } + + // Check final candidate actually inside box + if (IR(MaxT[WhichPlane]) & 0x80000000) { + // Miss the box + return false; + } + + for (int i = 0; i < 3; ++i) { + if (i != WhichPlane) { + location[i] = origin[i] + MaxT[WhichPlane] * dir[i]; + if ((location[i] < MinB[i]) || + (location[i] > MaxB[i])) { + // On this plane we're outside the box extents, so + // we miss the box + return false; + } + } + } + + // Choose the normal to be the plane normal facing into the ray + normal = Vector3::zero(); + normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0; + + return true; + + #undef IR +} + + + +float CollisionDetection::collisionTimeForMovingPointFixedRectangle( + const Vector3& point, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& location, + Vector3& outNormal) { + + Plane plane = Plane(v0, v1, v2); + + float time = collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal); + + if (time == finf()) { + // No collision is ever going to happen + return time; + } + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) { + // The intersection point is inside the rectangle; that is the location where + // the point hits the rectangle. + return time; + } else { + return finf(); + } +} + +/** Used by findRayCapsuleIntersection. + @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp */ +static int findRayCapsuleIntersectionAux( + const Vector3& rkOrigin, + const Vector3& rkDirection, + const Capsule& rkCapsule, + double afT[2]) { + + Vector3 capsuleDirection = rkCapsule.point(1) - rkCapsule.point(0); + + // set up quadratic Q(t) = a*t^2 + 2*b*t + c + Vector3 kU, kV, kW = capsuleDirection; + float fWLength = kW.unitize(); + Vector3::generateOrthonormalBasis(kU, kV, kW); + Vector3 kD(kU.dot(rkDirection), kV.dot(rkDirection), kW.dot(rkDirection)); + float fDLength = kD.unitize(); + + float fEpsilon = 1e-6f; + + float fInvDLength = 1.0f/fDLength; + Vector3 kDiff = rkOrigin - rkCapsule.point(0); + Vector3 kP(kU.dot(kDiff),kV.dot(kDiff),kW.dot(kDiff)); + float fRadiusSqr = square(rkCapsule.radius()); + + float fInv, fA, fB, fC, fDiscr, fRoot, fT, fTmp; + + // Is the velocity parallel to the capsule direction? (or zero) + if ((abs(kD.z) >= 1.0f - fEpsilon) || (fDLength < fEpsilon)) { + + float fAxisDir = rkDirection.dot(capsuleDirection); + + fDiscr = fRadiusSqr - kP.x*kP.x - kP.y*kP.y; + if ((fAxisDir < 0) && (fDiscr >= 0.0f)) { + // Velocity anti-parallel to the capsule direction + fRoot = sqrt(fDiscr); + afT[0] = (kP.z + fRoot)*fInvDLength; + afT[1] = -(fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } else if ((fAxisDir > 0) && (fDiscr >= 0.0f)) { + // Velocity parallel to the capsule direction + fRoot = sqrt(fDiscr); + afT[0] = -(kP.z + fRoot)*fInvDLength; + afT[1] = (fWLength - kP.z + fRoot)*fInvDLength; + return 2; + } else { + // sphere heading wrong direction, or no velocity at all + return 0; + } + } + + // test intersection with infinite cylinder + fA = kD.x*kD.x + kD.y*kD.y; + fB = kP.x*kD.x + kP.y*kD.y; + fC = kP.x*kP.x + kP.y*kP.y - fRadiusSqr; + fDiscr = fB*fB - fA*fC; + if (fDiscr < 0.0f) { + // line does not intersect infinite cylinder + return 0; + } + + int iQuantity = 0; + + if (fDiscr > 0.0f) { + // line intersects infinite cylinder in two places + fRoot = sqrt(fDiscr); + fInv = 1.0f/fA; + fT = (-fB - fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[iQuantity] = fT * fInvDLength; + iQuantity++; + } + + fT = (-fB + fRoot)*fInv; + fTmp = kP.z + fT*kD.z; + + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[iQuantity++] = fT*fInvDLength; + } + + if (iQuantity == 2) { + // line intersects capsule wall in two places + return 2; + } + } else { + // line is tangent to infinite cylinder + fT = -fB/fA; + fTmp = kP.z + fT*kD.z; + if ((0.0f <= fTmp) && (fTmp <= fWLength)) { + afT[0] = fT*fInvDLength; + return 1; + } + } + + // test intersection with bottom hemisphere + // fA = 1 + fB += kP.z*kD.z; + fC += kP.z*kP.z; + fDiscr = fB*fB - fC; + if (fDiscr > 0.0f) { + fRoot = sqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } else if (fDiscr == 0.0f) { + fT = -fB; + fTmp = kP.z + fT*kD.z; + if (fTmp <= 0.0f) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } + + // test intersection with top hemisphere + // fA = 1 + fB -= kD.z*fWLength; + fC += fWLength*(fWLength - 2.0f*kP.z); + + fDiscr = fB*fB - fC; + if (fDiscr > 0.0f) { + fRoot = sqrt(fDiscr); + fT = -fB - fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + + fT = -fB + fRoot; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } else if (fDiscr == 0.0f) { + fT = -fB; + fTmp = kP.z + fT*kD.z; + if (fTmp >= fWLength) { + afT[iQuantity++] = fT*fInvDLength; + if (iQuantity == 2) { + return 2; + } + } + } + + return iQuantity; +} + + +/** Used by collisionTimeForMovingPointFixedCapsule. + @cite From magic software http://www.magic-software.com/Source/Intersection3D/MgcIntr3DLinCap.cpp + + @param rkRay The ray + @param rkCapsule The capsule + @param riQuantity The number of intersections found + @param akPoint The intersections found + @return True if there is at least one intersection + */ +static bool findRayCapsuleIntersection( + const Ray& rkRay, + const Capsule& rkCapsule, + int& riQuantity, + Vector3 akPoint[2]) { + + double afT[2]; + riQuantity = findRayCapsuleIntersectionAux(rkRay.origin(), rkRay.direction(), rkCapsule, afT); + + // Only return intersections that occur in the future + int iClipQuantity = 0; + int i; + for (i = 0; i < riQuantity; ++i) { + if (afT[i] >= 0.0f) { + akPoint[iClipQuantity] = rkRay.origin() + afT[i] * rkRay.direction(); + ++iClipQuantity; + } + } + + riQuantity = iClipQuantity; + return (riQuantity > 0); +} + +float CollisionDetection::collisionTimeForMovingPointFixedCapsule( + const Vector3& _point, + const Vector3& velocity, + const Capsule& capsule, + Vector3& location, + Vector3& outNormal) { + + float timeScale = velocity.magnitude(); + + if (timeScale == 0.0f) { + timeScale = 1; + } + + Vector3 direction = velocity / timeScale; + int numIntersections; + Vector3 intersection[2]; + findRayCapsuleIntersection(Ray::fromOriginAndDirection(_point, direction), capsule, numIntersections, intersection); + + if (numIntersections == 2) { + // A collision can only occur if there are two intersections. If there is one + // intersection, that one is exiting the capsule. + + // Find the entering intersection (the first one that occurs). + float d0 = (intersection[0] - _point).squaredMagnitude(); + float d1 = (intersection[1] - _point).squaredMagnitude(); + + // Compute the surface normal (if we aren't ignoring the result) + if (&outNormal != &ignore) { + Vector3 p2 = LineSegment::fromTwoPoints(capsule.point(0), capsule.point(1)).closestPoint(_point); + outNormal = (_point - p2).direction(); + } + + if (d0 > d1) { + location = intersection[1]; + return sqrt(d1) / timeScale; + } else { + location = intersection[0]; + return sqrt(d0) / timeScale; + } + } else { + // No entering intersection discovered; return no intersection. + location = Vector3::inf(); + return finf(); + } +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedPlane( + const Sphere& sphere, + const Vector3& velocity, + const Plane& plane, + Vector3& location, + Vector3& outNormal) { + + if (sphere.radius == 0) { + // Optimization for zero radius sphere + return collisionTimeForMovingPointFixedPlane(sphere.center, velocity, plane, location, outNormal); + } + + // The collision point on the sphere will be the point at + // center - (radius * normal). Collisions only occur when + // the sphere is travelling into the plane. + + double d; + plane.getEquation(outNormal, d); + + double vdotN = velocity.dot(outNormal); + + if (fuzzyGt(vdotN, 0)) { + // No collision when the sphere is moving towards a backface. + location = Vector3::inf(); + return (float)finf(); + } + + float cdotN = sphere.center.dot(outNormal); + + // Distance from the center to the plane + float distance = cdotN + (float)d; + + // Where is the collision on the sphere? + Vector3 point = sphere.center - (sphere.radius * outNormal); + + if (fuzzyLe(G3D::abs(distance), sphere.radius)) { + // Already interpenetrating + location = sphere.center - distance * outNormal; + return 0; + } else { + return collisionTimeForMovingPointFixedPlane(point, velocity, plane, location, outNormal); + } + +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedTriangle( + const class Sphere& sphere, + const Vector3& velocity, + const Triangle& triangle, + Vector3& outLocation, + float b[3]) { + + Vector3 dummy; + float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, triangle.plane(), + outLocation, dummy); + + if (time == finf()) { + // No collision is ever going to happen + return time; + } + + // We will hit the plane of the triangle at *time*. See if + // the intersection point actually is within the triangle. + + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + outLocation, b, triangle.primaryAxis())) { + + // The intersection point is inside the triangle; that is the location where + // the sphere hits the triangle. + +# ifdef G3D_DEBUG + { + // Internal consistency checks + debugAssertM(b[0] >= 0.0 && b[0] <= 1.0f, "Intersection is outside triangle."); + debugAssertM(b[1] >= 0.0 && b[1] <= 1.0f, "Intersection is outside triangle."); + debugAssertM(b[2] >= 0.0 && b[2] <= 1.0f, "Intersection is outside triangle."); + Vector3 blend = + b[0] * triangle.vertex(0) + + b[1] * triangle.vertex(1) + + b[2] * triangle.vertex(2); + debugAssertM(blend.fuzzyEq(outLocation), "Barycentric coords don't match intersection."); + // Call again so that we can debug the problem + // isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + // outLocation, b, triangle.primaryAxis()); + } +# endif + + return time; + } + + // The collision (if it exists) is with a point on the triangle perimeter. + // Switch over to moving the triangle towards a fixed sphere and see at what time + // they will hit. + + // Closest point on the triangle to the sphere intersection with the plane. + int edgeIndex; + const Vector3& point = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, + triangle.edgeMagnitude, outLocation, edgeIndex); + + float t = 0; + if (! sphere.contains(point)) { + // The point is outside the sphere--see when it will hit + t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy); + } + + if (t < finf()) { + outLocation = point; + // Compute Barycentric coords + + // Index of the next vertex + static const int next[] = {1, 2, 0}; + + // Project along the edge in question. + // Avoid sqrt by taking advantage of the existing edgeDirection unit vector. + b[next[edgeIndex]] = (outLocation - triangle._vertex[edgeIndex]).dot + (triangle.edgeDirection[edgeIndex]) / triangle.edgeMagnitude[edgeIndex]; + + b[edgeIndex] = 1.0f - b[next[edgeIndex]]; + + b[next[next[edgeIndex]]] = 0.0f; + +# ifdef G3D_DEBUG + { + // Internal consistency checks + for (int i = 0; i < 3; ++i) { + debugAssertM(fuzzyGe(b[i], 0.0f) && fuzzyLe(b[i], 1.0f), "Intersection is outside triangle."); + } + Vector3 blend = + b[0] * triangle.vertex(0) + + b[1] * triangle.vertex(1) + + b[2] * triangle.vertex(2); + debugAssertM(blend.fuzzyEq(outLocation), + format("Barycentric coords don't match intersection. %s != %s", + blend.toString().c_str(), + outLocation.toString().c_str())); + + // Call again so that we can debug the problem + collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, dummy, dummy); + } +# endif + + // Due to tiny roundoffs, these values might be slightly out of bounds. + // Ensure that they are legal. Note that the above debugging code + // verifies that we are not clamping truly illegal values. + for (int i = 0; i < 3; ++i) { + b[i] = clamp(b[i], 0.0f, 1.0f); + } + } + + // The collision occured at the point, if it occured. The normal + // was the plane normal, computed above. + + return t; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedRectangle( + const Sphere& sphere, + const Vector3& velocity, + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + Vector3& location, + Vector3& outNormal) { + + Plane plane(v0, v1, v2); + + float time = collisionTimeForMovingSphereFixedPlane(sphere, velocity, plane, location, outNormal); + + if (time == finf()) { + // No collision is ever going to happen + return time; + } + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), location)) { + // The intersection point is inside the rectangle; that is the location where + // the sphere hits the rectangle. + return time; + } + + // Switch over to moving the rectangle towards a fixed sphere and see at what time + // they will hit. + + Vector3 point = closestPointToRectanglePerimeter(v0, v1, v2, v3, sphere.center); + + Vector3 dummy; + double t = collisionTimeForMovingPointFixedSphere(point, -velocity, sphere, location, dummy); + + // Normal is the plane normal, location is the original location of the point. + location = point; + + return t; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedBox( + const Sphere& sphere, + const Vector3& velocity, + const Box& box, + Vector3& location, + Vector3& outNormal) { + + if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) { + // TODO: Compute more useful location and normal? + location = sphere.center; + outNormal = Vector3::zero(); + return 0; + } + + float bestTime; + + Vector3 v[4]; + int f = 0; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + bestTime = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], location, outNormal); + + for (f = 1; f < 6; ++f) { + Vector3 pos, normal; + box.getFaceCorners(f, v[0], v[1], v[2], v[3]); + float time = collisionTimeForMovingSphereFixedRectangle(sphere, velocity, v[0], v[1], v[2], v[3], pos, normal); + if (time < bestTime) { + bestTime = time; + location = pos; + outNormal = normal; + } + } + + return bestTime; +} + + +float CollisionDetection::collisionTimeForMovingSphereFixedCapsule( + const Sphere& sphere, + const Vector3& velocity, + const Capsule& capsule, + Vector3& location, + Vector3& outNormal) { + + (void)outNormal; + + Capsule _capsule(capsule.point(0), capsule.point(1), capsule.radius() + sphere.radius); + + Vector3 normal; + double time = collisionTimeForMovingPointFixedCapsule(sphere.center, velocity, _capsule, location, normal); + + if (time < finf()) { + // Location is now the position of the center of the sphere at the time of collision. + // We have to adjust the collision location for the size of the sphere. + location -= sphere.radius * normal; + } + + return time; +} + + +Vector3 CollisionDetection::bounceDirection( + const Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation, + const Vector3& collisionNormal) { + + // Location when the collision occurs + Vector3 sphereLocation = sphere.center + velocity * collisionTime; + + Vector3 normal = (sphereLocation - collisionLocation); + if (fuzzyEq(normal.squaredMagnitude(), 0)) { + normal = collisionNormal; + } else { + normal.unitize(); + } + + Vector3 direction = velocity.direction(); + + // Reflect direction about the normal + return direction - 2.0 * normal * normal.dot(direction); +} + + +Vector3 CollisionDetection::slideDirection( + const Sphere& sphere, + const Vector3& velocity, + const float collisionTime, + const Vector3& collisionLocation) { + + Vector3 sphereLocation = sphere.center + velocity * collisionTime; + Vector3 normal = (sphereLocation - collisionLocation).direction(); + Vector3 direction = velocity.direction(); + + // subtract off the part in the direction away from the normal. + return direction - normal * normal.dot(direction); +} + + +Vector3 CollisionDetection::closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& point) { + + const Vector3& edge = (v1 - v0); + float edgeLength = edge.magnitude(); + + if (edgeLength == 0) { + // The line segment is a point + return v0; + } + + return closestPointOnLineSegment(v0, v1, edge / edgeLength, edgeLength, point); +} + + +Vector3 CollisionDetection::closestPointOnLineSegment( + const Vector3& v0, + const Vector3& v1, + const Vector3& edgeDirection, + const float edgeLength, + const Vector3& point) { + + debugAssert((v1 - v0).direction().fuzzyEq(edgeDirection)); + debugAssert(fuzzyEq((v1 - v0).magnitude(), edgeLength)); + + // Vector towards the point + const Vector3& c = point - v0; + + // Projected onto the edge itself + float t = edgeDirection.dot(c); + + if (t <= 0) { + // Before the start + return v0; + } else if (t >= edgeLength) { + // After the end + return v1; + } else { + // At distance t along the edge + return v0 + edgeDirection * t; + } +} + + +Vector3 CollisionDetection::closestPointOnTrianglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& point) { + + Vector3 v[3] = {v0, v1, v2}; + Vector3 edgeDirection[3] = {(v1 - v0), (v2 - v1), (v0 - v2)}; + float edgeLength[3]; + + for (int i = 0; i < 3; ++i) { + edgeLength[i] = edgeDirection[i].magnitude(); + edgeDirection[i] /= edgeLength[i]; + } + + int edgeIndex; + return closestPointOnTrianglePerimeter(v, edgeDirection, edgeLength, point, edgeIndex); +} + + +Vector3 CollisionDetection::closestPointOnTrianglePerimeter( + const Vector3 v[3], + const Vector3 edgeDirection[3], + const float edgeLength[3], + const Vector3& point, + int& edgeIndex) { + + // Closest point on segment from v[i] to v[i + 1] + Vector3 r[3]; + + // Distance squared from r[i] to point + float d[3]; + + // Index of the next point + static const int next[] = {1, 2, 0}; + + for (int i = 0; i < 3; ++i) { + r[i] = closestPointOnLineSegment(v[i], v[next[i]], edgeDirection[i], edgeLength[i], point); + d[i] = (r[i] - point).squaredMagnitude(); + } + + if (d[0] < d[1]) { + if (d[0] < d[2]) { + // Between v0 and v1 + edgeIndex = 0; + } else { + // Between v2 and v0 + edgeIndex = 2; + } + } else { + if (d[1] < d[2]) { + // Between v1 and v2 + edgeIndex = 1; + } else { + // Between v2 and v0 + edgeIndex = 2; + } + } + +# ifdef G3D_DEBUG + { + Vector3 diff = r[edgeIndex] - v[edgeIndex]; + debugAssertM(fuzzyEq(diff.direction().dot(edgeDirection[edgeIndex]), 1.0f) || + diff.fuzzyEq(Vector3::zero()), "Point not on correct triangle edge"); + float frac = diff.dot(edgeDirection[edgeIndex])/edgeLength[edgeIndex]; + debugAssertM(frac >= -0.000001, "Point off low side of edge."); + debugAssertM(frac <= 1.000001, "Point off high side of edge."); + } +# endif + + return r[edgeIndex]; +} + + +bool CollisionDetection::isPointInsideTriangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& normal, + const Vector3& point, + float b[3], + Vector3::Axis primaryAxis) { + + if (primaryAxis == Vector3::DETECT_AXIS) { + primaryAxis = normal.primaryAxis(); + } + + // Check that the point is within the triangle using a Barycentric + // coordinate test on a two dimensional plane. + int i, j; + + switch (primaryAxis) { + case Vector3::X_AXIS: + i = Vector3::Y_AXIS; + j = Vector3::Z_AXIS; + break; + + case Vector3::Y_AXIS: + i = Vector3::Z_AXIS; + j = Vector3::X_AXIS; + break; + + case Vector3::Z_AXIS: + i = Vector3::X_AXIS; + j = Vector3::Y_AXIS; + break; + + default: + // This case is here to supress a warning on Linux + i = j = 0; + debugAssertM(false, "Should not get here."); + break; + } + + // See if all barycentric coordinates are non-negative + + // 2D area via cross product +# define AREA2(d, e, f) (((e)[i] - (d)[i]) * ((f)[j] - (d)[j]) - ((f)[i] - (d)[i]) * ((e)[j] - (d)[j])) + + // Area of the polygon + float area = AREA2(v0, v1, v2); + if (area == 0) { + // This triangle has zero area, so the point must not + // be in it unless the triangle point is the test point. + return (v0 == point); + } + + debugAssert(area != 0); + + float invArea = 1.0f / area; + + // (avoid normalization until absolutely necessary) + b[0] = AREA2(point, v1, v2) * invArea; + + if ((b[0] < 0.0f) || (b[0] > 1.0f)) { + return false; + } + + b[1] = AREA2(v0, point, v2) * invArea; + if ((b[1] < 0.0f) || (b[1] > 1.0f)) { + return false; + } + + b[2] = 1.0f - b[0] - b[1]; + +# undef AREA2 + + return (b[2] >= 0.0f) && (b[2] <= 1.0f); +} + + +bool CollisionDetection::isPointInsideRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& normal, + const Vector3& point) { + + return isPointInsideTriangle(v0, v1, v2, normal, point) || + isPointInsideTriangle(v2, v3, v0, normal, point); +} + + +Vector3 CollisionDetection::closestPointToRectanglePerimeter( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point) { + + Vector3 r0 = closestPointOnLineSegment(v0, v1, point); + Vector3 r1 = closestPointOnLineSegment(v1, v2, point); + Vector3 r2 = closestPointOnLineSegment(v2, v3, point); + Vector3 r3 = closestPointOnLineSegment(v3, v0, point); + + double d0 = (r0 - point).squaredMagnitude(); + double d1 = (r1 - point).squaredMagnitude(); + double d2 = (r2 - point).squaredMagnitude(); + double d3 = (r3 - point).squaredMagnitude(); + + if (d0 < d1) { + if (d0 < d2) { + if (d0 < d3) { + return r0; + } else { + return r3; + } + } else { + if (d2 < d3) { + return r2; + } else { + return r3; + } + } + } else { + if (d1 < d2) { + if (d1 < d3) { + return r1; + } else { + return r3; + } + } else { + if (d2 < d3) { + return r2; + } else { + return r3; + } + } + } +} + + +Vector3 CollisionDetection::closestPointToRectangle( + const Vector3& v0, + const Vector3& v1, + const Vector3& v2, + const Vector3& v3, + const Vector3& point) { + + Plane plane(v0, v1, v2); + + // Project the point into the plane + double a, b, c, d; + plane.getEquation(a, b, c, d); + + double distance = a*point.x + b*point.y + c*point.z + d; + Vector3 planePoint = point - distance * plane.normal(); + + if (isPointInsideRectangle(v0, v1, v2, v3, plane.normal(), planePoint)) { + return planePoint; + } else { + return closestPointToRectanglePerimeter(v0, v1, v2, v3, planePoint); + } +} + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidSphere( + const Sphere& sphere1, + const Sphere& sphere2) { + + return (sphere1.center - sphere2.center).squaredMagnitude() < square(sphere1.radius + sphere2.radius); +} + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedSolidBox( + const Sphere& sphere, + const Box& box) { + + // If the center of the sphere is within the box, the whole + // sphere is within the box. + if (box.contains(sphere.center)) { + return true; + } + + float r2 = square(sphere.radius); + + // Find the closest point on the surface of the box to the sphere. If + // this point is within the sphere's radius, they intersect. + int f; + for (f = 0; f < 6; ++f) { + Vector3 v0, v1, v2, v3; + box.getFaceCorners(f, v0, v1, v2, v3); + if ((closestPointToRectangle(v0, v1, v2, v3, sphere.center) - sphere.center).squaredMagnitude() <= r2) { + return true; + } + } + + return false; +} + + +bool CollisionDetection::movingSpherePassesThroughFixedBox( + const Sphere& sphere, + const Vector3& velocity, + const Box& box, + double timeLimit) { + + // If they intersect originally, they definitely pass through each other. + if (fixedSolidSphereIntersectsFixedSolidBox(sphere, box)) { + return true; + } + + // See if the sphere hits the box during the time period. + Vector3 dummy1, dummy2; + + return (collisionTimeForMovingSphereFixedBox(sphere, velocity, box, dummy1, dummy2) < timeLimit); +} + + +bool CollisionDetection::movingSpherePassesThroughFixedSphere( + const Sphere& sphere, + const Vector3& velocity, + const Sphere& fixedSphere, + double timeLimit) { + + if (fixedSolidSphereIntersectsFixedSolidSphere(sphere, fixedSphere)) { + return true; + } + + // Extend the fixed sphere by the radius of the moving sphere + Sphere bigFixed(fixedSphere.center, fixedSphere.radius + sphere.radius); + Vector3 dummy1, dummy2; + + // If the sphere collides with the other sphere during the time limit, it passes through + return (collisionTimeForMovingPointFixedSphere(sphere.center, velocity, bigFixed, dummy1, dummy2) < timeLimit); +} + + + +bool CollisionDetection::fixedSolidSphereIntersectsFixedTriangle( + const Sphere& sphere, + const Triangle& triangle) { + + // How far is the sphere from the plane of the triangle + const Plane& plane = triangle.plane(); + + // Does the closest point to the sphere center lie within the triangle? + Vector3 v = plane.closestPoint(sphere.center); + + // Is the closest point to the plane within the sphere? + if ((v - sphere.center).squaredLength() <= square(sphere.radius)) { + // Is it also within the triangle? + float b[3]; + if (isPointInsideTriangle(triangle.vertex(0), triangle.vertex(1), triangle.vertex(2), triangle.normal(), + v, b, triangle.primaryAxis())){ + // The closest point is inside the triangle + return true; + } + } + + // ignored + int edgeIndex; + + v = closestPointOnTrianglePerimeter(triangle._vertex, triangle.edgeDirection, triangle.edgeMagnitude, sphere.center, edgeIndex); + + // Is the closest point within the sphere? + return ((v - sphere.center).squaredLength() <= square(sphere.radius)); +} + + +//////////////////////////////////////////////////////////////////////////////// +// AABB-triangle overlap test code based on Tomas Akenine-Mller's +// http://www.cs.lth.se/home/Tomas_Akenine_Moller/code/tribox3.txt +// Ported 2008-12-28 + +#define X 0 +#define Y 1 +#define Z 2 + +#define FINDMINMAX(x0, x1, x2, min, max) \ + min = max = x0; \ + if(x1<min) min=x1;\ + if(x1>max) max=x1;\ + if(x2<min) min=x2;\ + if(x2>max) max=x2; + +static bool planeBoxOverlap(const Vector3& normal, const Vector3& vert, const Vector3& maxbox) { + Vector3 vmin, vmax; + float v; + + // for each axis + for(int a = 0; a < 3; ++a) { + v = vert[a]; + + if (normal[a] > 0.0f) { + vmin[a] = -maxbox[a] - v; + vmax[a] = maxbox[a] - v; + } else { + vmin[a] = maxbox[a] - v; + vmax[a] = -maxbox[a] - v; + } + } + + if (normal.dot(vmin) > 0.0f) { + return false; + } else if (normal.dot(vmax) >= 0.0f) { + return true; + } else { + return false; + } +} + +/*======================== X-tests ========================*/ + +#define AXISTEST_X01(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p2 = a*v2[Y] - b*v2[Z]; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if(min>rad || max<-rad) return false; + + +#define AXISTEST_X2(a, b, fa, fb) \ + p0 = a*v0[Y] - b*v0[Z]; \ + p1 = a*v1[Y] - b*v1[Z]; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize[Y] + fb * boxhalfsize[Z]; \ + if(min>rad || max<-rad) return false; + +/*======================== Y-tests ========================*/ + +#define AXISTEST_Y02(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p2 = -a*v2[X] + b*v2[Z]; \ + if(p0<p2) {min=p0; max=p2;} else {min=p2; max=p0;} \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Y1(a, b, fa, fb) \ + p0 = -a*v0[X] + b*v0[Z]; \ + p1 = -a*v1[X] + b*v1[Z]; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Z]; \ + if(min>rad || max<-rad) return false; + +/*======================== Z-tests ========================*/ + +#define AXISTEST_Z12(a, b, fa, fb) \ + p1 = a*v1[X] - b*v1[Y]; \ + p2 = a*v2[X] - b*v2[Y]; \ + if(p2<p1) {min=p2; max=p1;} else {min=p1; max=p2;} \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if(min>rad || max<-rad) return false; + +#define AXISTEST_Z0(a, b, fa, fb) \ + p0 = a*v0[X] - b*v0[Y]; \ + p1 = a*v1[X] - b*v1[Y]; \ + if(p0<p1) {min=p0; max=p1;} else {min=p1; max=p0;} \ + rad = fa * boxhalfsize[X] + fb * boxhalfsize[Y]; \ + if(min>rad || max<-rad) return false; + +bool CollisionDetection::fixedSolidBoxIntersectsFixedTriangle( + const AABox& box, const Triangle& tri) { + + // use separating axis theorem to test overlap between triangle and box + // need to test for overlap in these directions: + // 1) the {x,y,z}-directions (actually, since we use the AABB of the triangle + // we do not even need to test these) + // 2) normal of the triangle + // 3) crossproduct(edge from tri, {x,y,z}-direction) + // this gives 3x3=9 more tests + + // This is the fastest branch (on Sun). + // Move the triangle to the object space of the box + // Triangle vertices in box object space + + const Vector3& boxcenter = box.center(); + const Vector3& boxhalfsize = box.extent() * 0.5f; + + const Vector3& v0 = tri.vertex(0) - boxcenter; + const Vector3& v1 = tri.vertex(1) - boxcenter; + const Vector3& v2 = tri.vertex(2) - boxcenter; + + // Compute triangle edges in object space + const Vector3& e0 = v1 - v0; + const Vector3& e1 = v2 - v1; + const Vector3& e2 = v0 - v2; + + // Bullet 3: + // test the 9 tests first (this was faster) + float min,max,p0,p1,p2,rad; + Vector3 fe; + + fe = abs(e0); + AXISTEST_X01(e0[Z], e0[Y], fe[Z], fe[Y]); + AXISTEST_Y02(e0[Z], e0[X], fe[Z], fe[X]); + AXISTEST_Z12(e0[Y], e0[X], fe[Y], fe[X]); + + fe = abs(e1); + AXISTEST_X01(e1[Z], e1[Y], fe[Z], fe[Y]); + AXISTEST_Y02(e1[Z], e1[X], fe[Z], fe[X]); + AXISTEST_Z0 (e1[Y], e1[X], fe[Y], fe[X]); + + fe = abs(e2); + AXISTEST_X2 (e2[Z], e2[Y], fe[Z], fe[Y]); + AXISTEST_Y1 (e2[Z], e2[X], fe[Z], fe[X]); + AXISTEST_Z12(e2[Y], e2[X], fe[Y], fe[X]); + + // Bullet 1: + // first test overlap in the {x,y,z}-directions + // find min, max of the triangle each direction, and test for overlap in + // that direction -- this is equivalent to testing a minimal AABB around + // the triangle against the AABB + + // test in X-direction + FINDMINMAX(v0[X],v1[X],v2[X],min,max); + if (min > boxhalfsize[X] || max < -boxhalfsize[X]) { + return false; + } + + // test in Y-direction + FINDMINMAX(v0[Y],v1[Y],v2[Y],min,max); + if (min > boxhalfsize[Y] || max < -boxhalfsize[Y]) { + return false; + } + + // test in Z-direction + FINDMINMAX(v0[Z],v1[Z],v2[Z],min,max); + if (min > boxhalfsize[Z] || max < -boxhalfsize[Z]) { + return false; + } + + // Bullet 2: + // test if the box intersects the plane of the triangle + // compute plane equation of triangle: normal*x+d=0 + + if (! planeBoxOverlap(tri.normal(), v0, boxhalfsize)) { + return false; + } + + // box and triangle overlap + return true; +} +#undef X +#undef Y +#undef Z + +//////////////////////////////////////////////////////////////////////////////// + + +} // namespace + +#ifdef _MSC_VER +// Turn off fast floating-point optimizations +#pragma float_control( pop ) +#pragma warning (pop) +#endif diff --git a/externals/g3dlite/CoordinateFrame.cpp b/externals/g3dlite/CoordinateFrame.cpp new file mode 100644 index 00000000000..9b639b62082 --- /dev/null +++ b/externals/g3dlite/CoordinateFrame.cpp @@ -0,0 +1,436 @@ +/** + @file CoordinateFrame.cpp + + Coordinate frame class + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2001-06-02 + @edited 2009-11-13 + + Copyright 2000-2010, Morgan McGuire. + All rights reserved. +*/ + +#include "G3D/platform.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Quat.h" +#include "G3D/Matrix4.h" +#include "G3D/Box.h" +#include "G3D/AABox.h" +#include "G3D/Sphere.h" +#include "G3D/Triangle.h" +#include "G3D/Ray.h" +#include "G3D/Capsule.h" +#include "G3D/Cylinder.h" +#include "G3D/UprightFrame.h" +#include "G3D/Any.h" +#include "G3D/stringutils.h" + +namespace G3D { + +CoordinateFrame::CoordinateFrame(const Any& any) { + any.verifyName("CFrame"); + if (toUpper(any.name()) == "CFRAME") { + any.verifyType(Any::TABLE, Any::ARRAY); + if (any.type() == Any::TABLE) { + rotation = any["rotation"]; + translation = any["translation"]; + } else { + any.verifySize(2); + rotation = any[0]; + translation = any[1]; + } + } else { + any.verifyName("CFrame::fromXYZYPRDegrees"); + any.verifyType(Any::ARRAY); + any.verifySize(3, 6); + + int s = any.size(); + + *this = fromXYZYPRDegrees(any[0], any[1], any[2], + (s > 3) ? any[3].number() : 0.0f, + (s > 4) ? any[4].number() : 0.0f, + (s > 5) ? any[5].number() : 0.0f); + } +} + + +CoordinateFrame::operator Any() const { + float x, y, z, yaw, pitch, roll; + getXYZYPRDegrees(x, y, z, yaw, pitch, roll); + Any a(Any::ARRAY, "CFrame::fromXYZYPRDegrees"); + a.append(x, y, z, yaw); + if ( ! G3D::fuzzyEq(yaw, 0.0f) || ! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) { + a.append(yaw); + if (! G3D::fuzzyEq(pitch, 0.0f) || ! G3D::fuzzyEq(roll, 0.0f)) { + a.append(pitch); + if (! G3D::fuzzyEq(roll, 0.0f)) { + a.append(roll); + } + } + } + return a; +} + + +CoordinateFrame::CoordinateFrame(const class UprightFrame& f) { + *this = f.toCoordinateFrame(); +} + + +CoordinateFrame::CoordinateFrame() : + rotation(Matrix3::identity()), translation(Vector3::zero()) { +} + +CoordinateFrame CoordinateFrame::fromXYZYPRRadians(float x, float y, float z, float yaw, + float pitch, float roll) { + Matrix3 rotation = Matrix3::fromAxisAngle(Vector3::unitY(), yaw); + + rotation = Matrix3::fromAxisAngle(rotation.column(0), pitch) * rotation; + rotation = Matrix3::fromAxisAngle(rotation.column(2), roll) * rotation; + + const Vector3 translation(x, y, z); + + return CoordinateFrame(rotation, translation); +} + + +void CoordinateFrame::getXYZYPRRadians(float& x, float& y, float& z, + float& yaw, float& pitch, float& roll) const { + x = translation.x; + y = translation.y; + z = translation.z; + + const Vector3& look = lookVector(); + + if (abs(look.y) > 0.99f) { + // Looking nearly straight up or down + + yaw = G3D::pi() + atan2(look.x, look.z); + pitch = asin(look.y); + roll = 0.0f; + + } else { + + // Yaw cannot be affected by others, so pull it first + yaw = G3D::pi() + atan2(look.x, look.z); + + // Pitch is the elevation of the yaw vector + pitch = asin(look.y); + + Vector3 actualRight = rightVector(); + Vector3 expectedRight = look.cross(Vector3::unitY()); + + roll = 0;//acos(actualRight.dot(expectedRight)); TODO + } +} + + +void CoordinateFrame::getXYZYPRDegrees(float& x, float& y, float& z, + float& yaw, float& pitch, float& roll) const { + getXYZYPRRadians(x, y, z, yaw, pitch, roll); + yaw = toDegrees(yaw); + pitch = toDegrees(pitch); + roll = toDegrees(roll); +} + + +CoordinateFrame CoordinateFrame::fromXYZYPRDegrees(float x, float y, float z, + float yaw, float pitch, float roll) { + return fromXYZYPRRadians(x, y, z, toRadians(yaw), toRadians(pitch), toRadians(roll)); +} + + +Ray CoordinateFrame::lookRay() const { + return Ray::fromOriginAndDirection(translation, lookVector()); +} + + +bool CoordinateFrame::fuzzyEq(const CoordinateFrame& other) const { + + for (int c = 0; c < 3; ++c) { + for (int r = 0; r < 3; ++r) { + if (! G3D::fuzzyEq(other.rotation[r][c], rotation[r][c])) { + return false; + } + } + if (! G3D::fuzzyEq(translation[c], other.translation[c])) { + return false; + } + } + + return true; +} + + +bool CoordinateFrame::fuzzyIsIdentity() const { + const Matrix3& I = Matrix3::identity(); + + for (int c = 0; c < 3; ++c) { + for (int r = 0; r < 3; ++r) { + if (fuzzyNe(I[r][c], rotation[r][c])) { + return false; + } + } + if (fuzzyNe(translation[c], 0)) { + return false; + } + } + + return true; +} + + +bool CoordinateFrame::isIdentity() const { + return + (translation == Vector3::zero()) && + (rotation == Matrix3::identity()); +} + + +Matrix4 CoordinateFrame::toMatrix4() const { + return Matrix4(*this); +} + + +std::string CoordinateFrame::toXML() const { + return G3D::format( + "<COORDINATEFRAME>\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf,\n %lf,%lf,%lf,%lf\n</COORDINATEFRAME>\n", + rotation[0][0], rotation[0][1], rotation[0][2], translation.x, + rotation[1][0], rotation[1][1], rotation[1][2], translation.y, + rotation[2][0], rotation[2][1], rotation[2][2], translation.z, + 0.0, 0.0, 0.0, 1.0); +} + + +Plane CoordinateFrame::toObjectSpace(const Plane& p) const { + Vector3 N, P; + double d; + p.getEquation(N, d); + P = N * (float)d; + P = pointToObjectSpace(P); + N = normalToObjectSpace(N); + return Plane(N, P); +} + + +Plane CoordinateFrame::toWorldSpace(const Plane& p) const { + Vector3 N, P; + double d; + p.getEquation(N, d); + P = N * (float)d; + P = pointToWorldSpace(P); + N = normalToWorldSpace(N); + return Plane(N, P); +} + + +Triangle CoordinateFrame::toObjectSpace(const Triangle& t) const { + return Triangle(pointToObjectSpace(t.vertex(0)), + pointToObjectSpace(t.vertex(1)), + pointToObjectSpace(t.vertex(2))); +} + + +Triangle CoordinateFrame::toWorldSpace(const Triangle& t) const { + return Triangle(pointToWorldSpace(t.vertex(0)), + pointToWorldSpace(t.vertex(1)), + pointToWorldSpace(t.vertex(2))); +} + + +Cylinder CoordinateFrame::toWorldSpace(const Cylinder& c) const { + return Cylinder( + pointToWorldSpace(c.point(0)), + pointToWorldSpace(c.point(1)), + c.radius()); +} + + +Capsule CoordinateFrame::toWorldSpace(const Capsule& c) const { + return Capsule( + pointToWorldSpace(c.point(0)), + pointToWorldSpace(c.point(1)), + c.radius()); +} + + +Box CoordinateFrame::toWorldSpace(const AABox& b) const { + Box b2(b); + return toWorldSpace(b2); +} + + +Box CoordinateFrame::toWorldSpace(const Box& b) const { + Box out(b); + + for (int i = 0; i < 8; ++i) { + out._corner[i] = pointToWorldSpace(b._corner[i]); + debugAssert(! isNaN(out._corner[i].x)); + } + + for (int i = 0; i < 3; ++i) { + out._axis[i] = vectorToWorldSpace(b._axis[i]); + } + + out._center = pointToWorldSpace(b._center); + + return out; +} + + +Box CoordinateFrame::toObjectSpace(const Box &b) const { + return inverse().toWorldSpace(b); +} + + +Box CoordinateFrame::toObjectSpace(const AABox& b) const { + return toObjectSpace(Box(b)); +} + + +CoordinateFrame::CoordinateFrame(class BinaryInput& b) : rotation(Matrix3::zero()) { + deserialize(b); +} + + +void CoordinateFrame::deserialize(class BinaryInput& b) { + rotation.deserialize(b); + translation.deserialize(b); +} + + +void CoordinateFrame::serialize(class BinaryOutput& b) const { + rotation.serialize(b); + translation.serialize(b); +} + + +Sphere CoordinateFrame::toWorldSpace(const Sphere &b) const { + return Sphere(pointToWorldSpace(b.center), b.radius); +} + + +Sphere CoordinateFrame::toObjectSpace(const Sphere &b) const { + return Sphere(pointToObjectSpace(b.center), b.radius); +} + + +Ray CoordinateFrame::toWorldSpace(const Ray& r) const { + return Ray::fromOriginAndDirection(pointToWorldSpace(r.origin()), vectorToWorldSpace(r.direction())); +} + + +Ray CoordinateFrame::toObjectSpace(const Ray& r) const { + return Ray::fromOriginAndDirection(pointToObjectSpace(r.origin()), vectorToObjectSpace(r.direction())); +} + + +void CoordinateFrame::lookAt(const Vector3 &target) { + lookAt(target, Vector3::unitY()); +} + + +void CoordinateFrame::lookAt( + const Vector3& target, + Vector3 up) { + + up = up.direction(); + + Vector3 look = (target - translation).direction(); + if (fabs(look.dot(up)) > .99f) { + up = Vector3::unitX(); + if (fabs(look.dot(up)) > .99f) { + up = Vector3::unitY(); + } + } + + up -= look * look.dot(up); + up.unitize(); + + Vector3 z = -look; + Vector3 x = -z.cross(up); + x.unitize(); + + Vector3 y = z.cross(x); + + rotation.setColumn(0, x); + rotation.setColumn(1, y); + rotation.setColumn(2, z); +} + + +CoordinateFrame CoordinateFrame::lerp( + const CoordinateFrame& other, + float alpha) const { + + if (alpha == 1.0f) { + return other; + } else if (alpha == 0.0f) { + return *this; + } else { + Quat q1 = Quat(this->rotation); + Quat q2 = Quat(other.rotation); + + return CoordinateFrame( + q1.slerp(q2, alpha).toRotationMatrix(), + this->translation * (1 - alpha) + other.translation * alpha); + } +} + + +void CoordinateFrame::pointToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = pointToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::normalToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = normalToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::vectorToWorldSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = vectorToWorldSpace(v[i]); + } +} + + +void CoordinateFrame::pointToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = pointToObjectSpace(v[i]); + } +} + + +void CoordinateFrame::normalToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = normalToObjectSpace(v[i]); + } +} + + +void CoordinateFrame::vectorToObjectSpace(const Array<Vector3>& v, Array<Vector3>& vout) const { + vout.resize(v.size()); + + for (int i = v.size() - 1; i >= 0; --i) { + vout[i] = vectorToObjectSpace(v[i]); + } +} + +} // namespace diff --git a/externals/g3dlite/Crypto.cpp b/externals/g3dlite/Crypto.cpp new file mode 100644 index 00000000000..c69b23375ce --- /dev/null +++ b/externals/g3dlite/Crypto.cpp @@ -0,0 +1,70 @@ +/** + @file Crypto.cpp + + @author Morgan McGuire, http://graphics.cs.williams.edu + + + @created 2006-03-28 + @edited 2006-04-06 + */ + +#include "G3D/platform.h" +#include "G3D/Crypto.h" +#include "G3D/g3dmath.h" +#include <zlib.h> + +namespace G3D { + + +int Crypto::smallPrime(int n) { + debugAssert(n < numSmallPrimes() && n >= 0); + + // From: + // http://primes.utm.edu/lists/small/1000.txt + + static const int table[] = { + 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, + 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, + 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, + 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, + 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, + 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, + 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, + 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, + 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, + 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, + 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, + 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, + 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, + 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, + 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, + 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, + 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, + 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, + 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, + 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, + 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, + 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, + 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, + 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, + 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, + 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, + 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, + 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, + 1993, 1997, 1999}; + + return table[n]; +} + + +int Crypto::numSmallPrimes() { + return 303; +} + +uint32 Crypto::crc32(const void* byte, size_t numBytes) { + return ::crc32(::crc32(0, Z_NULL, 0), static_cast<const Bytef *>(byte), numBytes); +} + +} // G3D diff --git a/externals/g3dlite/Cylinder.cpp b/externals/g3dlite/Cylinder.cpp new file mode 100644 index 00000000000..7a7b9f9440d --- /dev/null +++ b/externals/g3dlite/Cylinder.cpp @@ -0,0 +1,176 @@ +/** + @file Cylinder.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-02-07 + @edited 2006-02-18 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/Cylinder.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/LineSegment.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Line.h" +#include "G3D/AABox.h" + +namespace G3D { + +Cylinder::Cylinder(class BinaryInput& b) { + deserialize(b); +} + + +Cylinder::Cylinder() { +} + + +Cylinder::Cylinder(const Vector3& _p1, const Vector3& _p2, float _r) + : p1(_p1), p2(_p2), mRadius(_r) { +} + + +void Cylinder::serialize(class BinaryOutput& b) const { + p1.serialize(b); + p2.serialize(b); + b.writeFloat64(mRadius); +} + + +void Cylinder::deserialize(class BinaryInput& b) { + p1.deserialize(b); + p2.deserialize(b); + mRadius = b.readFloat64(); +} + + +Line Cylinder::axis() const { + return Line::fromTwoPoints(p1, p2); +} + + + +float Cylinder::radius() const { + return mRadius; +} + + +float Cylinder::volume() const { + return + (float)pi() * square(mRadius) * (p1 - p2).magnitude(); +} + + +float Cylinder::area() const { + return + // Sides + (twoPi() * mRadius) * height() + + + // Caps + twoPi() * square(mRadius); +} + +void Cylinder::getBounds(AABox& out) const { + Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * mRadius); + Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * mRadius); + out = AABox(min, max); +} + +bool Cylinder::contains(const Vector3& p) const { + return LineSegment::fromTwoPoints(p1, p2).distanceSquared(p) <= square(mRadius); +} + + +void Cylinder::getReferenceFrame(CoordinateFrame& cframe) const { + cframe.translation = center(); + + Vector3 Y = (p1 - p2).direction(); + Vector3 X = (abs(Y.dot(Vector3::unitX())) > 0.9) ? Vector3::unitY() : Vector3::unitX(); + Vector3 Z = X.cross(Y).direction(); + X = Y.cross(Z); + cframe.rotation.setColumn(0, X); + cframe.rotation.setColumn(1, Y); + cframe.rotation.setColumn(2, Z); +} + + +void Cylinder::getRandomSurfacePoint(Vector3& p, Vector3& N) const { + float h = height(); + float r = radius(); + + // Create a random point on a standard cylinder and then rotate to the global frame. + + // Relative areas (factor of 2PI already taken out) + float capRelArea = square(r) / 2.0f; + float sideRelArea = r * h; + + float r1 = uniformRandom(0, capRelArea * 2 + sideRelArea); + + if (r1 < capRelArea * 2) { + + // Select a point uniformly at random on a disk + // @cite http://mathworld.wolfram.com/DiskPointPicking.html + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + p.x = cos(a) * r2; + p.z = sin(a) * r2; + + N.x = 0; + N.z = 0; + if (r1 < capRelArea) { + // Top + p.y = h / 2.0f; + N.y = 1; + } else { + // Bottom + p.y = -h / 2.0f; + N.y = -1; + } + } else { + // Side + float a = uniformRandom(0, (float)twoPi()); + N.x = cos(a); + N.y = 0; + N.z = sin(a); + p.x = N.x * r; + p.z = N.y * r; + p.y = uniformRandom(-h / 2.0f, h / 2.0f); + } + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + p = cframe.pointToWorldSpace(p); + N = cframe.normalToWorldSpace(N); +} + + +Vector3 Cylinder::randomInteriorPoint() const { + float h = height(); + float r = radius(); + + // Create a random point in a standard cylinder and then rotate to the global frame. + + // Select a point uniformly at random on a disk + // @cite http://mathworld.wolfram.com/DiskPointPicking.html + float a = uniformRandom(0, (float)twoPi()); + float r2 = sqrt(uniformRandom(0, 1)) * r; + + Vector3 p( cos(a) * r2, + uniformRandom(-h / 2.0f, h / 2.0f), + sin(a) * r2); + + // Transform to world space + CoordinateFrame cframe; + getReferenceFrame(cframe); + + return cframe.pointToWorldSpace(p); +} + +} // namespace diff --git a/externals/g3dlite/Line.cpp b/externals/g3dlite/Line.cpp new file mode 100644 index 00000000000..195ae7197f2 --- /dev/null +++ b/externals/g3dlite/Line.cpp @@ -0,0 +1,89 @@ +/** + @file Line.cpp + + Line class + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2006-01-28 + */ + +#include "G3D/Line.h" +#include "G3D/Plane.h" + +namespace G3D { + +Vector3 Line::intersection(const Plane& plane) const { + float d; + Vector3 normal = plane.normal(); + plane.getEquation(normal, d); + float rate = _direction.dot(normal); + + if (rate == 0) { + + return Vector3::inf(); + + } else { + float t = -(d + _point.dot(normal)) / rate; + + return _point + _direction * t; + } +} + + +Line::Line(class BinaryInput& b) { + deserialize(b); +} + + +void Line::serialize(class BinaryOutput& b) const { + _point.serialize(b); + _direction.serialize(b); +} + + +void Line::deserialize(class BinaryInput& b) { + _point.deserialize(b); + _direction.deserialize(b); +} + + +Vector3 Line::closestPoint(const Vector3& pt) const { + float t = _direction.dot(pt - _point); + return _point + _direction * t; +} + + +Vector3 Line::point() const { + return _point; +} + + +Vector3 Line::direction() const { + return _direction; +} + + +Vector3 Line::closestPoint(const Line& B, float& minDist) const { + const Vector3& P1 = _point; + const Vector3& U1 = _direction; + + Vector3 P2 = B.point(); + Vector3 U2 = B.direction(); + + const Vector3& P21 = P2 - P1; + const Vector3& M = U2.cross(U1); + float m2 = M.length(); + + Vector3 R = P21.cross(M) / m2; + + float t1 = R.dot(U2); + + minDist = abs(P21.dot(M)) / sqrt(m2); + + return P1 + t1 * U1; +} + +} + diff --git a/externals/g3dlite/LineSegment.cpp b/externals/g3dlite/LineSegment.cpp new file mode 100644 index 00000000000..754600ad554 --- /dev/null +++ b/externals/g3dlite/LineSegment.cpp @@ -0,0 +1,236 @@ +/** + @file LineSegment.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-02-08 + @edited 2008-02-02 + */ + +#include "G3D/platform.h" +#include "G3D/LineSegment.h" +#include "G3D/Sphere.h" +#include "G3D/debug.h" + +namespace G3D { + + +Vector3 LineSegment::closestPoint(const Vector3& p) const { + + // The vector from the end of the capsule to the point in question. + Vector3 v(p - _point); + + // Projection of v onto the line segment scaled by + // the length of direction. + float t = direction.dot(v); + + // Avoid some square roots. Derivation: + // t/direction.length() <= direction.length() + // t <= direction.squaredLength() + + if ((t >= 0) && (t <= direction.squaredMagnitude())) { + + // The point falls within the segment. Normalize direction, + // divide t by the length of direction. + return _point + direction * t / direction.squaredMagnitude(); + + } else { + + // The point does not fall within the segment; see which end is closer. + + // Distance from 0, squared + float d0Squared = v.squaredMagnitude(); + + // Distance from 1, squared + float d1Squared = (v - direction).squaredMagnitude(); + + if (d0Squared < d1Squared) { + + // Point 0 is closer + return _point; + + } else { + + // Point 1 is closer + return _point + direction; + + } + } + +} + +Vector3 LineSegment::point(int i) const { + switch (i) { + case 0: + return _point; + + case 1: + return _point + direction; + + default: + debugAssertM(i == 0 || i == 1, "Argument to point must be 0 or 1"); + return _point; + } +} + + +bool LineSegment::intersectsSolidSphere(const class Sphere& s) const { + return distanceSquared(s.center) <= square(s.radius); +} + + +LineSegment::LineSegment(class BinaryInput& b) { + deserialize(b); +} + + +void LineSegment::serialize(class BinaryOutput& b) const { + _point.serialize(b); + direction.serialize(b); +} + + +void LineSegment::deserialize(class BinaryInput& b) { + _point.deserialize(b); + direction.deserialize(b); +} + + +Vector3 LineSegment::randomPoint() const { + return _point + uniformRandom(0, 1) * direction; +} + + +///////////////////////////////////////////////////////////////////////////////////// + +LineSegment2D LineSegment2D::fromTwoPoints(const Vector2& p0, const Vector2& p1) { + LineSegment2D s; + s.m_origin = p0; + s.m_direction = p1 - p0; + s.m_length = s.m_direction.length(); + return s; +} + + +Vector2 LineSegment2D::point(int i) const { + debugAssert(i == 0 || i == 1); + if (i == 0) { + return m_origin; + } else { + return m_direction + m_origin; + } +} + + +Vector2 LineSegment2D::closestPoint(const Vector2& Q) const { + // Two constants that appear in the result + const Vector2 k1(m_origin - Q); + const Vector2& k2 = m_direction; + + if (fuzzyEq(m_length, 0)) { + // This line segment has no length + return m_origin; + } + + // Time [0, 1] at which we hit the closest point travelling from p0 to p1. + // Derivation can be obtained by minimizing the expression + // ||P0 + (P1 - P0)t - Q||. + const float t = -k1.dot(k2) / (m_length * m_length); + + if (t < 0) { + // Clipped to low end point + return m_origin; + } else if (t > 1) { + // Clipped to high end point + return m_origin + m_direction; + } else { + // Subsitute into the line equation to find + // the point on the segment. + return m_origin + k2 * t; + } +} + + +float LineSegment2D::distance(const Vector2& p) const { + Vector2 closest = closestPoint(p); + return (closest - p).length(); +} + + +float LineSegment2D::length() const { + return m_length; +} + + +Vector2 LineSegment2D::intersection(const LineSegment2D& other) const { + + if ((m_origin == other.m_origin) || + (m_origin == other.m_origin + other.m_direction)) { + return m_origin; + } + + if (m_origin + m_direction == other.m_origin) { + return other.m_origin; + } + + // Note: Now that we've checked the endpoints, all other parallel lines can now be assumed + // to not intersect (within numerical precision) + + Vector2 dir1 = m_direction; + Vector2 dir2 = other.m_direction; + Vector2 origin1 = m_origin; + Vector2 origin2 = other.m_origin; + + if (dir1.x == 0) { + // Avoid an upcoming divide by zero + dir1 = dir1.yx(); + dir2 = dir2.yx(); + origin1 = origin1.yx(); + origin2 = origin2.yx(); + } + + // t1 = ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) / m_direction.x + // + // ((other.m_origin.x - m_origin.x) + other.m_direction.x * t2) * m_direction.y / m_direction.x = + // (other.m_origin.y - m_origin.y) + other.m_direction.y * t2 + // + // m = m_direction.y / m_direction.x + // d = other.m_origin - m_origin + // + // (d.x + other.m_direction.x * t2) * m = d.y + other.m_direction.y * t2 + // + // d.x * m + other.m_direction.x * m * t2 = d.y + other.m_direction.y * t2 + // + // d.x * m - d.y = (other.m_direction.y - other.m_direction.x * m) * t2 + // + // (d.x * m - d.y) / (other.m_direction.y - other.m_direction.x * m) = t2 + // + + Vector2 d = origin2 - origin1; + float m = dir1.y / dir1.x; + + float t2 = (d.x * m - d.y) / (dir2.y - dir2.x * m); + if (! isFinite(t2)) { + // Parallel lines: no intersection + return Vector2::inf(); + } + + if ((t2 < 0.0f) || (t2 > 1.0f)) { + // Intersection occurs past the end of the line segments + return Vector2::inf(); + } + + float t1 = (d.x + dir2.x * t2) / dir1.x; + if ((t1 < 0.0f) || (t1 > 1.0f)) { + // Intersection occurs past the end of the line segments + return Vector2::inf(); + } + + // Return the intersection point (computed from non-transposed + // variables even if we flipped above) + return m_origin + m_direction * t1; + +} + +} + diff --git a/externals/g3dlite/Log.cpp b/externals/g3dlite/Log.cpp new file mode 100644 index 00000000000..07614fcf563 --- /dev/null +++ b/externals/g3dlite/Log.cpp @@ -0,0 +1,146 @@ +/** + @file Log.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + @created 2001-08-04 + @edited 2009-01-15 + */ + +#include "G3D/platform.h" +#include "G3D/Log.h" +#include "G3D/format.h" +#include "G3D/Array.h" +#include "G3D/fileutils.h" +#include <time.h> + +#ifdef G3D_WIN32 + #include <imagehlp.h> +#else + #include <stdarg.h> +#endif + +namespace G3D { + +void logPrintf(const char* fmt, ...) { + va_list arg_list; + va_start(arg_list, fmt); + Log::common()->vprintf(fmt, arg_list); + va_end(arg_list); +} + + +void logLazyPrintf(const char* fmt, ...) { + va_list arg_list; + va_start(arg_list, fmt); + Log::common()->lazyvprintf(fmt, arg_list); + va_end(arg_list); +} + +Log* Log::commonLog = NULL; + +Log::Log(const std::string& filename, int stripFromStackBottom) : + stripFromStackBottom(stripFromStackBottom) { + + this->filename = filename; + + logFile = fopen(filename.c_str(), "w"); + + if (logFile == NULL) { + std::string drive, base, ext; + Array<std::string> path; + parseFilename(filename, drive, path, base, ext); + std::string logName = base + ((ext != "") ? ("." + ext) : ""); + + // Write time is greater than 1ms. This may be a network drive.... try another file. + #ifdef G3D_WIN32 + logName = std::string(std::getenv("TEMP")) + logName; + #else + logName = std::string("/tmp/") + logName; + #endif + + logFile = fopen(logName.c_str(), "w"); + } + + // Use a large buffer (although we flush in logPrintf) + setvbuf(logFile, NULL, _IOFBF, 2048); + + fprintf(logFile, "Application Log\n"); + time_t t; + time(&t); + fprintf(logFile, "Start: %s\n", ctime(&t)); + fflush(logFile); + + if (commonLog == NULL) { + commonLog = this; + } +} + + +Log::~Log() { + section("Shutdown"); + println("Closing log file"); + + // Make sure we don't leave a dangling pointer + if (Log::commonLog == this) { + Log::commonLog = NULL; + } + + fclose(logFile); +} + + +FILE* Log::getFile() const { + return logFile; +} + + +Log* Log::common() { + if (commonLog == NULL) { + commonLog = new Log(); + } + return commonLog; +} + + +std::string Log::getCommonLogFilename() { + return common()->filename; +} + + +void Log::section(const std::string& s) { + fprintf(logFile, "_____________________________________________________\n"); + fprintf(logFile, "\n ### %s ###\n\n", s.c_str()); +} + + +void __cdecl Log::printf(const char* fmt, ...) { + va_list arg_list; + va_start(arg_list, fmt); + print(vformat(fmt, arg_list)); + va_end(arg_list); +} + + +void __cdecl Log::vprintf(const char* fmt, va_list argPtr) { + vfprintf(logFile, fmt, argPtr); + fflush(logFile); +} + + +void __cdecl Log::lazyvprintf(const char* fmt, va_list argPtr) { + vfprintf(logFile, fmt, argPtr); +} + + +void Log::print(const std::string& s) { + fprintf(logFile, "%s", s.c_str()); + fflush(logFile); +} + + +void Log::println(const std::string& s) { + fprintf(logFile, "%s\n", s.c_str()); + fflush(logFile); +} + +} diff --git a/externals/g3dlite/Matrix3.cpp b/externals/g3dlite/Matrix3.cpp new file mode 100644 index 00000000000..b32d938f0f9 --- /dev/null +++ b/externals/g3dlite/Matrix3.cpp @@ -0,0 +1,1927 @@ +/** + @file Matrix3.cpp + + 3x3 matrix class + + @author Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2009-11-15 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. +*/ + +#include "G3D/platform.h" +#include <memory.h> +#include <assert.h> +#include "G3D/Matrix3.h" +#include "G3D/g3dmath.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Quat.h" +#include "G3D/Any.h" + +namespace G3D { + +const float Matrix3::EPSILON = 1e-06f; + +Matrix3::Matrix3(const Any& any) { + any.verifyName("Matrix3"); + any.verifyType(Any::ARRAY); + any.verifySize(9); + + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + elt[r][c] = any[r * 3 + c]; + } + } +} + + +Matrix3::operator Any() const { + Any any(Any::ARRAY, "Matrix3"); + any.resize(9); + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + any[r * 3 + c] = elt[r][c]; + } + } + + return any; +} + +const Matrix3& Matrix3::zero() { + static Matrix3 m(0, 0, 0, 0, 0, 0, 0, 0, 0); + return m; +} + +const Matrix3& Matrix3::identity() { + static Matrix3 m(1, 0, 0, 0, 1, 0, 0, 0, 1); + return m; +} + + +const float Matrix3::ms_fSvdEpsilon = 1e-04f; +const int Matrix3::ms_iSvdMaxIterations = 32; + +Matrix3::Matrix3(BinaryInput& b) { + deserialize(b); +} + +bool Matrix3::fuzzyEq(const Matrix3& b) const { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + if (! G3D::fuzzyEq(elt[r][c], b[r][c])) { + return false; + } + } + } + return true; +} + + +bool Matrix3::isRightHanded() const{ + + const Vector3& X = column(0); + const Vector3& Y = column(1); + const Vector3& Z = column(2); + + const Vector3& W = X.cross(Y); + + return W.dot(Z) > 0.0f; +} + + +bool Matrix3::isOrthonormal() const { + const Vector3& X = column(0); + const Vector3& Y = column(1); + const Vector3& Z = column(2); + + return + (G3D::fuzzyEq(X.dot(Y), 0.0f) && + G3D::fuzzyEq(Y.dot(Z), 0.0f) && + G3D::fuzzyEq(X.dot(Z), 0.0f) && + G3D::fuzzyEq(X.squaredMagnitude(), 1.0f) && + G3D::fuzzyEq(Y.squaredMagnitude(), 1.0f) && + G3D::fuzzyEq(Z.squaredMagnitude(), 1.0f)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3(const Quat& _q) { + // Implementation from Watt and Watt, pg 362 + // See also http://www.flipcode.com/documents/matrfaq.html#Q54 + Quat q = _q; + q.unitize(); + float xx = 2.0f * q.x * q.x; + float xy = 2.0f * q.x * q.y; + float xz = 2.0f * q.x * q.z; + float xw = 2.0f * q.x * q.w; + + float yy = 2.0f * q.y * q.y; + float yz = 2.0f * q.y * q.z; + float yw = 2.0f * q.y * q.w; + + float zz = 2.0f * q.z * q.z; + float zw = 2.0f * q.z * q.w; + + set(1.0f - yy - zz, xy - zw, xz + yw, + xy + zw, 1.0f - xx - zz, yz - xw, + xz - yw, yz + xw, 1.0f - xx - yy); +} + +//---------------------------------------------------------------------------- + +Matrix3::Matrix3 (const float aafEntry[3][3]) { + memcpy(elt, aafEntry, 9*sizeof(float)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3 (const Matrix3& rkMatrix) { + memcpy(elt, rkMatrix.elt, 9*sizeof(float)); +} + +//---------------------------------------------------------------------------- +Matrix3::Matrix3( + float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22) { + set(fEntry00, fEntry01, fEntry02, + fEntry10, fEntry11, fEntry12, + fEntry20, fEntry21, fEntry22); +} + +void Matrix3::set( + float fEntry00, float fEntry01, float fEntry02, + float fEntry10, float fEntry11, float fEntry12, + float fEntry20, float fEntry21, float fEntry22) { + + elt[0][0] = fEntry00; + elt[0][1] = fEntry01; + elt[0][2] = fEntry02; + elt[1][0] = fEntry10; + elt[1][1] = fEntry11; + elt[1][2] = fEntry12; + elt[2][0] = fEntry20; + elt[2][1] = fEntry21; + elt[2][2] = fEntry22; +} + + +void Matrix3::deserialize(BinaryInput& b) { + int r,c; + for (c = 0; c < 3; ++c) { + for (r = 0; r < 3; ++r) { + elt[r][c] = b.readFloat32(); + } + } +} + + +void Matrix3::serialize(BinaryOutput& b) const { + int r,c; + for (c = 0; c < 3; ++c) { + for (r = 0; r < 3; ++r) { + b.writeFloat32(elt[r][c]); + } + } +} + + +//---------------------------------------------------------------------------- +Vector3 Matrix3::column (int iCol) const { + assert((0 <= iCol) && (iCol < 3)); + return Vector3(elt[0][iCol], elt[1][iCol], + elt[2][iCol]); +} + + +const Vector3& Matrix3::row (int iRow) const { + assert((0 <= iRow) && (iRow < 3)); + return *reinterpret_cast<const Vector3*>(elt[iRow]); +} + + +void Matrix3::setColumn(int iCol, const Vector3 &vector) { + debugAssert((iCol >= 0) && (iCol < 3)); + elt[0][iCol] = vector.x; + elt[1][iCol] = vector.y; + elt[2][iCol] = vector.z; +} + + +void Matrix3::setRow(int iRow, const Vector3 &vector) { + debugAssert((iRow >= 0) && (iRow < 3)); + elt[iRow][0] = vector.x; + elt[iRow][1] = vector.y; + elt[iRow][2] = vector.z; +} + + +//---------------------------------------------------------------------------- +bool Matrix3::operator== (const Matrix3& rkMatrix) const { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + if ( elt[iRow][iCol] != rkMatrix.elt[iRow][iCol] ) + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------------- +bool Matrix3::operator!= (const Matrix3& rkMatrix) const { + return !operator==(rkMatrix); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator+ (const Matrix3& rkMatrix) const { + Matrix3 kSum; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kSum.elt[iRow][iCol] = elt[iRow][iCol] + + rkMatrix.elt[iRow][iCol]; + } + } + + return kSum; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator- (const Matrix3& rkMatrix) const { + Matrix3 kDiff; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kDiff.elt[iRow][iCol] = elt[iRow][iCol] - + rkMatrix.elt[iRow][iCol]; + } + } + + return kDiff; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator* (const Matrix3& rkMatrix) const { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd.elt[iRow][iCol] = + elt[iRow][0] * rkMatrix.elt[0][iCol] + + elt[iRow][1] * rkMatrix.elt[1][iCol] + + elt[iRow][2] * rkMatrix.elt[2][iCol]; + } + } + + return kProd; +} + +Matrix3& Matrix3::operator+= (const Matrix3& rkMatrix) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = elt[iRow][iCol] + rkMatrix.elt[iRow][iCol]; + } + } + + return *this; +} + +Matrix3& Matrix3::operator-= (const Matrix3& rkMatrix) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = elt[iRow][iCol] - rkMatrix.elt[iRow][iCol]; + } + } + + return *this; +} + +Matrix3& Matrix3::operator*= (const Matrix3& rkMatrix) { + Matrix3 mulMat; + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + mulMat.elt[iRow][iCol] = + elt[iRow][0] * rkMatrix.elt[0][iCol] + + elt[iRow][1] * rkMatrix.elt[1][iCol] + + elt[iRow][2] * rkMatrix.elt[2][iCol]; + } + } + + *this = mulMat; + return *this; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator- () const { + Matrix3 kNeg; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kNeg[iRow][iCol] = -elt[iRow][iCol]; + } + } + + return kNeg; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::operator* (float fScalar) const { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd[iRow][iCol] = fScalar * elt[iRow][iCol]; + } + } + + return kProd; +} + +Matrix3& Matrix3::operator/= (float fScalar) { + return *this *= (1.0f / fScalar); +} + +Matrix3& Matrix3::operator*= (float fScalar) { + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] *= fScalar; + } + } + + return *this; +} + +//---------------------------------------------------------------------------- +Matrix3 operator* (double fScalar, const Matrix3& rkMatrix) { + Matrix3 kProd; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kProd[iRow][iCol] = fScalar * rkMatrix.elt[iRow][iCol]; + } + } + + return kProd; +} + +Matrix3 operator* (float fScalar, const Matrix3& rkMatrix) { + return (double)fScalar * rkMatrix; +} + + +Matrix3 operator* (int fScalar, const Matrix3& rkMatrix) { + return (double)fScalar * rkMatrix; +} +//---------------------------------------------------------------------------- +Matrix3 Matrix3::transpose () const { + Matrix3 kTranspose; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + kTranspose[iRow][iCol] = elt[iCol][iRow]; + } + } + + return kTranspose; +} + +//---------------------------------------------------------------------------- +bool Matrix3::inverse (Matrix3& rkInverse, float fTolerance) const { + // Invert a 3x3 using cofactors. This is about 8 times faster than + // the Numerical Recipes code which uses Gaussian elimination. + + rkInverse[0][0] = elt[1][1] * elt[2][2] - + elt[1][2] * elt[2][1]; + rkInverse[0][1] = elt[0][2] * elt[2][1] - + elt[0][1] * elt[2][2]; + rkInverse[0][2] = elt[0][1] * elt[1][2] - + elt[0][2] * elt[1][1]; + rkInverse[1][0] = elt[1][2] * elt[2][0] - + elt[1][0] * elt[2][2]; + rkInverse[1][1] = elt[0][0] * elt[2][2] - + elt[0][2] * elt[2][0]; + rkInverse[1][2] = elt[0][2] * elt[1][0] - + elt[0][0] * elt[1][2]; + rkInverse[2][0] = elt[1][0] * elt[2][1] - + elt[1][1] * elt[2][0]; + rkInverse[2][1] = elt[0][1] * elt[2][0] - + elt[0][0] * elt[2][1]; + rkInverse[2][2] = elt[0][0] * elt[1][1] - + elt[0][1] * elt[1][0]; + + float fDet = + elt[0][0] * rkInverse[0][0] + + elt[0][1] * rkInverse[1][0] + + elt[0][2] * rkInverse[2][0]; + + if ( G3D::abs(fDet) <= fTolerance ) + return false; + + float fInvDet = 1.0 / fDet; + + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) + rkInverse[iRow][iCol] *= fInvDet; + } + + return true; +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::inverse (float fTolerance) const { + Matrix3 kInverse = Matrix3::zero(); + inverse(kInverse, fTolerance); + return kInverse; +} + +//---------------------------------------------------------------------------- +float Matrix3::determinant () const { + float fCofactor00 = elt[1][1] * elt[2][2] - + elt[1][2] * elt[2][1]; + float fCofactor10 = elt[1][2] * elt[2][0] - + elt[1][0] * elt[2][2]; + float fCofactor20 = elt[1][0] * elt[2][1] - + elt[1][1] * elt[2][0]; + + float fDet = + elt[0][0] * fCofactor00 + + elt[0][1] * fCofactor10 + + elt[0][2] * fCofactor20; + + return fDet; +} + +//---------------------------------------------------------------------------- +void Matrix3::bidiagonalize (Matrix3& kA, Matrix3& kL, + Matrix3& kR) { + float afV[3], afW[3]; + float fLength, fSign, fT1, fInvT1, fT2; + bool bIdentity; + + // map first column to (*,0,0) + fLength = sqrt(kA[0][0] * kA[0][0] + kA[1][0] * kA[1][0] + + kA[2][0] * kA[2][0]); + + if ( fLength > 0.0 ) { + fSign = (kA[0][0] > 0.0 ? 1.0 : -1.0); + fT1 = kA[0][0] + fSign * fLength; + fInvT1 = 1.0 / fT1; + afV[1] = kA[1][0] * fInvT1; + afV[2] = kA[2][0] * fInvT1; + + fT2 = -2.0 / (1.0 + afV[1] * afV[1] + afV[2] * afV[2]); + afW[0] = fT2 * (kA[0][0] + kA[1][0] * afV[1] + kA[2][0] * afV[2]); + afW[1] = fT2 * (kA[0][1] + kA[1][1] * afV[1] + kA[2][1] * afV[2]); + afW[2] = fT2 * (kA[0][2] + kA[1][2] * afV[1] + kA[2][2] * afV[2]); + kA[0][0] += afW[0]; + kA[0][1] += afW[1]; + kA[0][2] += afW[2]; + kA[1][1] += afV[1] * afW[1]; + kA[1][2] += afV[1] * afW[2]; + kA[2][1] += afV[2] * afW[1]; + kA[2][2] += afV[2] * afW[2]; + + kL[0][0] = 1.0 + fT2; + kL[0][1] = kL[1][0] = fT2 * afV[1]; + kL[0][2] = kL[2][0] = fT2 * afV[2]; + kL[1][1] = 1.0 + fT2 * afV[1] * afV[1]; + kL[1][2] = kL[2][1] = fT2 * afV[1] * afV[2]; + kL[2][2] = 1.0 + fT2 * afV[2] * afV[2]; + bIdentity = false; + } else { + kL = Matrix3::identity(); + bIdentity = true; + } + + // map first row to (*,*,0) + fLength = sqrt(kA[0][1] * kA[0][1] + kA[0][2] * kA[0][2]); + + if ( fLength > 0.0 ) { + fSign = (kA[0][1] > 0.0 ? 1.0 : -1.0); + fT1 = kA[0][1] + fSign * fLength; + afV[2] = kA[0][2] / fT1; + + fT2 = -2.0 / (1.0 + afV[2] * afV[2]); + afW[0] = fT2 * (kA[0][1] + kA[0][2] * afV[2]); + afW[1] = fT2 * (kA[1][1] + kA[1][2] * afV[2]); + afW[2] = fT2 * (kA[2][1] + kA[2][2] * afV[2]); + kA[0][1] += afW[0]; + kA[1][1] += afW[1]; + kA[1][2] += afW[1] * afV[2]; + kA[2][1] += afW[2]; + kA[2][2] += afW[2] * afV[2]; + + kR[0][0] = 1.0; + kR[0][1] = kR[1][0] = 0.0; + kR[0][2] = kR[2][0] = 0.0; + kR[1][1] = 1.0 + fT2; + kR[1][2] = kR[2][1] = fT2 * afV[2]; + kR[2][2] = 1.0 + fT2 * afV[2] * afV[2]; + } else { + kR = Matrix3::identity(); + } + + // map second column to (*,*,0) + fLength = sqrt(kA[1][1] * kA[1][1] + kA[2][1] * kA[2][1]); + + if ( fLength > 0.0 ) { + fSign = (kA[1][1] > 0.0 ? 1.0 : -1.0); + fT1 = kA[1][1] + fSign * fLength; + afV[2] = kA[2][1] / fT1; + + fT2 = -2.0 / (1.0 + afV[2] * afV[2]); + afW[1] = fT2 * (kA[1][1] + kA[2][1] * afV[2]); + afW[2] = fT2 * (kA[1][2] + kA[2][2] * afV[2]); + kA[1][1] += afW[1]; + kA[1][2] += afW[2]; + kA[2][2] += afV[2] * afW[2]; + + float fA = 1.0 + fT2; + float fB = fT2 * afV[2]; + float fC = 1.0 + fB * afV[2]; + + if ( bIdentity ) { + kL[0][0] = 1.0; + kL[0][1] = kL[1][0] = 0.0; + kL[0][2] = kL[2][0] = 0.0; + kL[1][1] = fA; + kL[1][2] = kL[2][1] = fB; + kL[2][2] = fC; + } else { + for (int iRow = 0; iRow < 3; iRow++) { + float fTmp0 = kL[iRow][1]; + float fTmp1 = kL[iRow][2]; + kL[iRow][1] = fA * fTmp0 + fB * fTmp1; + kL[iRow][2] = fB * fTmp0 + fC * fTmp1; + } + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::golubKahanStep (Matrix3& kA, Matrix3& kL, + Matrix3& kR) { + float fT11 = kA[0][1] * kA[0][1] + kA[1][1] * kA[1][1]; + float fT22 = kA[1][2] * kA[1][2] + kA[2][2] * kA[2][2]; + float fT12 = kA[1][1] * kA[1][2]; + float fTrace = fT11 + fT22; + float fDiff = fT11 - fT22; + float fDiscr = sqrt(fDiff * fDiff + 4.0 * fT12 * fT12); + float fRoot1 = 0.5 * (fTrace + fDiscr); + float fRoot2 = 0.5 * (fTrace - fDiscr); + + // adjust right + float fY = kA[0][0] - (G3D::abs(fRoot1 - fT22) <= + G3D::abs(fRoot2 - fT22) ? fRoot1 : fRoot2); + float fZ = kA[0][1]; + float fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + float fSin = fZ * fInvLength; + float fCos = -fY * fInvLength; + + float fTmp0 = kA[0][0]; + float fTmp1 = kA[0][1]; + kA[0][0] = fCos * fTmp0 - fSin * fTmp1; + kA[0][1] = fSin * fTmp0 + fCos * fTmp1; + kA[1][0] = -fSin * kA[1][1]; + kA[1][1] *= fCos; + + int iRow; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[0][iRow]; + fTmp1 = kR[1][iRow]; + kR[0][iRow] = fCos * fTmp0 - fSin * fTmp1; + kR[1][iRow] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust left + fY = kA[0][0]; + + fZ = kA[1][0]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[0][0] = fCos * kA[0][0] - fSin * kA[1][0]; + + fTmp0 = kA[0][1]; + + fTmp1 = kA[1][1]; + + kA[0][1] = fCos * fTmp0 - fSin * fTmp1; + + kA[1][1] = fSin * fTmp0 + fCos * fTmp1; + + kA[0][2] = -fSin * kA[1][2]; + + kA[1][2] *= fCos; + + int iCol; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][0]; + fTmp1 = kL[iCol][1]; + kL[iCol][0] = fCos * fTmp0 - fSin * fTmp1; + kL[iCol][1] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust right + fY = kA[0][1]; + + fZ = kA[0][2]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[0][1] = fCos * kA[0][1] - fSin * kA[0][2]; + + fTmp0 = kA[1][1]; + + fTmp1 = kA[1][2]; + + kA[1][1] = fCos * fTmp0 - fSin * fTmp1; + + kA[1][2] = fSin * fTmp0 + fCos * fTmp1; + + kA[2][1] = -fSin * kA[2][2]; + + kA[2][2] *= fCos; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[1][iRow]; + fTmp1 = kR[2][iRow]; + kR[1][iRow] = fCos * fTmp0 - fSin * fTmp1; + kR[2][iRow] = fSin * fTmp0 + fCos * fTmp1; + } + + // adjust left + fY = kA[1][1]; + + fZ = kA[2][1]; + + fInvLength = 1.0 / sqrt(fY * fY + fZ * fZ); + + fSin = fZ * fInvLength; + + fCos = -fY * fInvLength; + + kA[1][1] = fCos * kA[1][1] - fSin * kA[2][1]; + + fTmp0 = kA[1][2]; + + fTmp1 = kA[2][2]; + + kA[1][2] = fCos * fTmp0 - fSin * fTmp1; + + kA[2][2] = fSin * fTmp0 + fCos * fTmp1; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][1]; + fTmp1 = kL[iCol][2]; + kL[iCol][1] = fCos * fTmp0 - fSin * fTmp1; + kL[iCol][2] = fSin * fTmp0 + fCos * fTmp1; + } +} + +//---------------------------------------------------------------------------- +void Matrix3::singularValueDecomposition (Matrix3& kL, Vector3& kS, + Matrix3& kR) const { + int iRow, iCol; + + Matrix3 kA = *this; + bidiagonalize(kA, kL, kR); + + for (int i = 0; i < ms_iSvdMaxIterations; i++) { + float fTmp, fTmp0, fTmp1; + float fSin0, fCos0, fTan0; + float fSin1, fCos1, fTan1; + + bool bTest1 = (G3D::abs(kA[0][1]) <= + ms_fSvdEpsilon * (G3D::abs(kA[0][0]) + G3D::abs(kA[1][1]))); + bool bTest2 = (G3D::abs(kA[1][2]) <= + ms_fSvdEpsilon * (G3D::abs(kA[1][1]) + G3D::abs(kA[2][2]))); + + if ( bTest1 ) { + if ( bTest2 ) { + kS[0] = kA[0][0]; + kS[1] = kA[1][1]; + kS[2] = kA[2][2]; + break; + } else { + // 2x2 closed form factorization + fTmp = (kA[1][1] * kA[1][1] - kA[2][2] * kA[2][2] + + kA[1][2] * kA[1][2]) / (kA[1][2] * kA[2][2]); + fTan0 = 0.5 * (fTmp + sqrt(fTmp * fTmp + 4.0)); + fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0); + fSin0 = fTan0 * fCos0; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][1]; + fTmp1 = kL[iCol][2]; + kL[iCol][1] = fCos0 * fTmp0 - fSin0 * fTmp1; + kL[iCol][2] = fSin0 * fTmp0 + fCos0 * fTmp1; + } + + fTan1 = (kA[1][2] - kA[2][2] * fTan0) / kA[1][1]; + fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1); + fSin1 = -fTan1 * fCos1; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[1][iRow]; + fTmp1 = kR[2][iRow]; + kR[1][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1; + kR[2][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1; + } + + kS[0] = kA[0][0]; + kS[1] = fCos0 * fCos1 * kA[1][1] - + fSin1 * (fCos0 * kA[1][2] - fSin0 * kA[2][2]); + kS[2] = fSin0 * fSin1 * kA[1][1] + + fCos1 * (fSin0 * kA[1][2] + fCos0 * kA[2][2]); + break; + } + } else { + if ( bTest2 ) { + // 2x2 closed form factorization + fTmp = (kA[0][0] * kA[0][0] + kA[1][1] * kA[1][1] - + kA[0][1] * kA[0][1]) / (kA[0][1] * kA[1][1]); + fTan0 = 0.5 * ( -fTmp + sqrt(fTmp * fTmp + 4.0)); + fCos0 = 1.0 / sqrt(1.0 + fTan0 * fTan0); + fSin0 = fTan0 * fCos0; + + for (iCol = 0; iCol < 3; iCol++) { + fTmp0 = kL[iCol][0]; + fTmp1 = kL[iCol][1]; + kL[iCol][0] = fCos0 * fTmp0 - fSin0 * fTmp1; + kL[iCol][1] = fSin0 * fTmp0 + fCos0 * fTmp1; + } + + fTan1 = (kA[0][1] - kA[1][1] * fTan0) / kA[0][0]; + fCos1 = 1.0 / sqrt(1.0 + fTan1 * fTan1); + fSin1 = -fTan1 * fCos1; + + for (iRow = 0; iRow < 3; iRow++) { + fTmp0 = kR[0][iRow]; + fTmp1 = kR[1][iRow]; + kR[0][iRow] = fCos1 * fTmp0 - fSin1 * fTmp1; + kR[1][iRow] = fSin1 * fTmp0 + fCos1 * fTmp1; + } + + kS[0] = fCos0 * fCos1 * kA[0][0] - + fSin1 * (fCos0 * kA[0][1] - fSin0 * kA[1][1]); + kS[1] = fSin0 * fSin1 * kA[0][0] + + fCos1 * (fSin0 * kA[0][1] + fCos0 * kA[1][1]); + kS[2] = kA[2][2]; + break; + } else { + golubKahanStep(kA, kL, kR); + } + } + } + + // positize diagonal + for (iRow = 0; iRow < 3; iRow++) { + if ( kS[iRow] < 0.0 ) { + kS[iRow] = -kS[iRow]; + + for (iCol = 0; iCol < 3; iCol++) + kR[iRow][iCol] = -kR[iRow][iCol]; + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::singularValueComposition (const Matrix3& kL, + const Vector3& kS, const Matrix3& kR) { + int iRow, iCol; + Matrix3 kTmp; + + // product S*R + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) + kTmp[iRow][iCol] = kS[iRow] * kR[iRow][iCol]; + } + + // product L*S*R + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) { + elt[iRow][iCol] = 0.0; + + for (int iMid = 0; iMid < 3; iMid++) + elt[iRow][iCol] += kL[iRow][iMid] * kTmp[iMid][iCol]; + } + } +} + +//---------------------------------------------------------------------------- +void Matrix3::orthonormalize () { + // Algorithm uses Gram-Schmidt orthogonalization. If 'this' matrix is + // M = [m0|m1|m2], then orthonormal output matrix is Q = [q0|q1|q2], + // + // q0 = m0/|m0| + // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0| + // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1| + // + // where |V| indicates length of vector V and A*B indicates dot + // product of vectors A and B. + + // compute q0 + float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0] + + elt[1][0] * elt[1][0] + + elt[2][0] * elt[2][0]); + + elt[0][0] *= fInvLength; + elt[1][0] *= fInvLength; + elt[2][0] *= fInvLength; + + // compute q1 + float fDot0 = + elt[0][0] * elt[0][1] + + elt[1][0] * elt[1][1] + + elt[2][0] * elt[2][1]; + + elt[0][1] -= fDot0 * elt[0][0]; + elt[1][1] -= fDot0 * elt[1][0]; + elt[2][1] -= fDot0 * elt[2][0]; + + fInvLength = 1.0 / sqrt(elt[0][1] * elt[0][1] + + elt[1][1] * elt[1][1] + + elt[2][1] * elt[2][1]); + + elt[0][1] *= fInvLength; + elt[1][1] *= fInvLength; + elt[2][1] *= fInvLength; + + // compute q2 + float fDot1 = + elt[0][1] * elt[0][2] + + elt[1][1] * elt[1][2] + + elt[2][1] * elt[2][2]; + + fDot0 = + elt[0][0] * elt[0][2] + + elt[1][0] * elt[1][2] + + elt[2][0] * elt[2][2]; + + elt[0][2] -= fDot0 * elt[0][0] + fDot1 * elt[0][1]; + elt[1][2] -= fDot0 * elt[1][0] + fDot1 * elt[1][1]; + elt[2][2] -= fDot0 * elt[2][0] + fDot1 * elt[2][1]; + + fInvLength = 1.0 / sqrt(elt[0][2] * elt[0][2] + + elt[1][2] * elt[1][2] + + elt[2][2] * elt[2][2]); + + elt[0][2] *= fInvLength; + elt[1][2] *= fInvLength; + elt[2][2] *= fInvLength; +} + +//---------------------------------------------------------------------------- +void Matrix3::qDUDecomposition (Matrix3& kQ, + Vector3& kD, Vector3& kU) const { + // Factor M = QR = QDU where Q is orthogonal, D is diagonal, + // and U is upper triangular with ones on its diagonal. Algorithm uses + // Gram-Schmidt orthogonalization (the QR algorithm). + // + // If M = [ m0 | m1 | m2 ] and Q = [ q0 | q1 | q2 ], then + // + // q0 = m0/|m0| + // q1 = (m1-(q0*m1)q0)/|m1-(q0*m1)q0| + // q2 = (m2-(q0*m2)q0-(q1*m2)q1)/|m2-(q0*m2)q0-(q1*m2)q1| + // + // where |V| indicates length of vector V and A*B indicates dot + // product of vectors A and B. The matrix R has entries + // + // r00 = q0*m0 r01 = q0*m1 r02 = q0*m2 + // r10 = 0 r11 = q1*m1 r12 = q1*m2 + // r20 = 0 r21 = 0 r22 = q2*m2 + // + // so D = diag(r00,r11,r22) and U has entries u01 = r01/r00, + // u02 = r02/r00, and u12 = r12/r11. + + // Q = rotation + // D = scaling + // U = shear + + // D stores the three diagonal entries r00, r11, r22 + // U stores the entries U[0] = u01, U[1] = u02, U[2] = u12 + + // build orthogonal matrix Q + float fInvLength = 1.0 / sqrt(elt[0][0] * elt[0][0] + + elt[1][0] * elt[1][0] + + elt[2][0] * elt[2][0]); + kQ[0][0] = elt[0][0] * fInvLength; + kQ[1][0] = elt[1][0] * fInvLength; + kQ[2][0] = elt[2][0] * fInvLength; + + float fDot = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] + + kQ[2][0] * elt[2][1]; + kQ[0][1] = elt[0][1] - fDot * kQ[0][0]; + kQ[1][1] = elt[1][1] - fDot * kQ[1][0]; + kQ[2][1] = elt[2][1] - fDot * kQ[2][0]; + fInvLength = 1.0 / sqrt(kQ[0][1] * kQ[0][1] + kQ[1][1] * kQ[1][1] + + kQ[2][1] * kQ[2][1]); + kQ[0][1] *= fInvLength; + kQ[1][1] *= fInvLength; + kQ[2][1] *= fInvLength; + + fDot = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] + + kQ[2][0] * elt[2][2]; + kQ[0][2] = elt[0][2] - fDot * kQ[0][0]; + kQ[1][2] = elt[1][2] - fDot * kQ[1][0]; + kQ[2][2] = elt[2][2] - fDot * kQ[2][0]; + fDot = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] + + kQ[2][1] * elt[2][2]; + kQ[0][2] -= fDot * kQ[0][1]; + kQ[1][2] -= fDot * kQ[1][1]; + kQ[2][2] -= fDot * kQ[2][1]; + fInvLength = 1.0 / sqrt(kQ[0][2] * kQ[0][2] + kQ[1][2] * kQ[1][2] + + kQ[2][2] * kQ[2][2]); + kQ[0][2] *= fInvLength; + kQ[1][2] *= fInvLength; + kQ[2][2] *= fInvLength; + + // guarantee that orthogonal matrix has determinant 1 (no reflections) + float fDet = kQ[0][0] * kQ[1][1] * kQ[2][2] + kQ[0][1] * kQ[1][2] * kQ[2][0] + + kQ[0][2] * kQ[1][0] * kQ[2][1] - kQ[0][2] * kQ[1][1] * kQ[2][0] - + kQ[0][1] * kQ[1][0] * kQ[2][2] - kQ[0][0] * kQ[1][2] * kQ[2][1]; + + if ( fDet < 0.0 ) { + for (int iRow = 0; iRow < 3; iRow++) + for (int iCol = 0; iCol < 3; iCol++) + kQ[iRow][iCol] = -kQ[iRow][iCol]; + } + + // build "right" matrix R + Matrix3 kR; + + kR[0][0] = kQ[0][0] * elt[0][0] + kQ[1][0] * elt[1][0] + + kQ[2][0] * elt[2][0]; + + kR[0][1] = kQ[0][0] * elt[0][1] + kQ[1][0] * elt[1][1] + + kQ[2][0] * elt[2][1]; + + kR[1][1] = kQ[0][1] * elt[0][1] + kQ[1][1] * elt[1][1] + + kQ[2][1] * elt[2][1]; + + kR[0][2] = kQ[0][0] * elt[0][2] + kQ[1][0] * elt[1][2] + + kQ[2][0] * elt[2][2]; + + kR[1][2] = kQ[0][1] * elt[0][2] + kQ[1][1] * elt[1][2] + + kQ[2][1] * elt[2][2]; + + kR[2][2] = kQ[0][2] * elt[0][2] + kQ[1][2] * elt[1][2] + + kQ[2][2] * elt[2][2]; + + // the scaling component + kD[0] = kR[0][0]; + + kD[1] = kR[1][1]; + + kD[2] = kR[2][2]; + + // the shear component + float fInvD0 = 1.0 / kD[0]; + + kU[0] = kR[0][1] * fInvD0; + + kU[1] = kR[0][2] * fInvD0; + + kU[2] = kR[1][2] / kD[1]; +} + +//---------------------------------------------------------------------------- +void Matrix3::polarDecomposition(Matrix3 &R, Matrix3 &S) const{ + /* + Polar decomposition of a matrix. Based on pseudocode from + Nicholas J Higham, "Computing the Polar Decomposition -- with + Applications Siam Journal of Science and Statistical Computing, Vol 7, No. 4, + October 1986. + + Decomposes A into R*S, where R is orthogonal and S is symmetric. + + Ken Shoemake's "Matrix animation and polar decomposition" + in Proceedings of the conference on Graphics interface '92 + seems to be better known in the world of graphics, but Higham's version + uses a scaling constant that can lead to faster convergence than + Shoemake's when the initial matrix is far from orthogonal. + */ + + Matrix3 X = *this; + Matrix3 tmp = X.inverse(); + Matrix3 Xit = tmp.transpose(); + int iter = 0; + + const int MAX_ITERS = 100; + + const double eps = 50 * std::numeric_limits<float>::epsilon(); + const float BigEps = 50 * eps; + + /* Higham suggests using OneNorm(Xit-X) < eps * OneNorm(X) + * as the convergence criterion, but OneNorm(X) should quickly + * settle down to something between 1 and 1.7, so just comparing + * with eps seems sufficient. + *--------------------------------------------------------------- */ + + double resid = X.diffOneNorm(Xit); + while (resid > eps && iter < MAX_ITERS) { + + tmp = X.inverse(); + Xit = tmp.transpose(); + + if (resid < BigEps) { + // close enough use simple iteration + X += Xit; + X *= 0.5f; + } + else { + // not close to convergence, compute acceleration factor + float gamma = sqrt( sqrt( + (Xit.l1Norm()* Xit.lInfNorm())/(X.l1Norm()*X.lInfNorm()) ) ); + + X *= 0.5f * gamma; + tmp = Xit; + tmp *= 0.5f / gamma; + X += tmp; + } + + resid = X.diffOneNorm(Xit); + iter++; + } + + R = X; + tmp = R.transpose(); + + S = tmp * (*this); + + // S := (S + S^t)/2 one more time to make sure it is symmetric + tmp = S.transpose(); + + S += tmp; + S *= 0.5f; + +#ifdef G3D_DEBUG + // Check iter limit + assert(iter < MAX_ITERS); + + // Check A = R*S + tmp = R*S; + resid = tmp.diffOneNorm(*this); + assert(resid < eps); + + // Check R is orthogonal + tmp = R*R.transpose(); + resid = tmp.diffOneNorm(Matrix3::identity()); + assert(resid < eps); + + // Check that S is symmetric + tmp = S.transpose(); + resid = tmp.diffOneNorm(S); + assert(resid < eps); +#endif +} + +//---------------------------------------------------------------------------- +float Matrix3::maxCubicRoot (float afCoeff[3]) { + // Spectral norm is for A^T*A, so characteristic polynomial + // P(x) = c[0]+c[1]*x+c[2]*x^2+x^3 has three positive float roots. + // This yields the assertions c[0] < 0 and c[2]*c[2] >= 3*c[1]. + + // quick out for uniform scale (triple root) + const float fOneThird = 1.0f / 3.0f; + const float fEpsilon = 1e-06f; + float fDiscr = afCoeff[2] * afCoeff[2] - 3.0f * afCoeff[1]; + + if ( fDiscr <= fEpsilon ) + return -fOneThird*afCoeff[2]; + + // Compute an upper bound on roots of P(x). This assumes that A^T*A + // has been scaled by its largest entry. + float fX = 1.0f; + + float fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX)); + + if ( fPoly < 0.0f ) { + // uses a matrix norm to find an upper bound on maximum root + fX = G3D::abs(afCoeff[0]); + float fTmp = 1.0 + G3D::abs(afCoeff[1]); + + if ( fTmp > fX ) + fX = fTmp; + + fTmp = 1.0 + G3D::abs(afCoeff[2]); + + if ( fTmp > fX ) + fX = fTmp; + } + + // Newton's method to find root + float fTwoC2 = 2.0f * afCoeff[2]; + + for (int i = 0; i < 16; i++) { + fPoly = afCoeff[0] + fX * (afCoeff[1] + fX * (afCoeff[2] + fX)); + + if ( G3D::abs(fPoly) <= fEpsilon ) + return fX; + + float fDeriv = afCoeff[1] + fX * (fTwoC2 + 3.0f * fX); + + fX -= fPoly / fDeriv; + } + + return fX; +} + +//---------------------------------------------------------------------------- +float Matrix3::spectralNorm () const { + Matrix3 kP; + int iRow, iCol; + float fPmax = 0.0; + + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) { + kP[iRow][iCol] = 0.0; + + for (int iMid = 0; iMid < 3; iMid++) { + kP[iRow][iCol] += + elt[iMid][iRow] * elt[iMid][iCol]; + } + + if ( kP[iRow][iCol] > fPmax ) + fPmax = kP[iRow][iCol]; + } + } + + float fInvPmax = 1.0 / fPmax; + + for (iRow = 0; iRow < 3; iRow++) { + for (iCol = 0; iCol < 3; iCol++) + kP[iRow][iCol] *= fInvPmax; + } + + float afCoeff[3]; + afCoeff[0] = -(kP[0][0] * (kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1]) + + kP[0][1] * (kP[2][0] * kP[1][2] - kP[1][0] * kP[2][2]) + + kP[0][2] * (kP[1][0] * kP[2][1] - kP[2][0] * kP[1][1])); + afCoeff[1] = kP[0][0] * kP[1][1] - kP[0][1] * kP[1][0] + + kP[0][0] * kP[2][2] - kP[0][2] * kP[2][0] + + kP[1][1] * kP[2][2] - kP[1][2] * kP[2][1]; + afCoeff[2] = -(kP[0][0] + kP[1][1] + kP[2][2]); + + float fRoot = maxCubicRoot(afCoeff); + float fNorm = sqrt(fPmax * fRoot); + return fNorm; +} + +//---------------------------------------------------------------------------- +float Matrix3::squaredFrobeniusNorm() const { + float norm2 = 0; + const float* e = &elt[0][0]; + + for (int i = 0; i < 9; ++i){ + norm2 += (*e) * (*e); + } + + return norm2; +} + +//---------------------------------------------------------------------------- +float Matrix3::frobeniusNorm() const { + return sqrtf(squaredFrobeniusNorm()); +} + +//---------------------------------------------------------------------------- +float Matrix3::l1Norm() const { + // The one norm of a matrix is the max column sum in absolute value. + float oneNorm = 0; + for (int c = 0; c < 3; ++c) { + + float f = fabs(elt[0][c])+ fabs(elt[1][c]) + fabs(elt[2][c]); + + if (f > oneNorm) { + oneNorm = f; + } + } + return oneNorm; +} + +//---------------------------------------------------------------------------- +float Matrix3::lInfNorm() const { + // The infinity norm of a matrix is the max row sum in absolute value. + float infNorm = 0; + + for (int r = 0; r < 3; ++r) { + + float f = fabs(elt[r][0]) + fabs(elt[r][1])+ fabs(elt[r][2]); + + if (f > infNorm) { + infNorm = f; + } + } + return infNorm; +} + +//---------------------------------------------------------------------------- +float Matrix3::diffOneNorm(const Matrix3 &y) const{ + float oneNorm = 0; + + for (int c = 0; c < 3; ++c){ + + float f = fabs(elt[0][c] - y[0][c]) + fabs(elt[1][c] - y[1][c]) + + fabs(elt[2][c] - y[2][c]); + + if (f > oneNorm) { + oneNorm = f; + } + } + return oneNorm; +} + +//---------------------------------------------------------------------------- +void Matrix3::toAxisAngle (Vector3& rkAxis, float& rfRadians) const { + // + // Let (x,y,z) be the unit-length axis and let A be an angle of rotation. + // The rotation matrix is R = I + sin(A)*P + (1-cos(A))*P^2 (Rodrigues' formula) where + // I is the identity and + // + // +- -+ + // P = | 0 -z +y | + // | +z 0 -x | + // | -y +x 0 | + // +- -+ + // + // If A > 0, R represents a counterclockwise rotation about the axis in + // the sense of looking from the tip of the axis vector towards the + // origin. Some algebra will show that + // + // cos(A) = (trace(R)-1)/2 and R - R^t = 2*sin(A)*P + // + // In the event that A = pi, R-R^t = 0 which prevents us from extracting + // the axis through P. Instead note that R = I+2*P^2 when A = pi, so + // P^2 = (R-I)/2. The diagonal entries of P^2 are x^2-1, y^2-1, and + // z^2-1. We can solve these for axis (x,y,z). Because the angle is pi, + // it does not matter which sign you choose on the square roots. + + float fTrace = elt[0][0] + elt[1][1] + elt[2][2]; + float fCos = 0.5f * (fTrace - 1.0f); + rfRadians = G3D::aCos(fCos); // in [0,PI] + + if ( rfRadians > 0.0 ) { + if ( rfRadians < pi() ) { + rkAxis.x = elt[2][1] - elt[1][2]; + rkAxis.y = elt[0][2] - elt[2][0]; + rkAxis.z = elt[1][0] - elt[0][1]; + rkAxis.unitize(); + } else { + // angle is PI + float fHalfInverse; + + if ( elt[0][0] >= elt[1][1] ) { + // r00 >= r11 + if ( elt[0][0] >= elt[2][2] ) { + // r00 is maximum diagonal term + rkAxis.x = 0.5 * sqrt(elt[0][0] - + elt[1][1] - elt[2][2] + 1.0); + fHalfInverse = 0.5 / rkAxis.x; + rkAxis.y = fHalfInverse * elt[0][1]; + rkAxis.z = fHalfInverse * elt[0][2]; + } else { + // r22 is maximum diagonal term + rkAxis.z = 0.5 * sqrt(elt[2][2] - + elt[0][0] - elt[1][1] + 1.0); + fHalfInverse = 0.5 / rkAxis.z; + rkAxis.x = fHalfInverse * elt[0][2]; + rkAxis.y = fHalfInverse * elt[1][2]; + } + } else { + // r11 > r00 + if ( elt[1][1] >= elt[2][2] ) { + // r11 is maximum diagonal term + rkAxis.y = 0.5 * sqrt(elt[1][1] - + elt[0][0] - elt[2][2] + 1.0); + fHalfInverse = 0.5 / rkAxis.y; + rkAxis.x = fHalfInverse * elt[0][1]; + rkAxis.z = fHalfInverse * elt[1][2]; + } else { + // r22 is maximum diagonal term + rkAxis.z = 0.5 * sqrt(elt[2][2] - + elt[0][0] - elt[1][1] + 1.0); + fHalfInverse = 0.5 / rkAxis.z; + rkAxis.x = fHalfInverse * elt[0][2]; + rkAxis.y = fHalfInverse * elt[1][2]; + } + } + } + } else { + // The angle is 0 and the matrix is the identity. Any axis will + // work, so just use the x-axis. + rkAxis.x = 1.0; + rkAxis.y = 0.0; + rkAxis.z = 0.0; + } +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromAxisAngle (const Vector3& _axis, float fRadians) { + Vector3 axis = _axis.direction(); + + Matrix3 m; + float fCos = cos(fRadians); + float fSin = sin(fRadians); + float fOneMinusCos = 1.0 - fCos; + float fX2 = square(axis.x); + float fY2 = square(axis.y); + float fZ2 = square(axis.z); + float fXYM = axis.x * axis.y * fOneMinusCos; + float fXZM = axis.x * axis.z * fOneMinusCos; + float fYZM = axis.y * axis.z * fOneMinusCos; + float fXSin = axis.x * fSin; + float fYSin = axis.y * fSin; + float fZSin = axis.z * fSin; + + m.elt[0][0] = fX2 * fOneMinusCos + fCos; + m.elt[0][1] = fXYM - fZSin; + m.elt[0][2] = fXZM + fYSin; + + m.elt[1][0] = fXYM + fZSin; + m.elt[1][1] = fY2 * fOneMinusCos + fCos; + m.elt[1][2] = fYZM - fXSin; + + m.elt[2][0] = fXZM - fYSin; + m.elt[2][1] = fYZM + fXSin; + m.elt[2][2] = fZ2 * fOneMinusCos + fCos; + + return m; +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesXYZ (float& rfXAngle, float& rfYAngle, + float& rfZAngle) const { + // rot = cy*cz -cy*sz sy + // cz*sx*sy+cx*sz cx*cz-sx*sy*sz -cy*sx + // -cx*cz*sy+sx*sz cz*sx+cx*sy*sz cx*cy + + if ( elt[0][2] < 1.0f ) { + if ( elt[0][2] > -1.0f ) { + rfXAngle = G3D::aTan2( -elt[1][2], elt[2][2]); + rfYAngle = (float) G3D::aSin(elt[0][2]); + rfZAngle = G3D::aTan2( -elt[0][1], elt[0][0]); + return true; + } else { + // WARNING. Not unique. XA - ZA = -atan2(r10,r11) + rfXAngle = -G3D::aTan2(elt[1][0], elt[1][1]); + rfYAngle = -(float)halfPi(); + rfZAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. XAngle + ZAngle = atan2(r10,r11) + rfXAngle = G3D::aTan2(elt[1][0], elt[1][1]); + rfYAngle = (float)halfPi(); + rfZAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesXZY (float& rfXAngle, float& rfZAngle, + float& rfYAngle) const { + // rot = cy*cz -sz cz*sy + // sx*sy+cx*cy*sz cx*cz -cy*sx+cx*sy*sz + // -cx*sy+cy*sx*sz cz*sx cx*cy+sx*sy*sz + + if ( elt[0][1] < 1.0f ) { + if ( elt[0][1] > -1.0f ) { + rfXAngle = G3D::aTan2(elt[2][1], elt[1][1]); + rfZAngle = (float) asin( -elt[0][1]); + rfYAngle = G3D::aTan2(elt[0][2], elt[0][0]); + return true; + } else { + // WARNING. Not unique. XA - YA = atan2(r20,r22) + rfXAngle = G3D::aTan2(elt[2][0], elt[2][2]); + rfZAngle = (float)halfPi(); + rfYAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. XA + YA = atan2(-r20,r22) + rfXAngle = G3D::aTan2( -elt[2][0], elt[2][2]); + rfZAngle = -(float)halfPi(); + rfYAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesYXZ (float& rfYAngle, float& rfXAngle, + float& rfZAngle) const { + // rot = cy*cz+sx*sy*sz cz*sx*sy-cy*sz cx*sy + // cx*sz cx*cz -sx + // -cz*sy+cy*sx*sz cy*cz*sx+sy*sz cx*cy + + if ( elt[1][2] < 1.0 ) { + if ( elt[1][2] > -1.0 ) { + rfYAngle = G3D::aTan2(elt[0][2], elt[2][2]); + rfXAngle = (float) asin( -elt[1][2]); + rfZAngle = G3D::aTan2(elt[1][0], elt[1][1]); + return true; + } else { + // WARNING. Not unique. YA - ZA = atan2(r01,r00) + rfYAngle = G3D::aTan2(elt[0][1], elt[0][0]); + rfXAngle = (float)halfPi(); + rfZAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. YA + ZA = atan2(-r01,r00) + rfYAngle = G3D::aTan2( -elt[0][1], elt[0][0]); + rfXAngle = -(float)halfPi(); + rfZAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesYZX (float& rfYAngle, float& rfZAngle, + float& rfXAngle) const { + // rot = cy*cz sx*sy-cx*cy*sz cx*sy+cy*sx*sz + // sz cx*cz -cz*sx + // -cz*sy cy*sx+cx*sy*sz cx*cy-sx*sy*sz + + if ( elt[1][0] < 1.0 ) { + if ( elt[1][0] > -1.0 ) { + rfYAngle = G3D::aTan2( -elt[2][0], elt[0][0]); + rfZAngle = (float) asin(elt[1][0]); + rfXAngle = G3D::aTan2( -elt[1][2], elt[1][1]); + return true; + } else { + // WARNING. Not unique. YA - XA = -atan2(r21,r22); + rfYAngle = -G3D::aTan2(elt[2][1], elt[2][2]); + rfZAngle = -(float)halfPi(); + rfXAngle = 0.0; + return false; + } + } else { + // WARNING. Not unique. YA + XA = atan2(r21,r22) + rfYAngle = G3D::aTan2(elt[2][1], elt[2][2]); + rfZAngle = (float)halfPi(); + rfXAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesZXY (float& rfZAngle, float& rfXAngle, + float& rfYAngle) const { + // rot = cy*cz-sx*sy*sz -cx*sz cz*sy+cy*sx*sz + // cz*sx*sy+cy*sz cx*cz -cy*cz*sx+sy*sz + // -cx*sy sx cx*cy + + if ( elt[2][1] < 1.0 ) { + if ( elt[2][1] > -1.0 ) { + rfZAngle = G3D::aTan2( -elt[0][1], elt[1][1]); + rfXAngle = (float) asin(elt[2][1]); + rfYAngle = G3D::aTan2( -elt[2][0], elt[2][2]); + return true; + } else { + // WARNING. Not unique. ZA - YA = -atan(r02,r00) + rfZAngle = -G3D::aTan2(elt[0][2], elt[0][0]); + rfXAngle = -(float)halfPi(); + rfYAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. ZA + YA = atan2(r02,r00) + rfZAngle = G3D::aTan2(elt[0][2], elt[0][0]); + rfXAngle = (float)halfPi(); + rfYAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::toEulerAnglesZYX (float& rfZAngle, float& rfYAngle, + float& rfXAngle) const { + // rot = cy*cz cz*sx*sy-cx*sz cx*cz*sy+sx*sz + // cy*sz cx*cz+sx*sy*sz -cz*sx+cx*sy*sz + // -sy cy*sx cx*cy + + if ( elt[2][0] < 1.0 ) { + if ( elt[2][0] > -1.0 ) { + rfZAngle = atan2f(elt[1][0], elt[0][0]); + rfYAngle = asinf(-(double)elt[2][1]); + rfXAngle = atan2f(elt[2][1], elt[2][2]); + return true; + } else { + // WARNING. Not unique. ZA - XA = -atan2(r01,r02) + rfZAngle = -G3D::aTan2(elt[0][1], elt[0][2]); + rfYAngle = (float)halfPi(); + rfXAngle = 0.0f; + return false; + } + } else { + // WARNING. Not unique. ZA + XA = atan2(-r01,-r02) + rfZAngle = G3D::aTan2( -elt[0][1], -elt[0][2]); + rfYAngle = -(float)halfPi(); + rfXAngle = 0.0f; + return false; + } +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesXYZ (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cosf(fYAngle); + fSin = sinf(fYAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cosf(fPAngle); + fSin = sinf(fPAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cosf(fRAngle); + fSin = sinf(fRAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + return kXMat * (kYMat * kZMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesXZY (float fYAngle, float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cosf(fYAngle); + fSin = sinf(fYAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cosf(fPAngle); + fSin = sinf(fPAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cosf(fRAngle); + fSin = sinf(fRAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + return kXMat * (kZMat * kYMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesYXZ( + float fYAngle, + float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + return kYMat * (kXMat * kZMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesYZX( + float fYAngle, + float fPAngle, + float fRAngle) { + + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kYMat(fCos, 0.0f, fSin, 0.0f, 1.0f, 0.0f, -fSin, 0.0f, fCos); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kZMat(fCos, -fSin, 0.0f, fSin, fCos, 0.0f, 0.0f, 0.0f, 1.0f); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kXMat(1.0f, 0.0f, 0.0f, 0.0f, fCos, -fSin, 0.0f, fSin, fCos); + + return kYMat * (kZMat * kXMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesZXY (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + return kZMat * (kXMat * kYMat); +} + +//---------------------------------------------------------------------------- +Matrix3 Matrix3::fromEulerAnglesZYX (float fYAngle, float fPAngle, + float fRAngle) { + float fCos, fSin; + + fCos = cos(fYAngle); + fSin = sin(fYAngle); + Matrix3 kZMat(fCos, -fSin, 0.0, fSin, fCos, 0.0, 0.0, 0.0, 1.0); + + fCos = cos(fPAngle); + fSin = sin(fPAngle); + Matrix3 kYMat(fCos, 0.0, fSin, 0.0, 1.0, 0.0, -fSin, 0.0, fCos); + + fCos = cos(fRAngle); + fSin = sin(fRAngle); + Matrix3 kXMat(1.0, 0.0, 0.0, 0.0, fCos, -fSin, 0.0, fSin, fCos); + + return kZMat * (kYMat * kXMat); +} + +//---------------------------------------------------------------------------- +void Matrix3::tridiagonal (float afDiag[3], float afSubDiag[3]) { + // Householder reduction T = Q^t M Q + // Input: + // mat, symmetric 3x3 matrix M + // Output: + // mat, orthogonal matrix Q + // diag, diagonal entries of T + // subd, subdiagonal entries of T (T is symmetric) + + float fA = elt[0][0]; + float fB = elt[0][1]; + float fC = elt[0][2]; + float fD = elt[1][1]; + float fE = elt[1][2]; + float fF = elt[2][2]; + + afDiag[0] = fA; + afSubDiag[2] = 0.0; + + if ( G3D::abs(fC) >= EPSILON ) { + float fLength = sqrt(fB * fB + fC * fC); + float fInvLength = 1.0 / fLength; + fB *= fInvLength; + fC *= fInvLength; + float fQ = 2.0 * fB * fE + fC * (fF - fD); + afDiag[1] = fD + fC * fQ; + afDiag[2] = fF - fC * fQ; + afSubDiag[0] = fLength; + afSubDiag[1] = fE - fB * fQ; + elt[0][0] = 1.0; + elt[0][1] = 0.0; + elt[0][2] = 0.0; + elt[1][0] = 0.0; + elt[1][1] = fB; + elt[1][2] = fC; + elt[2][0] = 0.0; + elt[2][1] = fC; + elt[2][2] = -fB; + } else { + afDiag[1] = fD; + afDiag[2] = fF; + afSubDiag[0] = fB; + afSubDiag[1] = fE; + elt[0][0] = 1.0; + elt[0][1] = 0.0; + elt[0][2] = 0.0; + elt[1][0] = 0.0; + elt[1][1] = 1.0; + elt[1][2] = 0.0; + elt[2][0] = 0.0; + elt[2][1] = 0.0; + elt[2][2] = 1.0; + } +} + +//---------------------------------------------------------------------------- +bool Matrix3::qLAlgorithm (float afDiag[3], float afSubDiag[3]) { + // QL iteration with implicit shifting to reduce matrix from tridiagonal + // to diagonal + + for (int i0 = 0; i0 < 3; i0++) { + const int iMaxIter = 32; + int iIter; + + for (iIter = 0; iIter < iMaxIter; iIter++) { + int i1; + + for (i1 = i0; i1 <= 1; i1++) { + float fSum = G3D::abs(afDiag[i1]) + + G3D::abs(afDiag[i1 + 1]); + + if ( G3D::abs(afSubDiag[i1]) + fSum == fSum ) + break; + } + + if ( i1 == i0 ) + break; + + float fTmp0 = (afDiag[i0 + 1] - afDiag[i0]) / (2.0 * afSubDiag[i0]); + + float fTmp1 = sqrt(fTmp0 * fTmp0 + 1.0); + + if ( fTmp0 < 0.0 ) + fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 - fTmp1); + else + fTmp0 = afDiag[i1] - afDiag[i0] + afSubDiag[i0] / (fTmp0 + fTmp1); + + float fSin = 1.0; + + float fCos = 1.0; + + float fTmp2 = 0.0; + + for (int i2 = i1 - 1; i2 >= i0; i2--) { + float fTmp3 = fSin * afSubDiag[i2]; + float fTmp4 = fCos * afSubDiag[i2]; + + if (G3D::abs(fTmp3) >= G3D::abs(fTmp0)) { + fCos = fTmp0 / fTmp3; + fTmp1 = sqrt(fCos * fCos + 1.0); + afSubDiag[i2 + 1] = fTmp3 * fTmp1; + fSin = 1.0 / fTmp1; + fCos *= fSin; + } else { + fSin = fTmp3 / fTmp0; + fTmp1 = sqrt(fSin * fSin + 1.0); + afSubDiag[i2 + 1] = fTmp0 * fTmp1; + fCos = 1.0 / fTmp1; + fSin *= fCos; + } + + fTmp0 = afDiag[i2 + 1] - fTmp2; + fTmp1 = (afDiag[i2] - fTmp0) * fSin + 2.0 * fTmp4 * fCos; + fTmp2 = fSin * fTmp1; + afDiag[i2 + 1] = fTmp0 + fTmp2; + fTmp0 = fCos * fTmp1 - fTmp4; + + for (int iRow = 0; iRow < 3; iRow++) { + fTmp3 = elt[iRow][i2 + 1]; + elt[iRow][i2 + 1] = fSin * elt[iRow][i2] + + fCos * fTmp3; + elt[iRow][i2] = fCos * elt[iRow][i2] - + fSin * fTmp3; + } + } + + afDiag[i0] -= fTmp2; + afSubDiag[i0] = fTmp0; + afSubDiag[i1] = 0.0; + } + + if ( iIter == iMaxIter ) { + // should not get here under normal circumstances + return false; + } + } + + return true; +} + +//---------------------------------------------------------------------------- +void Matrix3::eigenSolveSymmetric (float afEigenvalue[3], + Vector3 akEigenvector[3]) const { + Matrix3 kMatrix = *this; + float afSubDiag[3]; + kMatrix.tridiagonal(afEigenvalue, afSubDiag); + kMatrix.qLAlgorithm(afEigenvalue, afSubDiag); + + for (int i = 0; i < 3; i++) { + akEigenvector[i][0] = kMatrix[0][i]; + akEigenvector[i][1] = kMatrix[1][i]; + akEigenvector[i][2] = kMatrix[2][i]; + } + + // make eigenvectors form a right--handed system + Vector3 kCross = akEigenvector[1].cross(akEigenvector[2]); + + float fDet = akEigenvector[0].dot(kCross); + + if ( fDet < 0.0 ) { + akEigenvector[2][0] = - akEigenvector[2][0]; + akEigenvector[2][1] = - akEigenvector[2][1]; + akEigenvector[2][2] = - akEigenvector[2][2]; + } +} + +//---------------------------------------------------------------------------- +void Matrix3::tensorProduct (const Vector3& rkU, const Vector3& rkV, + Matrix3& rkProduct) { + for (int iRow = 0; iRow < 3; iRow++) { + for (int iCol = 0; iCol < 3; iCol++) { + rkProduct[iRow][iCol] = rkU[iRow] * rkV[iCol]; + } + } +} + +//---------------------------------------------------------------------------- + +// Runs in 52 cycles on AMD, 76 cycles on Intel Centrino +// +// The loop unrolling is necessary for performance. +// I was unable to improve performance further by flattening the matrices +// into float*'s instead of 2D arrays. +// +// -morgan +void Matrix3::_mul(const Matrix3& A, const Matrix3& B, Matrix3& out) { + const float* ARowPtr = A.elt[0]; + float* outRowPtr = out.elt[0]; + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; + + ARowPtr = A.elt[1]; + outRowPtr = out.elt[1]; + + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; + + ARowPtr = A.elt[2]; + outRowPtr = out.elt[2]; + + outRowPtr[0] = + ARowPtr[0] * B.elt[0][0] + + ARowPtr[1] * B.elt[1][0] + + ARowPtr[2] * B.elt[2][0]; + outRowPtr[1] = + ARowPtr[0] * B.elt[0][1] + + ARowPtr[1] * B.elt[1][1] + + ARowPtr[2] * B.elt[2][1]; + outRowPtr[2] = + ARowPtr[0] * B.elt[0][2] + + ARowPtr[1] * B.elt[1][2] + + ARowPtr[2] * B.elt[2][2]; +} + +//---------------------------------------------------------------------------- +void Matrix3::_transpose(const Matrix3& A, Matrix3& out) { + out[0][0] = A.elt[0][0]; + out[0][1] = A.elt[1][0]; + out[0][2] = A.elt[2][0]; + out[1][0] = A.elt[0][1]; + out[1][1] = A.elt[1][1]; + out[1][2] = A.elt[2][1]; + out[2][0] = A.elt[0][2]; + out[2][1] = A.elt[1][2]; + out[2][2] = A.elt[2][2]; +} + +//----------------------------------------------------------------------------- +std::string Matrix3::toString() const { + return G3D::format("[%g, %g, %g; %g, %g, %g; %g, %g, %g]", + elt[0][0], elt[0][1], elt[0][2], + elt[1][0], elt[1][1], elt[1][2], + elt[2][0], elt[2][1], elt[2][2]); +} + + + +} // namespace + diff --git a/externals/g3dlite/Matrix4.cpp b/externals/g3dlite/Matrix4.cpp new file mode 100644 index 00000000000..cd38a1a3602 --- /dev/null +++ b/externals/g3dlite/Matrix4.cpp @@ -0,0 +1,523 @@ +/** + @file Matrix4.cpp + + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-10-02 + @edited 2010-01-29 + */ + +#include "G3D/platform.h" +#include "G3D/Matrix4.h" +#include "G3D/Matrix3.h" +#include "G3D/Vector4.h" +#include "G3D/Vector3.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/CoordinateFrame.h" +#include "G3D/Rect2D.h" +#include "G3D/Any.h" +#include "G3D/stringutils.h" + +namespace G3D { + + +Matrix4::Matrix4(const Any& any) { + any.verifyName("Matrix4"); + any.verifyType(Any::ARRAY); + + const std::string& name = toLower(any.name()); + if (name == "matrix4") { + any.verifySize(16); + + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = any[r * 4 + c]; + } + } + } else if (name == "matrix4::scale") { + if (any.size() == 1) { + *this = scale(any[0].number()); + } else if (any.size() == 3) { + *this = scale(any[0], any[1], any[2]); + } else { + any.verify(false, "Matrix4::scale() takes either 1 or 3 arguments"); + } + } else { + any.verify(false, "Expected Matrix4 constructor"); + } +} + + +Matrix4::operator Any() const { + Any any(Any::ARRAY, "Matrix4"); + any.resize(16); + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + any[r * 4 + c] = elt[r][c]; + } + } + + return any; +} + +const Matrix4& Matrix4::identity() { + static Matrix4 m( + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + return m; +} + + +const Matrix4& Matrix4::zero() { + static Matrix4 m( + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0); + return m; +} + + +Matrix4::Matrix4(const class CoordinateFrame& cframe) { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + elt[r][c] = cframe.rotation[r][c]; + } + elt[r][3] = cframe.translation[r]; + } + elt[3][0] = 0.0f; + elt[3][1] = 0.0f; + elt[3][2] = 0.0f; + elt[3][3] = 1.0f; +} + +Matrix4::Matrix4(const Matrix3& upper3x3, const Vector3& lastCol) { + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + elt[r][c] = upper3x3[r][c]; + } + elt[r][3] = lastCol[r]; + } + elt[3][0] = 0.0f; + elt[3][1] = 0.0f; + elt[3][2] = 0.0f; + elt[3][3] = 1.0f; +} + + +Matrix3 Matrix4::upper3x3() const { + return Matrix3(elt[0][0], elt[0][1], elt[0][2], + elt[1][0], elt[1][1], elt[1][2], + elt[2][0], elt[2][1], elt[2][2]); +} + + +Matrix4 Matrix4::orthogonalProjection( + const class Rect2D& rect, + float nearval, + float farval, + float upDirection) { + return Matrix4::orthogonalProjection(rect.x0(), rect.x1(), rect.y1(), rect.y0(), nearval, farval, upDirection); +} + + +Matrix4 Matrix4::orthogonalProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval, + float upDirection) { + + // Adapted from Mesa. Note that Microsoft (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/opengl/glfunc03_8qnj.asp) + // and Linux (http://www.xfree86.org/current/glOrtho.3.html) have different matrices shown in their documentation. + + float x, y, z; + float tx, ty, tz; + + x = 2.0f / (right-left); + y = 2.0f / (top-bottom); + z = -2.0f / (farval-nearval); + tx = -(right+left) / (right-left); + ty = -(top+bottom) / (top-bottom); + tz = -(farval+nearval) / (farval-nearval); + + y *= upDirection; + ty *= upDirection; + + return + Matrix4( x , 0.0f, 0.0f, tx, + 0.0f, y , 0.0f, ty, + 0.0f, 0.0f, z , tz, + 0.0f, 0.0f, 0.0f, 1.0f); +} + + +Matrix4 Matrix4::perspectiveProjection( + float left, + float right, + float bottom, + float top, + float nearval, + float farval, + float upDirection) { + + float x, y, a, b, c, d; + + x = (2.0f*nearval) / (right-left); + y = (2.0f*nearval) / (top-bottom); + a = (right+left) / (right-left); + b = (top+bottom) / (top-bottom); + + if (farval >= finf()) { + // Infinite view frustum + c = -1.0f; + d = -2.0f * nearval; + } else { + c = -(farval+nearval) / (farval-nearval); + d = -(2.0f*farval*nearval) / (farval-nearval); + } + + debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1"); + y *= upDirection; + b *= upDirection; + + return Matrix4( + x, 0, a, 0, + 0, y, b, 0, + 0, 0, c, d, + 0, 0, -1, 0); +} + + +void Matrix4::getPerspectiveProjectionParameters( + float& left, + float& right, + float& bottom, + float& top, + float& nearval, + float& farval, + float upDirection) const { + + debugAssertM(abs(upDirection) == 1.0f, "upDirection must be -1 or +1"); + + float x = elt[0][0]; + float y = elt[1][1] * upDirection; + float a = elt[0][2]; + float b = elt[1][2] * upDirection; + float c = elt[2][2]; + float d = elt[2][3]; + + // Verify that this really is a projection matrix + debugAssertM(elt[3][2] == -1, "Not a projection matrix"); + debugAssertM(elt[0][1] == 0, "Not a projection matrix"); + debugAssertM(elt[0][3] == 0, "Not a projection matrix"); + debugAssertM(elt[1][3] == 0, "Not a projection matrix"); + debugAssertM(elt[3][3] == 0, "Not a projection matrix"); + debugAssertM(elt[1][0] == 0, "Not a projection matrix"); + debugAssertM(elt[2][0] == 0, "Not a projection matrix"); + debugAssertM(elt[2][1] == 0, "Not a projection matrix"); + debugAssertM(elt[3][0] == 0, "Not a projection matrix"); + debugAssertM(elt[3][1] == 0, "Not a projection matrix"); + + if (c == -1) { + farval = finf(); + nearval = -d / 2.0f; + } else { + nearval = d * ((c - 1.0f) / (c + 1.0f) - 1.0f) / (-2.0f * (c - 1.0f) / (c + 1.0f)); + farval = nearval * ((c - 1.0f) / (c + 1.0f)); + } + + + left = (a - 1.0f) * nearval / x; + right = 2.0f * nearval / x + left; + + bottom = (b - 1.0f) * nearval / y; + top = 2.0f * nearval / y + bottom; +} + + +Matrix4::Matrix4( + float r1c1, float r1c2, float r1c3, float r1c4, + float r2c1, float r2c2, float r2c3, float r2c4, + float r3c1, float r3c2, float r3c3, float r3c4, + float r4c1, float r4c2, float r4c3, float r4c4) { + elt[0][0] = r1c1; elt[0][1] = r1c2; elt[0][2] = r1c3; elt[0][3] = r1c4; + elt[1][0] = r2c1; elt[1][1] = r2c2; elt[1][2] = r2c3; elt[1][3] = r2c4; + elt[2][0] = r3c1; elt[2][1] = r3c2; elt[2][2] = r3c3; elt[2][3] = r3c4; + elt[3][0] = r4c1; elt[3][1] = r4c2; elt[3][2] = r4c3; elt[3][3] = r4c4; +} + +/** + init should be <B>row major</B>. + */ +Matrix4::Matrix4(const float* init) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = init[r * 4 + c]; + } + } +} + + +Matrix4::Matrix4(const double* init) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = (float)init[r * 4 + c]; + } + } +} + + +Matrix4::Matrix4() { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = 0; + } + } +} + + +void Matrix4::setRow(int r, const Vector4& v) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = v[c]; + } +} + + +void Matrix4::setColumn(int c, const Vector4& v) { + for (int r = 0; r < 4; ++r) { + elt[r][c] = v[r]; + } +} + + +const Vector4& Matrix4::row(int r) const { + return reinterpret_cast<const Vector4*>(elt[r])[0]; +} + + +Vector4 Matrix4::column(int c) const { + Vector4 v; + for (int r = 0; r < 4; ++r) { + v[r] = elt[r][c]; + } + return v; +} + + +Matrix4 Matrix4::operator*(const Matrix4& other) const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + for (int i = 0; i < 4; ++i) { + result.elt[r][c] += elt[r][i] * other.elt[i][c]; + } + } + } + + return result; +} + + +Matrix4 Matrix4::operator*(const float s) const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result.elt[r][c] = elt[r][c] * s; + } + } + + return result; +} + + +Vector3 Matrix4::homoMul(const class Vector3& v, float w) const { + Vector4 r = (*this) * Vector4(v, w); + return r.xyz() * (1.0f / r.w); +} + + +Vector4 Matrix4::operator*(const Vector4& vector) const { + Vector4 result(0,0,0,0); + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result[r] += elt[r][c] * vector[c]; + } + } + + return result; +} + + +Matrix4 Matrix4::transpose() const { + Matrix4 result; + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + result.elt[c][r] = elt[r][c]; + } + } + + return result; +} + + +bool Matrix4::operator!=(const Matrix4& other) const { + return ! (*this == other); +} + + +bool Matrix4::operator==(const Matrix4& other) const { + + // If the bit patterns are identical, they must be + // the same matrix. If not, they *might* still have + // equal elements due to floating point weirdness. + if (memcmp(this, &other, sizeof(Matrix4) == 0)) { + return true; + } + + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + if (elt[r][c] != other.elt[r][c]) { + return false; + } + } + } + + return true; +} + + +float Matrix4::determinant() const { + // Determinant is the dot product of the first row and the first row + // of cofactors (i.e. the first col of the adjoint matrix) + return cofactor().row(0).dot(row(0)); +} + + +Matrix4 Matrix4::adjoint() const { + return cofactor().transpose(); +} + + +Matrix4 Matrix4::inverse() const { + // Inverse = adjoint / determinant + + Matrix4 A = adjoint(); + + // Determinant is the dot product of the first row and the first row + // of cofactors (i.e. the first col of the adjoint matrix) + float det = A.column(0).dot(row(0)); + + return A * (1.0f / det); +} + + +Matrix4 Matrix4::cofactor() const { + Matrix4 out; + + // We'll use i to incrementally compute -1 ^ (r+c) + int i = 1; + + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + // Compute the determinant of the 3x3 submatrix + float det = subDeterminant(r, c); + out.elt[r][c] = i * det; + i = -i; + } + i = -i; + } + + return out; +} + + +float Matrix4::subDeterminant(int excludeRow, int excludeCol) const { + // Compute non-excluded row and column indices + int row[3]; + int col[3]; + + for (int i = 0; i < 3; ++i) { + row[i] = i; + col[i] = i; + + if (i >= excludeRow) { + ++row[i]; + } + if (i >= excludeCol) { + ++col[i]; + } + } + + // Compute the first row of cofactors + float cofactor00 = + elt[row[1]][col[1]] * elt[row[2]][col[2]] - + elt[row[1]][col[2]] * elt[row[2]][col[1]]; + + float cofactor10 = + elt[row[1]][col[2]] * elt[row[2]][col[0]] - + elt[row[1]][col[0]] * elt[row[2]][col[2]]; + + float cofactor20 = + elt[row[1]][col[0]] * elt[row[2]][col[1]] - + elt[row[1]][col[1]] * elt[row[2]][col[0]]; + + // Product of the first row and the cofactors along the first row + return + elt[row[0]][col[0]] * cofactor00 + + elt[row[0]][col[1]] * cofactor10 + + elt[row[0]][col[2]] * cofactor20; +} + + +CoordinateFrame Matrix4::approxCoordinateFrame() const { + CoordinateFrame cframe; + + for (int r = 0; r < 3; ++r) { + for (int c = 0; c < 3; ++c) { + cframe.rotation[r][c] = elt[r][c]; + } + cframe.translation[r] = elt[r][3]; + } + + // Ensure that the rotation matrix is orthonormal + cframe.rotation.orthonormalize(); + + return cframe; +} + + +void Matrix4::serialize(class BinaryOutput& b) const { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + b.writeFloat32(elt[r][c]); + } + } +} + + +void Matrix4::deserialize(class BinaryInput& b) { + for (int r = 0; r < 4; ++r) { + for (int c = 0; c < 4; ++c) { + elt[r][c] = b.readFloat32(); + } + } +} + +std::string Matrix4::toString() const { + return G3D::format("[%g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g; %g, %g, %g, %g]", + elt[0][0], elt[0][1], elt[0][2], elt[0][3], + elt[1][0], elt[1][1], elt[1][2], elt[1][3], + elt[2][0], elt[2][1], elt[2][2], elt[2][3], + elt[3][0], elt[3][1], elt[3][2], elt[3][3]); +} + +} // namespace + + diff --git a/externals/g3dlite/MemoryManager.cpp b/externals/g3dlite/MemoryManager.cpp new file mode 100644 index 00000000000..240188a1f0e --- /dev/null +++ b/externals/g3dlite/MemoryManager.cpp @@ -0,0 +1,91 @@ +/** + @file MemoryManager.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + @created 2009-04-20 + @edited 2009-05-29 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/MemoryManager.h" +#include "G3D/System.h" + +namespace G3D { + +MemoryManager::MemoryManager() {} + + +void* MemoryManager::alloc(size_t s) { + return System::malloc(s); +} + + +void MemoryManager::free(void* ptr) { + System::free(ptr); +} + + +bool MemoryManager::isThreadsafe() const { + return true; +} + + +MemoryManager::Ref MemoryManager::create() { + static MemoryManager::Ref m = new MemoryManager(); + return m; +} + + +/////////////////////////////////////////////////// + +AlignedMemoryManager::AlignedMemoryManager() {} + + +void* AlignedMemoryManager::alloc(size_t s) { + return System::alignedMalloc(s, 16); +} + + +void AlignedMemoryManager::free(void* ptr) { + System::alignedFree(ptr); +} + + +bool AlignedMemoryManager::isThreadsafe() const { + return true; +} + + +AlignedMemoryManager::Ref AlignedMemoryManager::create() { + static AlignedMemoryManager::Ref m = new AlignedMemoryManager(); + return m; +} + + +/////////////////////////////////////////////////// + +CRTMemoryManager::CRTMemoryManager() {} + + +void* CRTMemoryManager::alloc(size_t s) { + return ::malloc(s); +} + + +void CRTMemoryManager::free(void* ptr) { + return ::free(ptr); +} + + +bool CRTMemoryManager::isThreadsafe() const { + return true; +} + + +CRTMemoryManager::Ref CRTMemoryManager::create() { + static CRTMemoryManager::Ref m = new CRTMemoryManager(); + return m; +} +} diff --git a/externals/g3dlite/Plane.cpp b/externals/g3dlite/Plane.cpp new file mode 100644 index 00000000000..9b7991c0333 --- /dev/null +++ b/externals/g3dlite/Plane.cpp @@ -0,0 +1,149 @@ +/** + @file Plane.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2003-02-06 + @edited 2006-01-29 + */ + +#include "G3D/platform.h" +#include "G3D/Plane.h" +#include "G3D/BinaryOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/stringutils.h" + +namespace G3D { + +Plane::Plane(class BinaryInput& b) { + deserialize(b); +} + + +void Plane::serialize(class BinaryOutput& b) const { + _normal.serialize(b); + b.writeFloat64(_distance); +} + + +void Plane::deserialize(class BinaryInput& b) { + _normal.deserialize(b); + _distance = (float)b.readFloat64(); +} + + +Plane::Plane( + Vector4 point0, + Vector4 point1, + Vector4 point2) { + + debugAssertM( + point0.w != 0 || + point1.w != 0 || + point2.w != 0, + "At least one point must be finite."); + + // Rotate the points around so that the finite points come first. + + while ((point0.w == 0) && + ((point1.w == 0) || (point2.w != 0))) { + Vector4 temp = point0; + point0 = point1; + point1 = point2; + point2 = temp; + } + + Vector3 dir1; + Vector3 dir2; + + if (point1.w == 0) { + // 1 finite, 2 infinite points; the plane must contain + // the direction of the two direcitons + dir1 = point1.xyz(); + dir2 = point2.xyz(); + } else if (point2.w != 0) { + // 3 finite points, the plane must contain the directions + // betwseen the points. + dir1 = point1.xyz() - point0.xyz(); + dir2 = point2.xyz() - point0.xyz(); + } else { + // 2 finite, 1 infinite point; the plane must contain + // the direction between the first two points and the + // direction of the third point. + dir1 = point1.xyz() - point0.xyz(); + dir2 = point2.xyz(); + } + + _normal = dir1.cross(dir2).direction(); + _distance = _normal.dot(point0.xyz()); +} + + +Plane::Plane( + const Vector3& point0, + const Vector3& point1, + const Vector3& point2) { + + _normal = (point1 - point0).cross(point2 - point0).direction(); + _distance = _normal.dot(point0); +} + + +Plane::Plane( + const Vector3& __normal, + const Vector3& point) { + + _normal = __normal.direction(); + _distance = _normal.dot(point); +} + + +Plane Plane::fromEquation(float a, float b, float c, float d) { + Vector3 n(a, b, c); + float magnitude = n.magnitude(); + d /= magnitude; + n /= magnitude; + return Plane(n, -d); +} + + +void Plane::flip() { + _normal = -_normal; + _distance = -_distance; +} + + +void Plane::getEquation(Vector3& n, float& d) const { + double _d; + getEquation(n, _d); + d = (float)_d; +} + +void Plane::getEquation(Vector3& n, double& d) const { + n = _normal; + d = -_distance; +} + + +void Plane::getEquation(float& a, float& b, float& c, float& d) const { + double _a, _b, _c, _d; + getEquation(_a, _b, _c, _d); + a = (float)_a; + b = (float)_b; + c = (float)_c; + d = (float)_d; +} + +void Plane::getEquation(double& a, double& b, double& c, double& d) const { + a = _normal.x; + b = _normal.y; + c = _normal.z; + d = -_distance; +} + + +std::string Plane::toString() const { + return format("Plane(%g, %g, %g, %g)", _normal.x, _normal.y, _normal.z, _distance); +} + +} diff --git a/externals/g3dlite/Quat.cpp b/externals/g3dlite/Quat.cpp new file mode 100644 index 00000000000..225c5b51acc --- /dev/null +++ b/externals/g3dlite/Quat.cpp @@ -0,0 +1,583 @@ +/** + @file Quat.cpp + + Quaternion implementation based on Watt & Watt page 363 + + @author Morgan McGuire, graphics3d.com + + @created 2002-01-23 + @edited 2006-01-31 + */ + +#include "G3D/Quat.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +Quat Quat::fromAxisAngleRotation( + const Vector3& axis, + float angle) { + + Quat q; + + q.w = cos(angle / 2.0f); + q.imag() = axis.direction() * sin(angle / 2.0f); + + return q; +} + + +Quat::Quat( + const Matrix3& rot) { + + static const int plus1mod3[] = {1, 2, 0}; + + // Find the index of the largest diagonal component + // These ? operations hopefully compile to conditional + // move instructions instead of branches. + int i = (rot[1][1] > rot[0][0]) ? 1 : 0; + i = (rot[2][2] > rot[i][i]) ? 2 : i; + + // Find the indices of the other elements + int j = plus1mod3[i]; + int k = plus1mod3[j]; + + // Index the elements of the vector part of the quaternion as a float* + float* v = (float*)(this); + + // If we attempted to pre-normalize and trusted the matrix to be + // perfectly orthonormal, the result would be: + // + // double c = sqrt((rot[i][i] - (rot[j][j] + rot[k][k])) + 1.0) + // v[i] = -c * 0.5 + // v[j] = -(rot[i][j] + rot[j][i]) * 0.5 / c + // v[k] = -(rot[i][k] + rot[k][i]) * 0.5 / c + // w = (rot[j][k] - rot[k][j]) * 0.5 / c + // + // Since we're going to pay the sqrt anyway, we perform a post normalization, which also + // fixes any poorly normalized input. Multiply all elements by 2*c in the above, giving: + + // nc2 = -c^2 + double nc2 = ((rot[j][j] + rot[k][k]) - rot[i][i]) - 1.0; + v[i] = nc2; + w = (rot[j][k] - rot[k][j]); + v[j] = -(rot[i][j] + rot[j][i]); + v[k] = -(rot[i][k] + rot[k][i]); + + // We now have the correct result with the wrong magnitude, so normalize it: + float s = sqrt(x*x + y*y + z*z + w*w); + if (s > 0.00001f) { + s = 1.0f / s; + x *= s; + y *= s; + z *= s; + w *= s; + } else { + // The quaternion is nearly zero. Make it 0 0 0 1 + x = 0.0f; + y = 0.0f; + z = 0.0f; + w = 1.0f; + } +} + + +void Quat::toAxisAngleRotation( + Vector3& axis, + double& angle) const { + + // Decompose the quaternion into an angle and an axis. + + axis = Vector3(x, y, z); + angle = 2 * acos(w); + + float len = sqrt(1.0f - w * w); + + if (fuzzyGt(abs(len), 0.0f)) { + axis /= len; + } + + // Reduce the range of the angle. + + if (angle < 0) { + angle = -angle; + axis = -axis; + } + + while (angle > twoPi()) { + angle -= twoPi(); + } + + if (abs(angle) > pi()) { + angle -= twoPi(); + } + + // Make the angle positive. + + if (angle < 0.0f) { + angle = -angle; + axis = -axis; + } +} + + +Matrix3 Quat::toRotationMatrix() const { + Matrix3 out = Matrix3::zero(); + + toRotationMatrix(out); + + return out; +} + + +void Quat::toRotationMatrix( + Matrix3& rot) const { + + rot = Matrix3(*this); +} + + +Quat Quat::slerp( + const Quat& _quat1, + float alpha, + float threshold) const { + + // From: Game Physics -- David Eberly pg 538-540 + // Modified to include lerp for small angles, which + // is a common practice. + + // See also: + // http://number-none.com/product/Understanding%20Slerp,%20Then%20Not%20Using%20It/index.html + + const Quat& quat0 = *this; + Quat quat1 = _quat1; + + // angle between quaternion rotations + float phi; + float cosphi = quat0.dot(quat1); + + + if (cosphi < 0) { + // Change the sign and fix the dot product; we need to + // loop the other way to get the shortest path + quat1 = -quat1; + cosphi = -cosphi; + } + + // Using G3D::aCos will clamp the angle to 0 and pi + phi = static_cast<float>(G3D::aCos(cosphi)); + + if (phi >= threshold) { + // For large angles, slerp + float scale0, scale1; + + scale0 = sin((1.0f - alpha) * phi); + scale1 = sin(alpha * phi); + + return ( (quat0 * scale0) + (quat1 * scale1) ) / sin(phi); + } else { + // For small angles, linear interpolate + return quat0.nlerp(quat1, alpha); + } +} + + +Quat Quat::nlerp( + const Quat& quat1, + float alpha) const { + + Quat result = (*this) * (1.0f - alpha) + quat1 * alpha; + return result / result.magnitude(); +} + + +Quat Quat::operator*(const Quat& other) const { + + // Following Watt & Watt, page 360 + const Vector3& v1 = imag(); + const Vector3& v2 = other.imag(); + float s1 = w; + float s2 = other.w; + + return Quat(s1*v2 + s2*v1 + v1.cross(v2), s1*s2 - v1.dot(v2)); +} + + +// From "Uniform Random Rotations", Ken Shoemake, Graphics Gems III. +Quat Quat::unitRandom() { + float x0 = uniformRandom(); + float r1 = sqrtf(1 - x0), + r2 = sqrtf(x0); + float t1 = (float)G3D::twoPi() * uniformRandom(); + float t2 = (float)G3D::twoPi() * uniformRandom(); + float c1 = cosf(t1), + s1 = sinf(t1); + float c2 = cosf(t2), + s2 = sinf(t2); + return Quat(s1 * r1, c1 * r1, s2 * r2, c2 * r2); +} + + +void Quat::deserialize(class BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); + w = b.readFloat32(); +} + + +void Quat::serialize(class BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); + b.writeFloat32(w); +} + + +// 2-char swizzles + +Vector2 Quat::xx() const { return Vector2 (x, x); } +Vector2 Quat::yx() const { return Vector2 (y, x); } +Vector2 Quat::zx() const { return Vector2 (z, x); } +Vector2 Quat::wx() const { return Vector2 (w, x); } +Vector2 Quat::xy() const { return Vector2 (x, y); } +Vector2 Quat::yy() const { return Vector2 (y, y); } +Vector2 Quat::zy() const { return Vector2 (z, y); } +Vector2 Quat::wy() const { return Vector2 (w, y); } +Vector2 Quat::xz() const { return Vector2 (x, z); } +Vector2 Quat::yz() const { return Vector2 (y, z); } +Vector2 Quat::zz() const { return Vector2 (z, z); } +Vector2 Quat::wz() const { return Vector2 (w, z); } +Vector2 Quat::xw() const { return Vector2 (x, w); } +Vector2 Quat::yw() const { return Vector2 (y, w); } +Vector2 Quat::zw() const { return Vector2 (z, w); } +Vector2 Quat::ww() const { return Vector2 (w, w); } + +// 3-char swizzles + +Vector3 Quat::xxx() const { return Vector3 (x, x, x); } +Vector3 Quat::yxx() const { return Vector3 (y, x, x); } +Vector3 Quat::zxx() const { return Vector3 (z, x, x); } +Vector3 Quat::wxx() const { return Vector3 (w, x, x); } +Vector3 Quat::xyx() const { return Vector3 (x, y, x); } +Vector3 Quat::yyx() const { return Vector3 (y, y, x); } +Vector3 Quat::zyx() const { return Vector3 (z, y, x); } +Vector3 Quat::wyx() const { return Vector3 (w, y, x); } +Vector3 Quat::xzx() const { return Vector3 (x, z, x); } +Vector3 Quat::yzx() const { return Vector3 (y, z, x); } +Vector3 Quat::zzx() const { return Vector3 (z, z, x); } +Vector3 Quat::wzx() const { return Vector3 (w, z, x); } +Vector3 Quat::xwx() const { return Vector3 (x, w, x); } +Vector3 Quat::ywx() const { return Vector3 (y, w, x); } +Vector3 Quat::zwx() const { return Vector3 (z, w, x); } +Vector3 Quat::wwx() const { return Vector3 (w, w, x); } +Vector3 Quat::xxy() const { return Vector3 (x, x, y); } +Vector3 Quat::yxy() const { return Vector3 (y, x, y); } +Vector3 Quat::zxy() const { return Vector3 (z, x, y); } +Vector3 Quat::wxy() const { return Vector3 (w, x, y); } +Vector3 Quat::xyy() const { return Vector3 (x, y, y); } +Vector3 Quat::yyy() const { return Vector3 (y, y, y); } +Vector3 Quat::zyy() const { return Vector3 (z, y, y); } +Vector3 Quat::wyy() const { return Vector3 (w, y, y); } +Vector3 Quat::xzy() const { return Vector3 (x, z, y); } +Vector3 Quat::yzy() const { return Vector3 (y, z, y); } +Vector3 Quat::zzy() const { return Vector3 (z, z, y); } +Vector3 Quat::wzy() const { return Vector3 (w, z, y); } +Vector3 Quat::xwy() const { return Vector3 (x, w, y); } +Vector3 Quat::ywy() const { return Vector3 (y, w, y); } +Vector3 Quat::zwy() const { return Vector3 (z, w, y); } +Vector3 Quat::wwy() const { return Vector3 (w, w, y); } +Vector3 Quat::xxz() const { return Vector3 (x, x, z); } +Vector3 Quat::yxz() const { return Vector3 (y, x, z); } +Vector3 Quat::zxz() const { return Vector3 (z, x, z); } +Vector3 Quat::wxz() const { return Vector3 (w, x, z); } +Vector3 Quat::xyz() const { return Vector3 (x, y, z); } +Vector3 Quat::yyz() const { return Vector3 (y, y, z); } +Vector3 Quat::zyz() const { return Vector3 (z, y, z); } +Vector3 Quat::wyz() const { return Vector3 (w, y, z); } +Vector3 Quat::xzz() const { return Vector3 (x, z, z); } +Vector3 Quat::yzz() const { return Vector3 (y, z, z); } +Vector3 Quat::zzz() const { return Vector3 (z, z, z); } +Vector3 Quat::wzz() const { return Vector3 (w, z, z); } +Vector3 Quat::xwz() const { return Vector3 (x, w, z); } +Vector3 Quat::ywz() const { return Vector3 (y, w, z); } +Vector3 Quat::zwz() const { return Vector3 (z, w, z); } +Vector3 Quat::wwz() const { return Vector3 (w, w, z); } +Vector3 Quat::xxw() const { return Vector3 (x, x, w); } +Vector3 Quat::yxw() const { return Vector3 (y, x, w); } +Vector3 Quat::zxw() const { return Vector3 (z, x, w); } +Vector3 Quat::wxw() const { return Vector3 (w, x, w); } +Vector3 Quat::xyw() const { return Vector3 (x, y, w); } +Vector3 Quat::yyw() const { return Vector3 (y, y, w); } +Vector3 Quat::zyw() const { return Vector3 (z, y, w); } +Vector3 Quat::wyw() const { return Vector3 (w, y, w); } +Vector3 Quat::xzw() const { return Vector3 (x, z, w); } +Vector3 Quat::yzw() const { return Vector3 (y, z, w); } +Vector3 Quat::zzw() const { return Vector3 (z, z, w); } +Vector3 Quat::wzw() const { return Vector3 (w, z, w); } +Vector3 Quat::xww() const { return Vector3 (x, w, w); } +Vector3 Quat::yww() const { return Vector3 (y, w, w); } +Vector3 Quat::zww() const { return Vector3 (z, w, w); } +Vector3 Quat::www() const { return Vector3 (w, w, w); } + +// 4-char swizzles + +Vector4 Quat::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Quat::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Quat::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Quat::wxxx() const { return Vector4 (w, x, x, x); } +Vector4 Quat::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Quat::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Quat::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Quat::wyxx() const { return Vector4 (w, y, x, x); } +Vector4 Quat::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Quat::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Quat::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Quat::wzxx() const { return Vector4 (w, z, x, x); } +Vector4 Quat::xwxx() const { return Vector4 (x, w, x, x); } +Vector4 Quat::ywxx() const { return Vector4 (y, w, x, x); } +Vector4 Quat::zwxx() const { return Vector4 (z, w, x, x); } +Vector4 Quat::wwxx() const { return Vector4 (w, w, x, x); } +Vector4 Quat::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Quat::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Quat::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Quat::wxyx() const { return Vector4 (w, x, y, x); } +Vector4 Quat::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Quat::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Quat::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Quat::wyyx() const { return Vector4 (w, y, y, x); } +Vector4 Quat::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Quat::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Quat::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Quat::wzyx() const { return Vector4 (w, z, y, x); } +Vector4 Quat::xwyx() const { return Vector4 (x, w, y, x); } +Vector4 Quat::ywyx() const { return Vector4 (y, w, y, x); } +Vector4 Quat::zwyx() const { return Vector4 (z, w, y, x); } +Vector4 Quat::wwyx() const { return Vector4 (w, w, y, x); } +Vector4 Quat::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Quat::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Quat::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Quat::wxzx() const { return Vector4 (w, x, z, x); } +Vector4 Quat::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Quat::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Quat::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Quat::wyzx() const { return Vector4 (w, y, z, x); } +Vector4 Quat::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Quat::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Quat::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Quat::wzzx() const { return Vector4 (w, z, z, x); } +Vector4 Quat::xwzx() const { return Vector4 (x, w, z, x); } +Vector4 Quat::ywzx() const { return Vector4 (y, w, z, x); } +Vector4 Quat::zwzx() const { return Vector4 (z, w, z, x); } +Vector4 Quat::wwzx() const { return Vector4 (w, w, z, x); } +Vector4 Quat::xxwx() const { return Vector4 (x, x, w, x); } +Vector4 Quat::yxwx() const { return Vector4 (y, x, w, x); } +Vector4 Quat::zxwx() const { return Vector4 (z, x, w, x); } +Vector4 Quat::wxwx() const { return Vector4 (w, x, w, x); } +Vector4 Quat::xywx() const { return Vector4 (x, y, w, x); } +Vector4 Quat::yywx() const { return Vector4 (y, y, w, x); } +Vector4 Quat::zywx() const { return Vector4 (z, y, w, x); } +Vector4 Quat::wywx() const { return Vector4 (w, y, w, x); } +Vector4 Quat::xzwx() const { return Vector4 (x, z, w, x); } +Vector4 Quat::yzwx() const { return Vector4 (y, z, w, x); } +Vector4 Quat::zzwx() const { return Vector4 (z, z, w, x); } +Vector4 Quat::wzwx() const { return Vector4 (w, z, w, x); } +Vector4 Quat::xwwx() const { return Vector4 (x, w, w, x); } +Vector4 Quat::ywwx() const { return Vector4 (y, w, w, x); } +Vector4 Quat::zwwx() const { return Vector4 (z, w, w, x); } +Vector4 Quat::wwwx() const { return Vector4 (w, w, w, x); } +Vector4 Quat::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Quat::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Quat::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Quat::wxxy() const { return Vector4 (w, x, x, y); } +Vector4 Quat::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Quat::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Quat::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Quat::wyxy() const { return Vector4 (w, y, x, y); } +Vector4 Quat::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Quat::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Quat::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Quat::wzxy() const { return Vector4 (w, z, x, y); } +Vector4 Quat::xwxy() const { return Vector4 (x, w, x, y); } +Vector4 Quat::ywxy() const { return Vector4 (y, w, x, y); } +Vector4 Quat::zwxy() const { return Vector4 (z, w, x, y); } +Vector4 Quat::wwxy() const { return Vector4 (w, w, x, y); } +Vector4 Quat::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Quat::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Quat::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Quat::wxyy() const { return Vector4 (w, x, y, y); } +Vector4 Quat::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Quat::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Quat::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Quat::wyyy() const { return Vector4 (w, y, y, y); } +Vector4 Quat::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Quat::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Quat::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Quat::wzyy() const { return Vector4 (w, z, y, y); } +Vector4 Quat::xwyy() const { return Vector4 (x, w, y, y); } +Vector4 Quat::ywyy() const { return Vector4 (y, w, y, y); } +Vector4 Quat::zwyy() const { return Vector4 (z, w, y, y); } +Vector4 Quat::wwyy() const { return Vector4 (w, w, y, y); } +Vector4 Quat::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Quat::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Quat::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Quat::wxzy() const { return Vector4 (w, x, z, y); } +Vector4 Quat::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Quat::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Quat::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Quat::wyzy() const { return Vector4 (w, y, z, y); } +Vector4 Quat::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Quat::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Quat::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Quat::wzzy() const { return Vector4 (w, z, z, y); } +Vector4 Quat::xwzy() const { return Vector4 (x, w, z, y); } +Vector4 Quat::ywzy() const { return Vector4 (y, w, z, y); } +Vector4 Quat::zwzy() const { return Vector4 (z, w, z, y); } +Vector4 Quat::wwzy() const { return Vector4 (w, w, z, y); } +Vector4 Quat::xxwy() const { return Vector4 (x, x, w, y); } +Vector4 Quat::yxwy() const { return Vector4 (y, x, w, y); } +Vector4 Quat::zxwy() const { return Vector4 (z, x, w, y); } +Vector4 Quat::wxwy() const { return Vector4 (w, x, w, y); } +Vector4 Quat::xywy() const { return Vector4 (x, y, w, y); } +Vector4 Quat::yywy() const { return Vector4 (y, y, w, y); } +Vector4 Quat::zywy() const { return Vector4 (z, y, w, y); } +Vector4 Quat::wywy() const { return Vector4 (w, y, w, y); } +Vector4 Quat::xzwy() const { return Vector4 (x, z, w, y); } +Vector4 Quat::yzwy() const { return Vector4 (y, z, w, y); } +Vector4 Quat::zzwy() const { return Vector4 (z, z, w, y); } +Vector4 Quat::wzwy() const { return Vector4 (w, z, w, y); } +Vector4 Quat::xwwy() const { return Vector4 (x, w, w, y); } +Vector4 Quat::ywwy() const { return Vector4 (y, w, w, y); } +Vector4 Quat::zwwy() const { return Vector4 (z, w, w, y); } +Vector4 Quat::wwwy() const { return Vector4 (w, w, w, y); } +Vector4 Quat::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Quat::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Quat::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Quat::wxxz() const { return Vector4 (w, x, x, z); } +Vector4 Quat::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Quat::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Quat::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Quat::wyxz() const { return Vector4 (w, y, x, z); } +Vector4 Quat::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Quat::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Quat::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Quat::wzxz() const { return Vector4 (w, z, x, z); } +Vector4 Quat::xwxz() const { return Vector4 (x, w, x, z); } +Vector4 Quat::ywxz() const { return Vector4 (y, w, x, z); } +Vector4 Quat::zwxz() const { return Vector4 (z, w, x, z); } +Vector4 Quat::wwxz() const { return Vector4 (w, w, x, z); } +Vector4 Quat::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Quat::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Quat::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Quat::wxyz() const { return Vector4 (w, x, y, z); } +Vector4 Quat::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Quat::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Quat::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Quat::wyyz() const { return Vector4 (w, y, y, z); } +Vector4 Quat::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Quat::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Quat::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Quat::wzyz() const { return Vector4 (w, z, y, z); } +Vector4 Quat::xwyz() const { return Vector4 (x, w, y, z); } +Vector4 Quat::ywyz() const { return Vector4 (y, w, y, z); } +Vector4 Quat::zwyz() const { return Vector4 (z, w, y, z); } +Vector4 Quat::wwyz() const { return Vector4 (w, w, y, z); } +Vector4 Quat::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Quat::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Quat::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Quat::wxzz() const { return Vector4 (w, x, z, z); } +Vector4 Quat::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Quat::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Quat::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Quat::wyzz() const { return Vector4 (w, y, z, z); } +Vector4 Quat::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Quat::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Quat::zzzz() const { return Vector4 (z, z, z, z); } +Vector4 Quat::wzzz() const { return Vector4 (w, z, z, z); } +Vector4 Quat::xwzz() const { return Vector4 (x, w, z, z); } +Vector4 Quat::ywzz() const { return Vector4 (y, w, z, z); } +Vector4 Quat::zwzz() const { return Vector4 (z, w, z, z); } +Vector4 Quat::wwzz() const { return Vector4 (w, w, z, z); } +Vector4 Quat::xxwz() const { return Vector4 (x, x, w, z); } +Vector4 Quat::yxwz() const { return Vector4 (y, x, w, z); } +Vector4 Quat::zxwz() const { return Vector4 (z, x, w, z); } +Vector4 Quat::wxwz() const { return Vector4 (w, x, w, z); } +Vector4 Quat::xywz() const { return Vector4 (x, y, w, z); } +Vector4 Quat::yywz() const { return Vector4 (y, y, w, z); } +Vector4 Quat::zywz() const { return Vector4 (z, y, w, z); } +Vector4 Quat::wywz() const { return Vector4 (w, y, w, z); } +Vector4 Quat::xzwz() const { return Vector4 (x, z, w, z); } +Vector4 Quat::yzwz() const { return Vector4 (y, z, w, z); } +Vector4 Quat::zzwz() const { return Vector4 (z, z, w, z); } +Vector4 Quat::wzwz() const { return Vector4 (w, z, w, z); } +Vector4 Quat::xwwz() const { return Vector4 (x, w, w, z); } +Vector4 Quat::ywwz() const { return Vector4 (y, w, w, z); } +Vector4 Quat::zwwz() const { return Vector4 (z, w, w, z); } +Vector4 Quat::wwwz() const { return Vector4 (w, w, w, z); } +Vector4 Quat::xxxw() const { return Vector4 (x, x, x, w); } +Vector4 Quat::yxxw() const { return Vector4 (y, x, x, w); } +Vector4 Quat::zxxw() const { return Vector4 (z, x, x, w); } +Vector4 Quat::wxxw() const { return Vector4 (w, x, x, w); } +Vector4 Quat::xyxw() const { return Vector4 (x, y, x, w); } +Vector4 Quat::yyxw() const { return Vector4 (y, y, x, w); } +Vector4 Quat::zyxw() const { return Vector4 (z, y, x, w); } +Vector4 Quat::wyxw() const { return Vector4 (w, y, x, w); } +Vector4 Quat::xzxw() const { return Vector4 (x, z, x, w); } +Vector4 Quat::yzxw() const { return Vector4 (y, z, x, w); } +Vector4 Quat::zzxw() const { return Vector4 (z, z, x, w); } +Vector4 Quat::wzxw() const { return Vector4 (w, z, x, w); } +Vector4 Quat::xwxw() const { return Vector4 (x, w, x, w); } +Vector4 Quat::ywxw() const { return Vector4 (y, w, x, w); } +Vector4 Quat::zwxw() const { return Vector4 (z, w, x, w); } +Vector4 Quat::wwxw() const { return Vector4 (w, w, x, w); } +Vector4 Quat::xxyw() const { return Vector4 (x, x, y, w); } +Vector4 Quat::yxyw() const { return Vector4 (y, x, y, w); } +Vector4 Quat::zxyw() const { return Vector4 (z, x, y, w); } +Vector4 Quat::wxyw() const { return Vector4 (w, x, y, w); } +Vector4 Quat::xyyw() const { return Vector4 (x, y, y, w); } +Vector4 Quat::yyyw() const { return Vector4 (y, y, y, w); } +Vector4 Quat::zyyw() const { return Vector4 (z, y, y, w); } +Vector4 Quat::wyyw() const { return Vector4 (w, y, y, w); } +Vector4 Quat::xzyw() const { return Vector4 (x, z, y, w); } +Vector4 Quat::yzyw() const { return Vector4 (y, z, y, w); } +Vector4 Quat::zzyw() const { return Vector4 (z, z, y, w); } +Vector4 Quat::wzyw() const { return Vector4 (w, z, y, w); } +Vector4 Quat::xwyw() const { return Vector4 (x, w, y, w); } +Vector4 Quat::ywyw() const { return Vector4 (y, w, y, w); } +Vector4 Quat::zwyw() const { return Vector4 (z, w, y, w); } +Vector4 Quat::wwyw() const { return Vector4 (w, w, y, w); } +Vector4 Quat::xxzw() const { return Vector4 (x, x, z, w); } +Vector4 Quat::yxzw() const { return Vector4 (y, x, z, w); } +Vector4 Quat::zxzw() const { return Vector4 (z, x, z, w); } +Vector4 Quat::wxzw() const { return Vector4 (w, x, z, w); } +Vector4 Quat::xyzw() const { return Vector4 (x, y, z, w); } +Vector4 Quat::yyzw() const { return Vector4 (y, y, z, w); } +Vector4 Quat::zyzw() const { return Vector4 (z, y, z, w); } +Vector4 Quat::wyzw() const { return Vector4 (w, y, z, w); } +Vector4 Quat::xzzw() const { return Vector4 (x, z, z, w); } +Vector4 Quat::yzzw() const { return Vector4 (y, z, z, w); } +Vector4 Quat::zzzw() const { return Vector4 (z, z, z, w); } +Vector4 Quat::wzzw() const { return Vector4 (w, z, z, w); } +Vector4 Quat::xwzw() const { return Vector4 (x, w, z, w); } +Vector4 Quat::ywzw() const { return Vector4 (y, w, z, w); } +Vector4 Quat::zwzw() const { return Vector4 (z, w, z, w); } +Vector4 Quat::wwzw() const { return Vector4 (w, w, z, w); } +Vector4 Quat::xxww() const { return Vector4 (x, x, w, w); } +Vector4 Quat::yxww() const { return Vector4 (y, x, w, w); } +Vector4 Quat::zxww() const { return Vector4 (z, x, w, w); } +Vector4 Quat::wxww() const { return Vector4 (w, x, w, w); } +Vector4 Quat::xyww() const { return Vector4 (x, y, w, w); } +Vector4 Quat::yyww() const { return Vector4 (y, y, w, w); } +Vector4 Quat::zyww() const { return Vector4 (z, y, w, w); } +Vector4 Quat::wyww() const { return Vector4 (w, y, w, w); } +Vector4 Quat::xzww() const { return Vector4 (x, z, w, w); } +Vector4 Quat::yzww() const { return Vector4 (y, z, w, w); } +Vector4 Quat::zzww() const { return Vector4 (z, z, w, w); } +Vector4 Quat::wzww() const { return Vector4 (w, z, w, w); } +Vector4 Quat::xwww() const { return Vector4 (x, w, w, w); } +Vector4 Quat::ywww() const { return Vector4 (y, w, w, w); } +Vector4 Quat::zwww() const { return Vector4 (z, w, w, w); } +Vector4 Quat::wwww() const { return Vector4 (w, w, w, w); } +} + diff --git a/externals/g3dlite/Random.cpp b/externals/g3dlite/Random.cpp new file mode 100644 index 00000000000..2dda744a1ac --- /dev/null +++ b/externals/g3dlite/Random.cpp @@ -0,0 +1,212 @@ +/** + @file Random.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2009-01-02 + @edited 2009-03-29 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. + */ +#include "G3D/Random.h" + +namespace G3D { + +Random& Random::common() { + static Random r; + return r; +} + +Random::Random(void* x) : state(NULL), m_threadsafe(false) { + (void)x; +} + + +Random::Random(uint32 seed, bool threadsafe) : m_threadsafe(threadsafe) { + const uint32 X = 1812433253UL; + + state = new uint32[N]; + state[0] = seed; + for (index = 1; index < (int)N; ++index) { + state[index] = X * (state[index - 1] ^ (state[index - 1] >> 30)) + index; + } +} + + +Random::~Random() { + delete[] state; + state = NULL; +} + + +uint32 Random::bits() { + // See http://en.wikipedia.org/wiki/Mersenne_twister + + // Make a local copy of the index variable to ensure that it + // is not out of bounds + int localIndex = index; + + // Automatically checks for index < 0 if corrupted + // by unsynchronized threads. + if ((unsigned int)localIndex >= (unsigned int)N) { + generate(); + localIndex = 0; + } + // Increment the global index. It may go out of bounds on + // multiple threads, but the above check ensures that the + // array index actually used never goes out of bounds. + // It doesn't matter if we grab the same array index twice + // on two threads, since the distribution of random numbers + // will still be uniform. + ++index; + // Return the next random in the sequence + uint32 r = state[localIndex]; + + // Temper the result + r ^= r >> U; + r ^= (r << S) & B; + r ^= (r << T) & C; + r ^= r >> L; + + return r; +} + + +/** Generate the next N ints, and store them for readback later */ +void Random::generate() { + // Lower R bits + static const uint32 LOWER_MASK = (1LU << R) - 1; + + // Upper (32 - R) bits + static const uint32 UPPER_MASK = 0xFFFFFFFF << R; + static const uint32 mag01[2] = {0UL, (uint32)A}; + + if (m_threadsafe) { + bool contention = ! lock.lock(); + if (contention) { + // Another thread just generated a set of numbers; no need for + // this thread to do it too + lock.unlock(); + return; + } + } + + // First N - M + for (unsigned int i = 0; i < N - M; ++i) { + uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK); + state[i] = state[i + M] ^ (x >> 1) ^ mag01[x & 1]; + } + + // Rest + for (unsigned int i = N - M + 1; i < N - 1; ++i) { + uint32 x = (state[i] & UPPER_MASK) | (state[i + 1] & LOWER_MASK); + state[i] = state[i + (M - N)] ^ (x >> 1) ^ mag01[x & 1]; + } + + uint32 y = (state[N - 1] & UPPER_MASK) | (state[0] & LOWER_MASK); + state[N - 1] = state[M - 1] ^ (y >> 1) ^ mag01[y & 1]; + index = 0; + + if (m_threadsafe) { + lock.unlock(); + } +} + + +int Random::integer(int low, int high) { + int r = iFloor(low + (high - low + 1) * (double)bits() / 0xFFFFFFFFUL); + + // There is a *very small* chance of generating + // a number larger than high. + if (r > high) { + return high; + } else { + return r; + } +} + + +float Random::gaussian(float mean, float stdev) { + + // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html + // Modified to specify standard deviation and mean of distribution + float w, x1, x2; + + // Loop until w is less than 1 so that log(w) is negative + do { + x1 = uniform(-1.0, 1.0); + x2 = uniform(-1.0, 1.0); + + w = float(square(x1) + square(x2)); + } while (w > 1.0f); + + // Transform to gassian distribution + // Multiply by sigma (stdev ^ 2) and add mean. + return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean; +} + + +void Random::cosHemi(float& x, float& y, float& z) { + const float e1 = uniform(); + const float e2 = uniform(); + + // Jensen's method + const float sin_theta = sqrtf(1.0f - e1); + const float cos_theta = sqrtf(e1); + const float phi = 6.28318531f * e2; + + x = cos(phi) * sin_theta; + y = sin(phi) * sin_theta; + z = cos_theta; + + // We could also use Malley's method (pbrt p.657), since they are the same cost: + // + // r = sqrt(e1); + // t = 2*pi*e2; + // x = cos(t)*r; + // y = sin(t)*r; + // z = sqrt(1.0 - x*x + y*y); +} + + +void Random::cosPowHemi(const float k, float& x, float& y, float& z) { + const float e1 = uniform(); + const float e2 = uniform(); + + const float cos_theta = pow(e1, 1.0f / (k + 1.0f)); + const float sin_theta = sqrtf(1.0f - square(cos_theta)); + const float phi = 6.28318531f * e2; + + x = cos(phi) * sin_theta; + y = sin(phi) * sin_theta; + z = cos_theta; +} + + +void Random::hemi(float& x, float& y, float& z) { + sphere(x, y, z); + z = fabsf(z); +} + + +void Random::sphere(float& x, float& y, float& z) { + // Squared magnitude + float m2; + + // Rejection sample + do { + x = uniform() * 2.0f - 1.0f, + y = uniform() * 2.0f - 1.0f, + z = uniform() * 2.0f - 1.0f; + m2 = x*x + y*y + z*z; + } while (m2 >= 1.0f); + + // Divide by magnitude to produce a unit vector + float s = rsqrt(m2); + x *= s; + y *= s; + z *= s; +} + +} // G3D diff --git a/externals/g3dlite/Ray.cpp b/externals/g3dlite/Ray.cpp new file mode 100644 index 00000000000..0436ef0b323 --- /dev/null +++ b/externals/g3dlite/Ray.cpp @@ -0,0 +1,218 @@ +/** + @file Ray.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2002-07-12 + @edited 2004-03-19 + */ + +#include "G3D/platform.h" +#include "G3D/Ray.h" +#include "G3D/Plane.h" +#include "G3D/Sphere.h" +#include "G3D/CollisionDetection.h" + +namespace G3D { + +void Ray::set(const Vector3& origin, const Vector3& direction) { + m_origin = origin; + m_direction = direction; + debugAssert(direction.isUnit()); + + m_invDirection = Vector3::one() / direction; + + // ray slope + ibyj = m_direction.x * m_invDirection.y; + jbyi = m_direction.y * m_invDirection.x; + jbyk = m_direction.y * m_invDirection.z; + kbyj = m_direction.z * m_invDirection.y; + ibyk = m_direction.x * m_invDirection.z; + kbyi = m_direction.z * m_invDirection.x; + + // precomputed terms + c_xy = m_origin.y - jbyi * m_origin.x; + c_xz = m_origin.z - kbyi * m_origin.x; + c_yx = m_origin.x - ibyj * m_origin.y; + c_yz = m_origin.z - kbyj * m_origin.y; + c_zx = m_origin.x - ibyk * m_origin.z; + c_zy = m_origin.y - jbyk * m_origin.z; + + //ray slope classification + if (m_direction.x < 0) { + if (m_direction.y < 0) { + if (m_direction.z < 0) { + classification = MMM; + } else if (m_direction.z > 0) { + classification = MMP; + } else { //(m_direction.z >= 0) + classification = MMO; + } + } else { //(m_direction.y >= 0) + if (m_direction.z < 0) { + if (m_direction.y == 0) { + classification = MOM; + } else { + classification = MPM; + } + } else { //(m_direction.z >= 0) + if ((m_direction.y == 0) && (m_direction.z == 0)) { + classification = MOO; + } else if (m_direction.z == 0) { + classification = MPO; + } else if (m_direction.y == 0) { + classification = MOP; + } else { + classification = MPP; + } + } + } + } else { //(m_direction.x >= 0) + if (m_direction.y < 0) { + if (m_direction.z < 0) { + if (m_direction.x == 0) { + classification = OMM; + } else { + classification = PMM; + } + } else { //(m_direction.z >= 0) + if ((m_direction.x == 0) && (m_direction.z == 0)) { + classification = OMO; + } else if (m_direction.z == 0) { + classification = PMO; + } else if (m_direction.x == 0) { + classification = OMP; + } else { + classification = PMP; + } + } + } else { //(m_direction.y >= 0) + if (m_direction.z < 0) { + if ((m_direction.x == 0) && (m_direction.y == 0)) { + classification = OOM; + } else if (m_direction.x == 0) { + classification = OPM; + } else if (m_direction.y == 0) { + classification = POM; + } else { + classification = PPM; + } + } else { //(m_direction.z > 0) + if (m_direction.x == 0) { + if (m_direction.y == 0) { + classification = OOP; + } else if (m_direction.z == 0) { + classification = OPO; + } else { + classification = OPP; + } + } else { + if ((m_direction.y == 0) && (m_direction.z == 0)) { + classification = POO; + } else if (m_direction.y == 0) { + classification = POP; + } else if (m_direction.z == 0) { + classification = PPO; + } else { + classification = PPP; + } + } + } + } + } +} + +Ray::Ray(class BinaryInput& b) { + deserialize(b); +} + + +void Ray::serialize(class BinaryOutput& b) const { + m_origin.serialize(b); + m_direction.serialize(b); +} + + +void Ray::deserialize(class BinaryInput& b) { + m_origin.deserialize(b); + m_direction.deserialize(b); + set(m_origin, m_direction); +} + + +Ray Ray::refract( + const Vector3& newOrigin, + const Vector3& normal, + float iInside, + float iOutside) const { + + Vector3 D = m_direction.refractionDirection(normal, iInside, iOutside); + return Ray(newOrigin + (m_direction + normal * (float)sign(m_direction.dot(normal))) * 0.001f, D); +} + + +Ray Ray::reflect( + const Vector3& newOrigin, + const Vector3& normal) const { + + Vector3 D = m_direction.reflectionDirection(normal); + return Ray(newOrigin + (D + normal) * 0.001f, D); +} + + +Vector3 Ray::intersection(const Plane& plane) const { + float d; + Vector3 normal = plane.normal(); + plane.getEquation(normal, d); + float rate = m_direction.dot(normal); + + if (rate >= 0.0f) { + return Vector3::inf(); + } else { + float t = -(d + m_origin.dot(normal)) / rate; + return m_origin + m_direction * t; + } +} + + +float Ray::intersectionTime(const class Sphere& sphere, bool solid) const { + Vector3 dummy; + return CollisionDetection::collisionTimeForMovingPointFixedSphere( + m_origin, m_direction, sphere, dummy, dummy, solid); +} + + +float Ray::intersectionTime(const class Plane& plane) const { + Vector3 dummy; + return CollisionDetection::collisionTimeForMovingPointFixedPlane( + m_origin, m_direction, plane, dummy); +} + + +float Ray::intersectionTime(const class Box& box) const { + Vector3 dummy; + float time = CollisionDetection::collisionTimeForMovingPointFixedBox( + m_origin, m_direction, box, dummy); + + if ((time == finf()) && (box.contains(m_origin))) { + return 0.0f; + } else { + return time; + } +} + + +float Ray::intersectionTime(const class AABox& box) const { + Vector3 dummy; + bool inside; + float time = CollisionDetection::collisionTimeForMovingPointFixedAABox( + m_origin, m_direction, box, dummy, inside); + + if ((time == finf()) && inside) { + return 0.0f; + } else { + return time; + } +} + +} diff --git a/externals/g3dlite/ReferenceCount.cpp b/externals/g3dlite/ReferenceCount.cpp new file mode 100644 index 00000000000..2e1f117e0d9 --- /dev/null +++ b/externals/g3dlite/ReferenceCount.cpp @@ -0,0 +1,61 @@ +/** + @file ReferenceCount.cpp + + Reference Counting Garbage Collector for C++ + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + @cite Adapted and extended from Justin Miller's "RGC" class that appeared in BYTE magazine. + @cite See also http://www.jelovic.com/articles/cpp_without_memory_errors_slides.htm + + @created 2001-10-23 + @edited 2009-04-25 +*/ +#include "G3D/platform.h" +#include "G3D/ReferenceCount.h" + +namespace G3D { + +ReferenceCountedObject::ReferenceCountedObject() : + ReferenceCountedObject_refCount(0), + ReferenceCountedObject_weakPointer(0) { + + debugAssertM(isValidHeapPointer(this), + "Reference counted objects must be allocated on the heap."); +} + +void ReferenceCountedObject::ReferenceCountedObject_zeroWeakPointers() { + // Tell all of my weak pointers that I'm gone. + + _WeakPtrLinkedList* node = ReferenceCountedObject_weakPointer; + + while (node != NULL) { + // Notify the weak pointer that it is going away + node->weakPtr->objectCollected(); + + // Free the node and advance + _WeakPtrLinkedList* tmp = node; + node = node->next; + delete tmp; + } +} + +ReferenceCountedObject::~ReferenceCountedObject() {} + + +ReferenceCountedObject::ReferenceCountedObject(const ReferenceCountedObject& notUsed) : + ReferenceCountedObject_refCount(0), + ReferenceCountedObject_weakPointer(0) { + (void)notUsed; + debugAssertM(G3D::isValidHeapPointer(this), + "Reference counted objects must be allocated on the heap."); +} + +ReferenceCountedObject& ReferenceCountedObject::operator=(const ReferenceCountedObject& other) { + (void)other; + // Nothing changes when I am assigned; the reference count on + // both objects is the same (although my super-class probably + // changes). + return *this; +} + +} // G3D diff --git a/externals/g3dlite/Sphere.cpp b/externals/g3dlite/Sphere.cpp new file mode 100644 index 00000000000..4ed0811cb29 --- /dev/null +++ b/externals/g3dlite/Sphere.cpp @@ -0,0 +1,223 @@ +/** + @file Sphere.cpp + + Sphere class + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2001-04-17 + @edited 2009-01-20 + */ + +#include "G3D/platform.h" +#include "G3D/Sphere.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryOutput.h" +#include "G3D/BinaryInput.h" +#include "G3D/AABox.h" +#include "G3D/Plane.h" + +namespace G3D { + +int32 Sphere::dummy; + +Sphere::Sphere(class BinaryInput& b) { + deserialize(b); +} + + +void Sphere::serialize(class BinaryOutput& b) const { + center.serialize(b); + b.writeFloat64(radius); +} + + +void Sphere::deserialize(class BinaryInput& b) { + center.deserialize(b); + radius = (float)b.readFloat64(); +} + + +std::string Sphere::toString() const { + return format("Sphere(<%g, %g, %g>, %g)", + center.x, center.y, center.z, radius); +} + + +bool Sphere::contains(const Vector3& point) const { + float distance = (center - point).squaredMagnitude(); + return distance <= square(radius); +} + + +bool Sphere::contains(const Sphere& other) const { + float distance = (center - other.center).squaredMagnitude(); + return (radius >= other.radius) && (distance <= square(radius - other.radius)); +} + + +bool Sphere::intersects(const Sphere& other) const { + return (other.center - center).length() <= (radius + other.radius); +} + + +void Sphere::merge(const Sphere& other) { + if (other.contains(*this)) { + *this = other; + } else if (! contains(other)) { + // The farthest distance is along the axis between the centers, which + // must not be colocated since neither contains the other. + Vector3 toMe = center - other.center; + // Get a point on the axis from each + toMe = toMe.direction(); + const Vector3& A = center + toMe * radius; + const Vector3& B = other.center - toMe * other.radius; + + // Now just bound the A->B segment + center = (A + B) * 0.5f; + radius = (A - B).length(); + } + // (if this contains other, we're done) +} + + +bool Sphere::culledBy( + const Array<Plane>& plane, + int& cullingPlaneIndex, + const uint32 inMask, + uint32& outMask) const { + + return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask, outMask); +} + + +bool Sphere::culledBy( + const Array<Plane>& plane, + int& cullingPlaneIndex, + const uint32 inMask) const { + + return culledBy(plane.getCArray(), plane.size(), cullingPlaneIndex, inMask); +} + + +bool Sphere::culledBy( + const class Plane* plane, + int numPlanes, + int& cullingPlane, + const uint32 _inMask, + uint32& childMask) const { + + if (radius == finf()) { + // No plane can cull the infinite box + return false; + } + + uint32 inMask = _inMask; + assert(numPlanes < 31); + + childMask = 0; + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < numPlanes; p++) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + + bool culledLow = ! plane[p].halfSpaceContainsFinite(center + plane[p].normal() * radius); + bool culledHigh = ! plane[p].halfSpaceContainsFinite(center - plane[p].normal() * radius); + + if (culledLow) { + // Plane p culled the sphere + cullingPlane = p; + + // The caller should not recurse into the children, + // since the parent is culled. If they do recurse, + // make them only test against this one plane, which + // will immediately cull the volume. + childMask = 1 << p; + return true; + + } else if (culledHigh) { + // The bounding volume straddled the plane; we have + // to keep testing against this plane + childMask |= (1 << p); + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +bool Sphere::culledBy( + const class Plane* plane, + int numPlanes, + int& cullingPlane, + const uint32 _inMask) const { + + uint32 inMask = _inMask; + assert(numPlanes < 31); + + // See if there is one plane for which all of the + // vertices are in the negative half space. + for (int p = 0; p < numPlanes; p++) { + + // Only test planes that are not masked + if ((inMask & 1) != 0) { + bool culled = ! plane[p].halfSpaceContains(center + plane[p].normal() * radius); + if (culled) { + // Plane p culled the sphere + cullingPlane = p; + return true; + } + } + + // Move on to the next bit. + inMask = inMask >> 1; + } + + // None of the planes could cull this box + cullingPlane = -1; + return false; +} + + +Vector3 Sphere::randomSurfacePoint() const { + return Vector3::random() * radius + center; +} + + +Vector3 Sphere::randomInteriorPoint() const { + Vector3 result; + do { + result = Vector3(uniformRandom(-1, 1), + uniformRandom(-1, 1), + uniformRandom(-1, 1)); + } while (result.squaredMagnitude() >= 1.0f); + + return result * radius + center; +} + + +float Sphere::volume() const { + return (float)pi() * (4.0f / 3.0f) * powf((float)radius, 3.0f); +} + + +float Sphere::area() const { + return (float)pi() * 4.0f * powf((float)radius, 2.0f); +} + + +void Sphere::getBounds(AABox& out) const { + Vector3 extent(radius, radius, radius); + out = AABox(center - extent, center + extent); +} + +} // namespace diff --git a/externals/g3dlite/System.cpp b/externals/g3dlite/System.cpp new file mode 100644 index 00000000000..e03c4e8c6fa --- /dev/null +++ b/externals/g3dlite/System.cpp @@ -0,0 +1,1746 @@ +/** + @file System.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + Note: every routine must call init() first. + + There are two kinds of detection used in this file. At compile + time, the _MSC_VER #define is used to determine whether x86 assembly + can be used at all. At runtime, processor detection is used to + determine if we can safely call the routines that use that assembly. + + @created 2003-01-25 + @edited 2010-01-03 + */ + +#include "G3D/platform.h" +#include "G3D/System.h" +#include "G3D/debug.h" +#include "G3D/fileutils.h" +#include "G3D/TextOutput.h" +#include "G3D/G3DGameUnits.h" +#include "G3D/Crypto.h" +#include "G3D/prompt.h" +#include "G3D/stringutils.h" +#include "G3D/Log.h" +#include "G3D/Table.h" +#include "G3D/GMutex.h" +#include "G3D/units.h" +#include <time.h> + +#include <cstring> +#include <cstdio> + +// Uncomment the following line to turn off G3D::System memory +// allocation and use the operating system's malloc. +//#define NO_BUFFERPOOL + +#if defined(__i386__) || defined(__x86_64__) || defined(G3D_WIN32) +# define G3D_NOT_OSX_PPC +#endif + +#include <cstdlib> + +#ifdef G3D_WIN32 + +# include <conio.h> +# include <sys/timeb.h> +# include "G3D/RegistryUtil.h" + +#elif defined(G3D_LINUX) + +# include <stdlib.h> +# include <stdio.h> +# include <errno.h> +# include <sys/types.h> +# include <sys/select.h> +# include <termios.h> +# include <unistd.h> +# include <sys/ioctl.h> +# include <sys/time.h> +# include <pthread.h> + +#elif defined(G3D_OSX) + + #include <stdlib.h> + #include <stdio.h> + #include <errno.h> + #include <sys/types.h> + #include <sys/sysctl.h> + #include <sys/select.h> + #include <sys/time.h> + #include <termios.h> + #include <unistd.h> + #include <pthread.h> + #include <mach-o/arch.h> + + #include <sstream> + #include <CoreServices/CoreServices.h> +#endif + +// SIMM include +#ifdef __SSE__ +#include <xmmintrin.h> +#endif + +namespace G3D { + + +/** Checks if the CPUID command is available on the processor (called from init) */ +static bool checkForCPUID(); + +/** Called from init */ +static void getG3DVersion(std::string& s); + +/** Called from init */ +static G3DEndian checkEndian(); + + +System& System::instance() { + static System thesystem; + return thesystem; +} + + +System::System() : + m_initialized(false), + m_cpuSpeed(0), + m_hasCPUID(false), + m_hasRDTSC(false), + m_hasMMX(false), + m_hasSSE(false), + m_hasSSE2(false), + m_hasSSE3(false), + m_has3DNOW(false), + m_has3DNOW2(false), + m_hasAMDMMX(false), + m_cpuVendor("Uninitialized"), + m_numCores(1), + m_machineEndian(G3D_LITTLE_ENDIAN), + m_cpuArch("Uninitialized"), + m_operatingSystem("Uninitialized"), + m_version("Uninitialized"), + m_outOfMemoryCallback(NULL), + m_realWorldGetTickTime0(0), + m_highestCPUIDFunction(0) { + + init(); +} + + +void System::init() { + // NOTE: Cannot use most G3D data structures or utility functions + // in here because they are not initialized. + + if (m_initialized) { + return; + } else { + m_initialized = true; + } + + getG3DVersion(m_version); + + m_machineEndian = checkEndian(); + + m_hasCPUID = checkForCPUID(); + // Process the CPUID information + if (m_hasCPUID) { + // We read the standard CPUID level 0x00000000 which should + // be available on every x86 processor. This fills out + // a string with the processor vendor tag. + unsigned int eaxreg = 0, ebxreg = 0, ecxreg = 0, edxreg = 0; + + cpuid(CPUID_VENDOR_ID, eaxreg, ebxreg, ecxreg, edxreg); + + { + char c[100]; + // Then we connect the single register values to the vendor string + *((unsigned int*) c) = ebxreg; + *((unsigned int*) (c + 4)) = edxreg; + *((unsigned int*) (c + 8)) = ecxreg; + c[12] = '\0'; + m_cpuVendor = c; + } + + switch (ebxreg) { + case 0x756E6547: // GenuineIntel + m_cpuArch = "Intel Processor"; + break; + + case 0x68747541: // AuthenticAMD + m_cpuArch = "AMD Processor"; + break; + + case 0x69727943: // CyrixInstead + m_cpuArch = "Cyrix Processor"; + break; + + default: + m_cpuArch = "Unknown Processor Vendor"; + break; + } + + + unsigned int highestFunction = eaxreg; + if (highestFunction >= CPUID_NUM_CORES) { + cpuid(CPUID_NUM_CORES, eaxreg, ebxreg, ecxreg, edxreg); + // Number of cores is in (eax>>26) + 1 + m_numCores = (eaxreg >> 26) + 1; + } + + cpuid(CPUID_GET_HIGHEST_FUNCTION, m_highestCPUIDFunction, ebxreg, ecxreg, edxreg); + } + + + // Get the operating system name (also happens to read some other information) +# ifdef G3D_WIN32 + // Note that this overrides some of the values computed above + bool success = RegistryUtil::readInt32 + ("HKEY_LOCAL_MACHINE\\HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + "~MHz", m_cpuSpeed); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + const char* arch = NULL; + switch (systemInfo.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_INTEL: + arch = "Intel"; + break; + + case PROCESSOR_ARCHITECTURE_MIPS: + arch = "MIPS"; + break; + + case PROCESSOR_ARCHITECTURE_ALPHA: + arch = "Alpha"; + break; + + case PROCESSOR_ARCHITECTURE_PPC: + arch = "Power PC"; + break; + + default: + arch = "Unknown"; + } + + m_numCores = systemInfo.dwNumberOfProcessors; + uint32 maxAddr = (uint32)systemInfo.lpMaximumApplicationAddress; + { + char c[1024]; + sprintf(c, "%d x %d-bit %s processor", + systemInfo.dwNumberOfProcessors, + (int)(::log((double)maxAddr) / ::log(2.0) + 2.0), + arch); + m_cpuArch = c; + } + + OSVERSIONINFO osVersionInfo; + osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + success = GetVersionEx(&osVersionInfo) != 0; + + if (success) { + char c[1000]; + sprintf(c, "Windows %d.%d build %d Platform %d %s", + osVersionInfo.dwMajorVersion, + osVersionInfo.dwMinorVersion, + osVersionInfo.dwBuildNumber, + osVersionInfo.dwPlatformId, + osVersionInfo.szCSDVersion); + m_operatingSystem = c; + } else { + m_operatingSystem = "Windows"; + } + +# elif defined(G3D_LINUX) || defined(G3D_FREEBSD) + + { + // Find the operating system using the 'uname' command + FILE* f = popen("uname -a", "r"); + + int len = 100; + char* r = (char*)::malloc(len * sizeof(char)); + fgets(r, len, f); + // Remove trailing newline + if (r[strlen(r) - 1] == '\n') { + r[strlen(r) - 1] = '\0'; + } + fclose(f); + + m_operatingSystem = r; + ::free(r); + } + +# elif defined(G3D_OSX) + + // Operating System: + SInt32 macVersion; + Gestalt(gestaltSystemVersion, &macVersion); + + int major = (macVersion >> 8) & 0xFF; + int minor = (macVersion >> 4) & 0xF; + int revision = macVersion & 0xF; + + { + char c[1000]; + sprintf(c, "OS X %x.%x.%x", major, minor, revision); + m_operatingSystem = c; + } + + // Clock Cycle Timing Information: + Gestalt('pclk', &m_OSXCPUSpeed); + m_cpuSpeed = iRound((double)m_OSXCPUSpeed / (1024 * 1024)); + m_secondsPerNS = 1.0 / 1.0e9; + + // System Architecture: + const NXArchInfo* pInfo = NXGetLocalArchInfo(); + + if (pInfo) { + m_cpuArch = pInfo->description; + + switch (pInfo->cputype) { + case CPU_TYPE_POWERPC: + switch(pInfo->cpusubtype){ + case CPU_SUBTYPE_POWERPC_750: + case CPU_SUBTYPE_POWERPC_7400: + case CPU_SUBTYPE_POWERPC_7450: + m_cpuVendor = "Motorola"; + break; + case CPU_SUBTYPE_POWERPC_970: + m_cpuVendor = "IBM"; + break; + } + break; + + case CPU_TYPE_I386: + m_cpuVendor = "Intel"; + break; + } + } +# endif + + initTime(); + + getStandardProcessorExtensions(); +} + + +void getG3DVersion(std::string& s) { + char cstr[100]; + if ((G3D_VER % 100) != 0) { + sprintf(cstr, "G3D %d.%02d beta %d", + G3D_VER / 10000, + (G3D_VER / 100) % 100, + G3D_VER % 100); + } else { + sprintf(cstr, "G3D %d.%02d", + G3D_VER / 10000, + (G3D_VER / 100) % 100); + } + s = cstr; +} + +#if 0 // TODO: delete +struct Directory { + std::string path; + Array<std::string> contents; +}; + +static bool maybeAddDirectory(const std::string& newPath, Array<Directory>& directoryArray, bool recurse = true) { + if (fileExists(newPath)) { + Directory& d = directoryArray.next(); + d.path = newPath; + getFiles(pathConcat(newPath, "*"), d.contents); + Array<std::string> dirs; + getDirs(pathConcat(newPath, "*"), dirs); + d.contents.append(dirs); + + if (recurse) { + // Look for subdirectories + static const std::string subdirs[] = + {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""}; + + for (int j = 0; j < dirs.size(); ++j) { + for (int i = 0; ! subdirs[i].empty(); ++i) { + if (dirs[j] == subdirs[i]) { + maybeAddDirectory(pathConcat(newPath, dirs[j]), directoryArray, false); + } + } + } + } + return true; + } else { + return false; + } +} +#endif + +std::string System::findDataFile +(const std::string& full, + bool errorIfNotFound) { + + // Places where specific files were most recently found. This is + // used to cache seeking of common files. + static Table<std::string, std::string> lastFound; + + // First check if the file exists as requested. This will go + // through the FileSystemCache, so most calls do not touch disk. + if (fileExists(full)) { + return full; + } + + // Now check where we previously found this file. + std::string* last = lastFound.getPointer(full); + if (last != NULL) { + if (fileExists(*last)) { + // Even if cwd has changed the file is still present. + // We won't notice if it has been deleted, however. + return *last; + } else { + // Remove this from the cache it is invalid + lastFound.remove(full); + } + } + + // Places to look + static Array<std::string> directoryArray; + + if (directoryArray.size() == 0) { + // Initialize the directory array + RealTime t0 = System::time(); + + Array<std::string> baseDirArray; + + std::string initialAppDataDir(instance().m_appDataDir); + + baseDirArray.append(""); + if (! initialAppDataDir.empty()) { + baseDirArray.append(initialAppDataDir); + } + + const char* g3dPath = getenv("G3DDATA"); + + if (g3dPath && (initialAppDataDir != g3dPath)) { + baseDirArray.append(g3dPath); + } + + static const std::string subdirs[] = + {"font", "gui", "SuperShader", "cubemap", "icon", "material", "image", "md2", "md3", "ifs", "3ds", "sky", ""}; + for (int j = 0; j < baseDirArray.size(); ++j) { + std::string d = baseDirArray[j]; + if (fileExists(d)) { + directoryArray.append(d); + for (int i = 0; ! subdirs[i].empty(); ++i) { + const std::string& p = pathConcat(d, subdirs[i]); + if (fileExists(p)) { + directoryArray.append(p); + } + } + } + } + + logLazyPrintf("Initializing System::findDataFile took %fs\n", System::time() - t0); + } + + for (int i = 0; i < directoryArray.size(); ++i) { + const std::string& p = pathConcat(directoryArray[i], full); + if (fileExists(p)) { + lastFound.set(full, p); + return p; + } + } + + if (errorIfNotFound) { + // Generate an error message + std::string locations; + for (int i = 0; i < directoryArray.size(); ++i) { + locations += pathConcat(directoryArray[i], full) + "\n"; + } + alwaysAssertM(false, "Could not find '" + full + "' in:\n" + locations); + } + + // Not found + return ""; +} + + +void System::setAppDataDir(const std::string& path) { + instance().m_appDataDir = path; +} + + +std::string demoFindData(bool errorIfNotFound) { + static const char* g3dPath = getenv("G3DDATA"); + if (g3dPath) { + return g3dPath; +# ifdef G3D_WIN32 + } else if (fileExists("../data")) { + // G3D install on Windows + return "../data"; + } else if (fileExists("../data-files")) { + // G3D source on Windows + return "../data-files"; +# else + } else if (fileExists("../../../../data")) { + // G3D install on Unix + return "../../../../data"; + } else if (fileExists("../../../../data-files")) { + // G3D source on Unix + return "../../../../data-files"; +# endif + } else { + return ""; + } +} + + +const std::string& System::build() { + const static std::string b = +# ifdef _DEBUG + "Debug"; +# else + "Release"; +# endif + + return b; +} + + +static G3DEndian checkEndian() { + int32 a = 1; + if (*(uint8*)&a == 1) { + return G3D_LITTLE_ENDIAN; + } else { + return G3D_BIG_ENDIAN; + } +} + + +static bool checkForCPUID() { + // all known supported architectures have cpuid + // add cases for incompatible architectures if they are added + // e.g., if we ever support __powerpc__ being defined again + + return true; +} + + +void System::getStandardProcessorExtensions() { +#if ! defined(G3D_OSX) || defined(G3D_OSX_INTEL) + if (! m_hasCPUID) { + return; + } + + uint32 eaxreg = 0, ebxreg = 0, ecxreg = 0, features = 0; + + cpuid(CPUID_PROCESSOR_FEATURES, eaxreg, ebxreg, ecxreg, features); + +# define checkBit(var, bit) ((var & (1 << bit)) ? true : false) + + m_hasRDTSC = checkBit(features, 4); + m_hasMMX = checkBit(features, 23); + m_hasSSE = checkBit(features, 25); + m_hasSSE2 = checkBit(features, 26); + // Bit 28 is HTT; not checked by G3D + + m_hasSSE3 = checkBit(ecxreg, 0); + + if (m_highestCPUIDFunction >= CPUID_EXTENDED_FEATURES) { + cpuid(CPUID_EXTENDED_FEATURES, eaxreg, ebxreg, ecxreg, features); + m_hasAMDMMX = checkBit(features, 22); // Only on AMD + m_has3DNOW = checkBit(features, 31); // Only on AMD + m_has3DNOW2 = checkBit(features, 30); // Only on AMD + } else { + m_hasAMDMMX = false; + m_has3DNOW = false; + m_has3DNOW2 = false; + } + +# undef checkBit +#endif +} + +#if defined(G3D_WIN32) && !defined(G3D_64BIT) + #pragma message("Port System::memcpy SIMD to all platforms") +/** Michael Herf's fast memcpy */ +void memcpyMMX(void* dst, const void* src, int nbytes) { + int remainingBytes = nbytes; + + if (nbytes > 64) { + _asm { + mov esi, src + mov edi, dst + mov ecx, nbytes + shr ecx, 6 // 64 bytes per iteration + + loop1: + movq mm1, 0[ESI] // Read in source data + movq mm2, 8[ESI] + movq mm3, 16[ESI] + movq mm4, 24[ESI] + movq mm5, 32[ESI] + movq mm6, 40[ESI] + movq mm7, 48[ESI] + movq mm0, 56[ESI] + + movntq 0[EDI], mm1 // Non-temporal stores + movntq 8[EDI], mm2 + movntq 16[EDI], mm3 + movntq 24[EDI], mm4 + movntq 32[EDI], mm5 + movntq 40[EDI], mm6 + movntq 48[EDI], mm7 + movntq 56[EDI], mm0 + + add esi, 64 + add edi, 64 + dec ecx + jnz loop1 + + emms + } + remainingBytes -= ((nbytes >> 6) << 6); + } + + if (remainingBytes > 0) { + // Memcpy the rest + memcpy((uint8*)dst + (nbytes - remainingBytes), + (const uint8*)src + (nbytes - remainingBytes), remainingBytes); + } +} +#endif + +void System::memcpy(void* dst, const void* src, size_t numBytes) { +#if defined(G3D_WIN32) && !defined(G3D_64BIT) + memcpyMMX(dst, src, numBytes); +#else + ::memcpy(dst, src, numBytes); +#endif +} + + +/** Michael Herf's fastest memset. n32 must be filled with the same + character repeated. */ +#if defined(G3D_WIN32) && !defined(G3D_64BIT) + #pragma message("Port System::memfill SIMD to all platforms") + +// On x86 processors, use MMX +void memfill(void *dst, int n32, unsigned long i) { + + int originalSize = i; + int bytesRemaining = i; + + if (i > 16) { + + bytesRemaining = i % 16; + i -= bytesRemaining; + __asm { + movq mm0, n32 + punpckldq mm0, mm0 + mov edi, dst + + loopwrite: + + movntq 0[edi], mm0 + movntq 8[edi], mm0 + + add edi, 16 + sub i, 16 + jg loopwrite + + emms + } + } + + if (bytesRemaining > 0) { + ::memset((uint8*)dst + (originalSize - bytesRemaining), n32, bytesRemaining); + } +} +#endif + + +void System::memset(void* dst, uint8 value, size_t numBytes) { +#if defined(G3D_WIN32) && !defined(G3D_64BIT) + uint32 v = value; + v = v + (v << 8) + (v << 16) + (v << 24); + G3D::memfill(dst, v, numBytes); +#else + ::memset(dst, value, numBytes); +#endif +} + + +/** Removes the 'd' that icompile / Morgan's VC convention appends. */ +static std::string computeAppName(const std::string& start) { + if (start.size() < 2) { + return start; + } + + if (start[start.size() - 1] == 'd') { + // Maybe remove the 'd'; see if ../ or ../../ has the same name + char tmp[1024]; + getcwd(tmp, sizeof(tmp)); + std::string drive, base, ext; + Array<std::string> path; + parseFilename(tmp, drive, path, base, ext); + + std::string shortName = start.substr(0, start.size() - 1); + + if ((path.size() > 1) && (toLower(path.last()) == toLower(shortName))) { + return shortName; + } + + if ((path.size() > 2) && (toLower(path[path.size() - 2]) == toLower(shortName))) { + return shortName; + } + } + + return start; +} + + +std::string& System::appName() { + static std::string n = computeAppName(filenameBase(currentProgramFilename())); + return n; +} + + +std::string System::currentProgramFilename() { + char filename[2048]; + +# ifdef G3D_WIN32 + { + GetModuleFileNameA(NULL, filename, sizeof(filename)); + } +# elif defined(G3D_OSX) + { + // Run the 'ps' program to extract the program name + // from the process ID. + int pid; + FILE* fd; + char cmd[80]; + pid = getpid(); + sprintf(cmd, "ps -p %d -o comm=\"\"", pid); + + fd = popen(cmd, "r"); + int s = fread(filename, 1, sizeof(filename), fd); + // filename will contain a newline. Overwrite it: + filename[s - 1] = '\0'; + } +# else + { + int ret = readlink("/proc/self/exe", filename, sizeof(filename)); + + // In case of an error, leave the handling up to the caller + if (ret == -1) { + return ""; + } + + debugAssert((int)sizeof(filename) > ret); + + // Ensure proper NULL termination + filename[ret] = 0; + } + #endif + + return filename; +} + + +void System::sleep(RealTime t) { + + // Overhead of calling this function, measured from a previous run. + static const RealTime OVERHEAD = 0.00006f; + + RealTime now = time(); + RealTime wakeupTime = now + t - OVERHEAD; + + RealTime remainingTime = wakeupTime - now; + RealTime sleepTime = 0; + + // On Windows, a "time slice" is measured in quanta of 3-5 ms (http://support.microsoft.com/kb/259025) + // Sleep(0) yields the remainder of the time slice, which could be a long time. + // A 1 ms minimum time experimentally kept the "Empty GApp" at nearly no CPU load at 100 fps, + // yet nailed the frame timing perfectly. + static RealTime minRealSleepTime = 3 * units::milliseconds(); + + while (remainingTime > 0) { + + if (remainingTime > minRealSleepTime * 2.5) { + // Safe to use Sleep with a time... sleep for half the remaining time + sleepTime = max(remainingTime * 0.5, 0.0005); + } else if (remainingTime > minRealSleepTime) { + // Safe to use Sleep with a zero time; + // causes the program to yield only + // the current time slice, and then return. + sleepTime = 0; + } else { + // Not safe to use Sleep; busy wait + sleepTime = -1; + } + + if (sleepTime >= 0) { + #ifdef G3D_WIN32 + // Translate to milliseconds + Sleep((int)(sleepTime * 1e3)); + #else + // Translate to microseconds + usleep((int)(sleepTime * 1e6)); + #endif + } + + now = time(); + remainingTime = wakeupTime - now; + } +} + + +void System::consoleClearScreen() { +# ifdef G3D_WIN32 + system("cls"); +# else + system("clear"); +# endif +} + + +bool System::consoleKeyPressed() { + #ifdef G3D_WIN32 + + return _kbhit() != 0; + + #else + + static const int STDIN = 0; + static bool initialized = false; + + if (! initialized) { + // Use termios to turn off line buffering + termios term; + tcgetattr(STDIN, &term); + term.c_lflag &= ~ICANON; + tcsetattr(STDIN, TCSANOW, &term); + setbuf(stdin, NULL); + initialized = true; + } + + #ifdef G3D_LINUX + + int bytesWaiting; + ioctl(STDIN, FIONREAD, &bytesWaiting); + return bytesWaiting; + + #else + + timeval timeout; + fd_set rdset; + + FD_ZERO(&rdset); + FD_SET(STDIN, &rdset); + timeout.tv_sec = 0; + timeout.tv_usec = 0; + + return select(STDIN + 1, &rdset, NULL, NULL, &timeout); + #endif + #endif +} + + +int System::consoleReadKey() { +# ifdef G3D_WIN32 + return _getch(); +# else + char c; + read(0, &c, 1); + return c; +# endif +} + + +void System::initTime() { + #ifdef G3D_WIN32 + if (QueryPerformanceFrequency(&m_counterFrequency)) { + QueryPerformanceCounter(&m_start); + } + + struct _timeb t; + _ftime(&t); + + m_realWorldGetTickTime0 = (RealTime)t.time - t.timezone * G3D::MINUTE + (t.dstflag ? G3D::HOUR : 0); + + #else + gettimeofday(&m_start, NULL); + // "sse" = "seconds since epoch". The time + // function returns the seconds since the epoch + // GMT (perhaps more correctly called UTC). + time_t gmt = ::time(NULL); + + // No call to free or delete is needed, but subsequent + // calls to asctime, ctime, mktime, etc. might overwrite + // local_time_vals. + tm* localTimeVals = localtime(&gmt); + + time_t local = gmt; + + if (localTimeVals) { + // tm_gmtoff is already corrected for daylight savings. + local = local + localTimeVals->tm_gmtoff; + } + + m_realWorldGetTickTime0 = local; + #endif +} + + +RealTime System::time() { +# ifdef G3D_WIN32 + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + + return ((RealTime)(now.QuadPart - instance().m_start.QuadPart) / + instance().m_counterFrequency.QuadPart) + instance().m_realWorldGetTickTime0; +# else + // Linux resolution defaults to 100Hz. + // There is no need to do a separate RDTSC call as gettimeofday + // actually uses RDTSC when on systems that support it, otherwise + // it uses the system clock. + struct timeval now; + gettimeofday(&now, NULL); + + return (now.tv_sec - instance().m_start.tv_sec) + + (now.tv_usec - instance().m_start.tv_usec) / 1e6 + + instance().m_realWorldGetTickTime0; +# endif +} + + +//////////////////////////////////////////////////////////////// + +#define REALPTR_TO_USERPTR(x) ((uint8*)(x) + sizeof (void *)) +#define USERPTR_TO_REALPTR(x) ((uint8*)(x) - sizeof (void *)) +#define REALBLOCK_SIZE(x) ((x) + sizeof (void *)) + +class BufferPool { +public: + + /** Only store buffers up to these sizes (in bytes) in each pool-> + Different pools have different management strategies. + + A large block is preallocated for tiny buffers; they are used with + tremendous frequency. Other buffers are allocated as demanded. + Tiny buffers are 128 bytes long because that seems to align well with + cache sizes on many machines. + */ + enum {tinyBufferSize = 128, smallBufferSize = 1024, medBufferSize = 4096}; + + /** + Most buffers we're allowed to store. + 250000 * 128 = 32 MB (preallocated) + 10000 * 1024 = 10 MB (allocated on demand) + 1024 * 4096 = 4 MB (allocated on demand) + */ + enum {maxTinyBuffers = 250000, maxSmallBuffers = 10000, maxMedBuffers = 1024}; + +private: + + class MemBlock { + public: + void* ptr; + size_t bytes; + + inline MemBlock() : ptr(NULL), bytes(0) {} + inline MemBlock(void* p, size_t b) : ptr(p), bytes(b) {} + }; + + MemBlock smallPool[maxSmallBuffers]; + int smallPoolSize; + + MemBlock medPool[maxMedBuffers]; + int medPoolSize; + + /** The tiny pool is a single block of storage into which all tiny + objects are allocated. This provides better locality for + small objects and avoids the search time, since all tiny + blocks are exactly the same size. */ + void* tinyPool[maxTinyBuffers]; + int tinyPoolSize; + + /** Pointer to the data in the tiny pool */ + void* tinyHeap; + + Spinlock m_lock; + + void lock() { + m_lock.lock(); + } + + void unlock() { + m_lock.unlock(); + } + +#if 0 //-----------------------------------------------old mutex +# ifdef G3D_WIN32 + CRITICAL_SECTION mutex; +# else + pthread_mutex_t mutex; +# endif + + /** Provide synchronization between threads */ + void lock() { +# ifdef G3D_WIN32 + EnterCriticalSection(&mutex); +# else + pthread_mutex_lock(&mutex); +# endif + } + + void unlock() { +# ifdef G3D_WIN32 + LeaveCriticalSection(&mutex); +# else + pthread_mutex_unlock(&mutex); +# endif + } +#endif //-------------------------------------------old mutex + + /** + Malloc out of the tiny heap. Returns NULL if allocation failed. + */ + inline void* tinyMalloc(size_t bytes) { + // Note that we ignore the actual byte size + // and create a constant size block. + (void)bytes; + assert(tinyBufferSize >= bytes); + + void* ptr = NULL; + + if (tinyPoolSize > 0) { + --tinyPoolSize; + + // Return the old last pointer from the freelist + ptr = tinyPool[tinyPoolSize]; + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + assert(tinyPool[tinyPoolSize - 1] != ptr); + // "System::malloc heap corruption detected: " + // "the last two pointers on the freelist are identical (during tinyMalloc)."); + } +# endif + + // NULL out the entry to help detect corruption + tinyPool[tinyPoolSize] = NULL; + } + + return ptr; + } + + /** Returns true if this is a pointer into the tiny heap. */ + bool inTinyHeap(void* ptr) { + return + (ptr >= tinyHeap) && + (ptr < (uint8*)tinyHeap + maxTinyBuffers * tinyBufferSize); + } + + void tinyFree(void* ptr) { + assert(ptr); + assert(tinyPoolSize < maxTinyBuffers); + // "Tried to free a tiny pool buffer when the tiny pool freelist is full."); + +# ifdef G3D_DEBUG + if (tinyPoolSize > 0) { + void* prevOnHeap = tinyPool[tinyPoolSize - 1]; + assert(prevOnHeap != ptr); +// "System::malloc heap corruption detected: " +// "the last two pointers on the freelist are identical (during tinyFree)."); + } +# endif + + assert(tinyPool[tinyPoolSize] == NULL); + + // Put the pointer back into the free list + tinyPool[tinyPoolSize] = ptr; + ++tinyPoolSize; + + } + + void flushPool(MemBlock* pool, int& poolSize) { + for (int i = 0; i < poolSize; ++i) { + ::free(pool[i].ptr); + pool[i].ptr = NULL; + pool[i].bytes = 0; + } + poolSize = 0; + } + + + /** Allocate out of a specific pool-> Return NULL if no suitable + memory was found. + + */ + void* malloc(MemBlock* pool, int& poolSize, size_t bytes) { + + // OPT: find the smallest block that satisfies the request. + + // See if there's something we can use in the buffer pool-> + // Search backwards since usually we'll re-use the last one. + for (int i = (int)poolSize - 1; i >= 0; --i) { + if (pool[i].bytes >= bytes) { + // We found a suitable entry in the pool-> + + // No need to offset the pointer; it is already offset + void* ptr = pool[i].ptr; + + // Remove this element from the pool + --poolSize; + pool[i] = pool[poolSize]; + + return ptr; + } + } + + return NULL; + } + +public: + + /** Count of memory allocations that have occurred. */ + int totalMallocs; + int mallocsFromTinyPool; + int mallocsFromSmallPool; + int mallocsFromMedPool; + + /** Amount of memory currently allocated (according to the application). + This does not count the memory still remaining in the buffer pool, + but does count extra memory required for rounding off to the size + of a buffer. + Primarily useful for detecting leaks.*/ + // TODO: make me an atomic int! + volatile int bytesAllocated; + + BufferPool() { + totalMallocs = 0; + + mallocsFromTinyPool = 0; + mallocsFromSmallPool = 0; + mallocsFromMedPool = 0; + + bytesAllocated = true; + + tinyPoolSize = 0; + tinyHeap = NULL; + + smallPoolSize = 0; + + medPoolSize = 0; + + + // Initialize the tiny heap as a bunch of pointers into one + // pre-allocated buffer. + tinyHeap = ::malloc(maxTinyBuffers * tinyBufferSize); + for (int i = 0; i < maxTinyBuffers; ++i) { + tinyPool[i] = (uint8*)tinyHeap + (tinyBufferSize * i); + } + tinyPoolSize = maxTinyBuffers; + +#if 0 ///---------------------------------- old mutex +# ifdef G3D_WIN32 + InitializeCriticalSection(&mutex); +# else + pthread_mutex_init(&mutex, NULL); +# endif +#endif ///---------------------------------- old mutex + } + + + ~BufferPool() { + ::free(tinyHeap); +#if 0 //-------------------------------- old mutex +# ifdef G3D_WIN32 + DeleteCriticalSection(&mutex); +# else + // No destruction on pthreads +# endif +#endif //--------------------------------old mutex + } + + + void* realloc(void* ptr, size_t bytes) { + if (ptr == NULL) { + return malloc(bytes); + } + + if (inTinyHeap(ptr)) { + if (bytes <= tinyBufferSize) { + // The old pointer actually had enough space. + return ptr; + } else { + // Free the old pointer and malloc + + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, tinyBufferSize); + tinyFree(ptr); + return newPtr; + + } + } else { + // In one of our heaps. + + // See how big the block really was + size_t realSize = *(uint32*)USERPTR_TO_REALPTR(ptr); + if (bytes <= realSize) { + // The old block was big enough. + return ptr; + } + + // Need to reallocate + void* newPtr = malloc(bytes); + System::memcpy(newPtr, ptr, realSize); + free(ptr); + return newPtr; + } + } + + + void* malloc(size_t bytes) { + lock(); + ++totalMallocs; + + if (bytes <= tinyBufferSize) { + + void* ptr = tinyMalloc(bytes); + + if (ptr) { + ++mallocsFromTinyPool; + unlock(); + return ptr; + } + + } + + // Failure to allocate a tiny buffer is allowed to flow + // through to a small buffer + if (bytes <= smallBufferSize) { + + void* ptr = malloc(smallPool, smallPoolSize, bytes); + + if (ptr) { + ++mallocsFromSmallPool; + unlock(); + return ptr; + } + + } else if (bytes <= medBufferSize) { + // Note that a small allocation failure does *not* fall + // through into a medium allocation because that would + // waste the medium buffer's resources. + + void* ptr = malloc(medPool, medPoolSize, bytes); + + if (ptr) { + ++mallocsFromMedPool; + unlock(); + debugAssertM(ptr != NULL, "BufferPool::malloc returned NULL"); + return ptr; + } + } + + bytesAllocated += REALBLOCK_SIZE(bytes); + unlock(); + + // Heap allocate + + // Allocate 4 extra bytes for our size header (unfortunate, + // since malloc already added its own header). + void* ptr = ::malloc(REALBLOCK_SIZE(bytes)); + + if (ptr == NULL) { + // Flush memory pools to try and recover space + flushPool(smallPool, smallPoolSize); + flushPool(medPool, medPoolSize); + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + + if (ptr == NULL) { + if ((System::outOfMemoryCallback() != NULL) && + (System::outOfMemoryCallback()(REALBLOCK_SIZE(bytes), true) == true)) { + // Re-attempt the malloc + ptr = ::malloc(REALBLOCK_SIZE(bytes)); + } + } + + if (ptr == NULL) { + if (System::outOfMemoryCallback() != NULL) { + // Notify the application + System::outOfMemoryCallback()(REALBLOCK_SIZE(bytes), false); + } +# ifdef G3D_DEBUG + debugPrintf("::malloc(%d) returned NULL\n", (int)REALBLOCK_SIZE(bytes)); +# endif + debugAssertM(ptr != NULL, + "::malloc returned NULL. Either the " + "operating system is out of memory or the " + "heap is corrupt."); + return NULL; + } + + *(uint32*)ptr = bytes; + + return REALPTR_TO_USERPTR(ptr); + } + + + void free(void* ptr) { + if (ptr == NULL) { + // Free does nothing on null pointers + return; + } + + assert(isValidPointer(ptr)); + + if (inTinyHeap(ptr)) { + lock(); + tinyFree(ptr); + unlock(); + return; + } + + uint32 bytes = *(uint32*)USERPTR_TO_REALPTR(ptr); + + lock(); + if (bytes <= smallBufferSize) { + if (smallPoolSize < maxSmallBuffers) { + smallPool[smallPoolSize] = MemBlock(ptr, bytes); + ++smallPoolSize; + unlock(); + return; + } + } else if (bytes <= medBufferSize) { + if (medPoolSize < maxMedBuffers) { + medPool[medPoolSize] = MemBlock(ptr, bytes); + ++medPoolSize; + unlock(); + return; + } + } + bytesAllocated -= REALBLOCK_SIZE(bytes); + unlock(); + + // Free; the buffer pools are full or this is too big to store. + ::free(USERPTR_TO_REALPTR(ptr)); + } + + std::string performance() const { + if (totalMallocs > 0) { + int pooled = mallocsFromTinyPool + + mallocsFromSmallPool + + mallocsFromMedPool; + + int total = totalMallocs; + + return format("malloc performance: %5.1f%% <= %db, %5.1f%% <= %db, " + "%5.1f%% <= %db, %5.1f%% > %db", + 100.0 * mallocsFromTinyPool / total, + BufferPool::tinyBufferSize, + 100.0 * mallocsFromSmallPool / total, + BufferPool::smallBufferSize, + 100.0 * mallocsFromMedPool / total, + BufferPool::medBufferSize, + 100.0 * (1.0 - (double)pooled / total), + BufferPool::medBufferSize); + } else { + return "No System::malloc calls made yet."; + } + } + + std::string status() const { + return format("preallocated shared buffers: %5d/%d x %db", + maxTinyBuffers - tinyPoolSize, maxTinyBuffers, tinyBufferSize); + } +}; + +// Dynamically allocated because we need to ensure that +// the buffer pool is still around when the last global variable +// is deallocated. +static BufferPool* bufferpool = NULL; + +std::string System::mallocPerformance() { +#ifndef NO_BUFFERPOOL + return bufferpool->performance(); +#else + return "NO_BUFFERPOOL"; +#endif +} + +std::string System::mallocStatus() { +#ifndef NO_BUFFERPOOL + return bufferpool->status(); +#else + return "NO_BUFFERPOOL"; +#endif +} + + +void System::resetMallocPerformanceCounters() { +#ifndef NO_BUFFERPOOL + bufferpool->totalMallocs = 0; + bufferpool->mallocsFromMedPool = 0; + bufferpool->mallocsFromSmallPool = 0; + bufferpool->mallocsFromTinyPool = 0; +#endif +} + + +#ifndef NO_BUFFERPOOL +inline void initMem() { + // Putting the test here ensures that the system is always + // initialized, even when globals are being allocated. + static bool initialized = false; + if (! initialized) { + bufferpool = new BufferPool(); + initialized = true; + } +} +#endif + + +void* System::malloc(size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->malloc(bytes); +#else + return ::malloc(bytes); +#endif +} + +void* System::calloc(size_t n, size_t x) { +#ifndef NO_BUFFERPOOL + void* b = System::malloc(n * x); + debugAssertM(b != NULL, "System::malloc returned NULL"); + debugAssertM(isValidHeapPointer(b), "System::malloc returned an invalid pointer"); + System::memset(b, 0, n * x); + return b; +#else + return ::calloc(n, x); +#endif +} + + +void* System::realloc(void* block, size_t bytes) { +#ifndef NO_BUFFERPOOL + initMem(); + return bufferpool->realloc(block, bytes); +#else + return ::realloc(block, bytes); +#endif +} + + +void System::free(void* p) { +#ifndef NO_BUFFERPOOL + bufferpool->free(p); +#else + return ::free(p); +#endif +} + + +void* System::alignedMalloc(size_t bytes, size_t alignment) { + + alwaysAssertM(isPow2(alignment), "alignment must be a power of 2"); + + // We must align to at least a word boundary. + alignment = iMax(alignment, sizeof(void *)); + + // Pad the allocation size with the alignment size and the + // size of the redirect pointer. + size_t totalBytes = bytes + alignment + sizeof(void*); + + size_t truePtr = (size_t)System::malloc(totalBytes); + + if (truePtr == 0) { + // malloc returned NULL + return NULL; + } + + debugAssert(isValidHeapPointer((void*)truePtr)); + #ifdef G3D_WIN32 + // The blocks we return will not be valid Win32 debug heap + // pointers because they are offset + // debugAssert(_CrtIsValidPointer((void*)truePtr, totalBytes, TRUE) ); + #endif + + // The return pointer will be the next aligned location (we must at least + // leave space for the redirect pointer, however). + size_t alignedPtr = truePtr + sizeof(void*); + + // 2^n - 1 has the form 1111... in binary. + uint32 bitMask = (alignment - 1); + + // Advance forward until we reach an aligned location. + while ((alignedPtr & bitMask) != 0) { + alignedPtr += sizeof(void*); + } + + debugAssert(alignedPtr - truePtr + bytes <= totalBytes); + + // Immediately before the aligned location, write the true array location + // so that we can free it correctly. + size_t* redirectPtr = (size_t *)(alignedPtr - sizeof(void *)); + redirectPtr[0] = truePtr; + + debugAssert(isValidHeapPointer((void*)truePtr)); + + #ifdef G3D_WIN32 + debugAssert( _CrtIsValidPointer((void*)alignedPtr, bytes, TRUE) ); + #endif + return (void *)alignedPtr; +} + + +void System::alignedFree(void* _ptr) { + if (_ptr == NULL) { + return; + } + + size_t alignedPtr = (size_t)_ptr; + + // Back up one word from the pointer the user passed in. + // We now have a pointer to a pointer to the true start + // of the memory block. + size_t* redirectPtr = (size_t*)(alignedPtr - sizeof(void *)); + + // Dereference that pointer so that ptr = true start + void* truePtr = (void*)redirectPtr[0]; + + debugAssert(isValidHeapPointer((void*)truePtr)); + System::free(truePtr); +} + + +void System::setEnv(const std::string& name, const std::string& value) { + std::string cmd = name + "=" + value; +# ifdef G3D_WIN32 + _putenv(cmd.c_str()); +# else + // Many linux implementations of putenv expect char* + putenv(const_cast<char*>(cmd.c_str())); +# endif +} + + +const char* System::getEnv(const std::string& name) { + return getenv(name.c_str()); +} + + +static void var(TextOutput& t, const std::string& name, const std::string& val) { + t.writeSymbols(name,"="); + t.writeString(val); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const bool val) { + t.writeSymbols(name, "=", val ? "Yes" : "No"); + t.writeNewline(); +} + + +static void var(TextOutput& t, const std::string& name, const int val) { + t.writeSymbols(name,"="); + t.writeNumber(val); + t.writeNewline(); +} + + +void System::describeSystem( + std::string& s) { + + TextOutput t; + describeSystem(t); + t.commitString(s); +} + +void System::describeSystem( + TextOutput& t) { + + t.writeSymbols("App", "{"); + t.writeNewline(); + t.pushIndent(); + { + var(t, "Name", System::currentProgramFilename()); + char cwd[1024]; + getcwd(cwd, 1024); + var(t, "cwd", std::string(cwd)); + } + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("OS", "{"); + t.writeNewline(); + t.pushIndent(); + { + var(t, "Name", System::operatingSystem()); + } + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("CPU", "{"); + t.writeNewline(); + t.pushIndent(); + { + var(t, "Vendor", System::cpuVendor()); + var(t, "Architecture", System::cpuArchitecture()); + var(t, "hasCPUID", System::hasCPUID()); + var(t, "hasMMX", System::hasMMX()); + var(t, "hasSSE", System::hasSSE()); + var(t, "hasSSE2", System::hasSSE2()); + var(t, "hasSSE3", System::hasSSE3()); + var(t, "has3DNow", System::has3DNow()); + var(t, "hasRDTSC", System::hasRDTSC()); + var(t, "numCores", System::numCores()); + } + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); + + t.writeSymbols("G3D", "{"); + t.writeNewline(); + t.pushIndent(); + { + var(t, "Link version", G3D_VER); + var(t, "Compile version", System::version()); + } + t.popIndent(); + t.writeSymbols("}"); + t.writeNewline(); + t.writeNewline(); +} + + +void System::setClipboardText(const std::string& s) { +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, s.size() + 1); + if (hMem) { + char *pMem = (char*)GlobalLock(hMem); + strcpy(pMem, s.c_str()); + GlobalUnlock(hMem); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + } + + CloseClipboard(); + GlobalFree(hMem); + } +# endif +} + + +std::string System::getClipboardText() { + std::string s; + +# ifdef G3D_WIN32 + if (OpenClipboard(NULL)) { + HANDLE h = GetClipboardData(CF_TEXT); + + if (h) { + char* temp = (char*)GlobalLock(h); + if (temp) { + s = temp; + } + temp = NULL; + GlobalUnlock(h); + } + CloseClipboard(); + } +# endif + return s; +} + + +std::string System::currentDateString() { + time_t t1; + ::time(&t1); + tm* t = localtime(&t1); + return format("%d-%02d-%02d", t->tm_year + 1900, t->tm_mon + 1, t->tm_mday); +} + +#ifdef _MSC_VER + +// VC on Intel +void System::cpuid(CPUIDFunction func, uint32& areg, uint32& breg, uint32& creg, uint32& dreg) { +#if !defined(G3D_64BIT) + // Can't copy from assembler direct to a function argument (which is on the stack) in VC. + uint32 a,b,c,d; + + // Intel assembler syntax + __asm { + mov eax, func // eax <- func + mov ecx, 0 + cpuid + mov a, eax + mov b, ebx + mov c, ecx + mov d, edx + } + areg = a; + breg = b; + creg = c; + dreg = d; +#else + int CPUInfo[4]; + __cpuid(CPUInfo, func); + memcpy(&areg, &CPUInfo[0], 4); + memcpy(&breg, &CPUInfo[1], 4); + memcpy(&creg, &CPUInfo[2], 4); + memcpy(&dreg, &CPUInfo[3], 4); +#endif +} + +#elif defined(G3D_OSX) && ! defined(G3D_OSX_INTEL) + +// non-intel OS X; no CPUID +void System::cpuid(CPUIDFunction func, uint32& eax, uint32& ebx, uint32& ecx, uint32& edx) { + eax = 0; + ebx = 0; + ecx = 0; + edx = 0; +} + +#else + +// See http://sam.zoy.org/blog/2007-04-13-shlib-with-non-pic-code-have-inline-assembly-and-pic-mix-well +// for a discussion of why the second version saves ebx; it allows 32-bit code to compile with the -fPIC option. +// On 64-bit x86, PIC code has a dedicated rip register for PIC so there is no ebx conflict. +void System::cpuid(CPUIDFunction func, uint32& eax, uint32& ebx, uint32& ecx, uint32& edx) { +#if ! defined(__PIC__) || defined(__x86_64__) + // AT&T assembler syntax + asm volatile( + "movl $0, %%ecx \n\n" /* Wipe ecx */ + "cpuid \n\t" + : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) + : "a"(func)); +#else + // AT&T assembler syntax + asm volatile( + "pushl %%ebx \n\t" /* save ebx */ + "movl $0, %%ecx \n\n" /* Wipe ecx */ + "cpuid \n\t" + "movl %%ebx, %1 \n\t" /* save what cpuid just put in %ebx */ + "popl %%ebx \n\t" /* restore the old ebx */ + : "=a"(eax), "=r"(ebx), "=c"(ecx), "=d"(edx) + : "a"(func)); +#endif +} + +#endif + +} // namespace diff --git a/externals/g3dlite/TextInput.cpp b/externals/g3dlite/TextInput.cpp new file mode 100644 index 00000000000..7276d8c66b2 --- /dev/null +++ b/externals/g3dlite/TextInput.cpp @@ -0,0 +1,1136 @@ +/** + @file TextInput.cpp + + @author Morgan McGuire, graphics3d.com + + @cite Based on a lexer written by Aaron Orenstein. + + @created 2001-11-27 + @edited 2008-07-14 + */ + +#include "G3D/fileutils.h" +#include "G3D/TextInput.h" +#include "G3D/BinaryInput.h" +#include "G3D/stringutils.h" + +#ifdef _MSC_VER +# pragma warning (push) +// conversion from 'int' to 'char', possible loss of data (TODO: fix underlying problems) +# pragma warning (disable: 4244) +#endif + +namespace G3D { + +Token TextInput::readSignificant() { + Token t; + do { + t = read(); + } while ((t.type() == Token::COMMENT) || (t.type() == Token::NEWLINE)); + return t; +} + + +double Token::number() const { + if (_type == NUMBER) { + std::string s = toLower(_string); + if (s == "-1.#ind00") { + return nan(); + } + + if (s == "1.#inf00") { + return inf(); + } + + if (s == "-1.#inf00") { + return -inf(); + } + + double n; + if ((_string.length() > 2) && + (_string[0] == '0') && + (_string[1] == 'x')) { + // Hex + uint32 i; + sscanf(_string.c_str(), "%x", &i); + n = i; + } else { + sscanf(_string.c_str(), "%lg", &n); + } + return n; + } else { + return 0.0; + } +} + + +TextInput::Settings::Settings () : + cppBlockComments(true), + cppLineComments(true), + otherLineComments(true), + escapeSequencesInStrings(true), + otherCommentCharacter('\0'), + otherCommentCharacter2('\0'), + generateCommentTokens(false), + generateNewlineTokens(false), + signedNumbers(true), + singleQuotedStrings(true), + singleQuoteCharacter('\''), + sourceFileName(), + startingLineNumberOffset(0), + msvcSpecials(true), + proofSymbols(false), + caseSensitive(true) +{ + trueSymbols.insert("true"); + falseSymbols.insert("false"); +} + + +Token TextInput::peek() { + if (stack.size() == 0) { + Token t = nextToken(); + push(t); + } + + return stack.front(); +} + + +int TextInput::peekLineNumber() { + return peek().line(); +} + + +int TextInput::peekCharacterNumber() { + return peek().character(); +} + + +Token TextInput::read() { + if (stack.size() > 0) { + Token t = stack.front(); + stack.pop_front(); + return t; + } else { + return nextToken(); + } +} + +static void toUpper(Set<std::string>& set) { + Array<std::string> symbols; + set.getMembers(symbols); + set.clear(); + for (int i = 0; i < symbols.size(); ++i) { + set.insert(toUpper(symbols[i])); + } +} + +void TextInput::init() { + currentCharOffset = 0; + charNumber = 1; + lineNumber = 1 + options.startingLineNumberOffset; + + if (! options.caseSensitive) { + // Convert true and false symbols to all uppercase for fast comparisons + toUpper(options.trueSymbols); + toUpper(options.falseSymbols); + } +} + + +void TextInput::push(const Token& t) { + stack.push_front(t); +} + + +bool TextInput::hasMore() { + return (peek()._type != Token::END); +} + + +int TextInput::eatInputChar() { + // Don't go off the end + if (currentCharOffset >= buffer.length()) { + return EOF; + } + + unsigned char c = buffer[currentCharOffset]; + ++currentCharOffset; + + // update lineNumber and charNumber to reflect the location of the *next* + // character which will be read. + + // increment line number for \r, \n and \r\n which matches Token::NEWLINE parsing + if (c == '\r') { + ++lineNumber; + charNumber = 1; + + // check for \r\n + if (currentCharOffset < buffer.length()) { + unsigned char c2 = buffer[currentCharOffset]; + if (c2 == '\n') { + c = c2; + ++currentCharOffset; + } + } + } else if (c == '\n') { + ++lineNumber; + charNumber = 1; + } else { + ++charNumber; + } + + return c; +} + +int TextInput::peekInputChar(int distance) { + // Don't go off the end + if ((currentCharOffset + distance) >= buffer.length()) { + return EOF; + } + + unsigned char c = buffer[currentCharOffset + distance]; + return c; +} + + +Token TextInput::nextToken() { + Token t; + + t._line = lineNumber; + t._character = charNumber; + t._type = Token::END; + t._extendedType = Token::END_TYPE; + + int c = peekInputChar(); + if (c == EOF) { + return t; + } + + // loop through white space, newlines and comments + // found before other tokens + bool whitespaceDone = false; + while (! whitespaceDone) { + whitespaceDone = true; + + // generate newlines tokens for '\n' and '\r' and '\r\n' + if (options.generateNewlineTokens && isNewline(c)) { + t._type = Token::NEWLINE; + t._extendedType = Token::NEWLINE_TYPE; + t._string = c; + + int c2 = peekInputChar(1); + if (c == '\r' && c2 == '\n') { + t._string += c2; + } + + eatInputChar(); + return t; + } else { + // Consume whitespace + while (isWhiteSpace(c)) { + c = eatAndPeekInputChar(); + } + } + + // update line and character number to include discarded whitespace + t._line = lineNumber; + t._character = charNumber; + + int c2 = peekInputChar(1); + + // parse comments and generate tokens if enabled + std::string commentString; + + // check for line comments first + bool isLineComment = false; + if (options.cppLineComments && (c == '/' && c2 == '/')) { + // set start of line comment and eat markers + isLineComment = true; + eatInputChar(); + eatInputChar(); + } else if ( options.otherCommentCharacter && + (options.otherCommentCharacter != '\0' && c == options.otherCommentCharacter) ) { + // set start of line comment and eat markers + isLineComment = true; + eatInputChar(); + } else if ( options.otherCommentCharacter && + (options.otherCommentCharacter2 != '\0' && c == options.otherCommentCharacter2) ) { + // set start of line comment and eat markers + isLineComment = true; + eatInputChar(); + } + + if (isLineComment) { + + // consume line comment to newline or EOF + c = peekInputChar(); + while (! isNewline(c) && c != EOF) { + // build comment string for token + commentString += c; + + c = eatAndPeekInputChar(); + } + + if (options.generateCommentTokens) { + t._type = Token::COMMENT; + t._extendedType = Token::LINE_COMMENT_TYPE; + t._string = commentString; + return t; + } else { + // There is whitespace after the comment (in particular, the + // newline that terminates the comment). There might also be + // whitespace at the start of the next line. + whitespaceDone = false; + } + + } else if (options.cppBlockComments && (c == '/' && c2 == '*')) { + // consume block comment to end-marker or EOF + + // consume both start-comment chars, can't let the trailing one + // help close the comment. + eatInputChar(); + eatInputChar(); + + c = peekInputChar(); + c2 = peekInputChar(1); + while (! ((c == '*') && (c2 == '/')) && (c != EOF)) { + commentString += c; + + eatInputChar(); + c = c2; + c2 = peekInputChar(1); + } + eatInputChar(); // eat closing '*' + eatInputChar(); // eat closing '/' + + c = peekInputChar(); + + if (options.generateCommentTokens) { + t._type = Token::COMMENT; + t._extendedType = Token::BLOCK_COMMENT_TYPE; + t._string = commentString; + return t; + } else { + // There is whitespace after the comment (in particular, the + // newline that terminates the comment). There might also be + // whitespace at the start of the next line. + whitespaceDone = false; + } + } + + } // while (! whitespaceDone) + + t._line = lineNumber; + t._character = charNumber; + + // handle EOF + if (c == EOF) { + return t; + } + + // Extended ASCII parses as itself, except for EOF + if (c > 127 && c < 255) { + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + t._string = c; + c = eatAndPeekInputChar(); + } + + + // Perform appropriate setup for a symbol (including setting up the token + // string to start with c), eat the input character, and overwrite + // 'c' with the peeked next input character. +#define SETUP_SYMBOL(c) \ + { \ + t._type = Token::SYMBOL; \ + t._extendedType = Token::SYMBOL_TYPE; \ + t._string = c; \ + c = eatAndPeekInputChar(); \ + } + + switch (c) { + + case '@': // Simple symbols -> just themselves. + case '(': + case ')': + case ',': + case ';': + case '{': + case '}': + case '[': + case ']': + case '#': + case '$': + case '?': + case '%': + SETUP_SYMBOL(c); + return t; + + case '-': // negative number, -, --, -=, or -> + SETUP_SYMBOL(c); + + switch (c) { + case '>': // -> + case '-': // -- + case '=': // -= + t._string += c; + eatInputChar(); + return t; + } + + if (options.signedNumbers + && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) { + + // Negative number. 'c' is still the first digit, and is + // the next input char. + + goto numLabel; + } + + // plain - + return t; + + case '+': // positive number, +, ++, or += + SETUP_SYMBOL(c); + + switch (c) { + case '+': // ++ + case '=': // += + t._string += c; + eatInputChar(); + return t; + } + + if (options.signedNumbers + && (isDigit(c) || (c == '.' && isDigit(peekInputChar(1))))) { + + // Positive number. 'c' is still the first digit, and is + // the next input char. + + goto numLabel; + } + + return t; + + case ':': // : or :: or ::> or ::= or := or :> + SETUP_SYMBOL(c); + + if (c == ':') { + t._string += c; + eatInputChar(); + + if (options.proofSymbols) { + c = peekInputChar(0); + + if ((c == '>') || (c == '=')) { + t._string += c; + eatInputChar(); + } + } + } + else if (options.proofSymbols && (c == '=' || c == '>')) { + t._string += c; + eatInputChar(); + } + return t; + + case '=': // = or == or => + SETUP_SYMBOL(c); + + if (c == '=') { + t._string += c; + eatInputChar(); + return t; + } else if (options.proofSymbols && (c == '>')) { + t._string += c; + eatInputChar(); + return t; + } + return t; + + case '*': // * or *= + case '/': // / or /= + case '!': // ! or != + case '~': // ~ or ~= + case '^': // ^ or ^= + SETUP_SYMBOL(c); + + if (c == '=') { + t._string += c; + eatInputChar(); + return t; + } + return t; + + case '>': // >, >>,or >= + case '<': // <<, <<, or <= or <- or <: + case '|': // ||, ||, or |= or |- + case '&': // &, &&, or &= + { + int orig_c = c; + SETUP_SYMBOL(c); + + if ((c == '=') || (orig_c == c)) { + t._string += c; + eatInputChar(); + return t; + } else if (options.proofSymbols) { + if ((orig_c == '<') && (c == '-')) { + t._string += c; + eatInputChar(); + } else if ((orig_c == '|') && (c == '-')) { + t._string += c; + eatInputChar(); + } else if ((orig_c == '<') && (c == ':')) { + t._string += c; + + c = eatAndPeekInputChar(); + + if (c == ':') { + t._string += c; + eatInputChar(); + } + } + } + } + return t; + + case '\\': // backslash or escaped comment char. + SETUP_SYMBOL(c); + + if ((options.otherCommentCharacter != '\0' + && c == options.otherCommentCharacter) + || (options.otherCommentCharacter2 != '\0' + && c == options.otherCommentCharacter2)) { + + // escaped comment character. Return the raw comment + // char (no backslash). + + t._string = c; + eatInputChar(); + return t; + } + return t; + + case '.': // number, ., .., or ... + if (isDigit(peekInputChar(1))) { + // We're parsing a float that began without a leading zero + goto numLabel; + } + + SETUP_SYMBOL(c); + + if (c == '.') { // .. or ... + t._string += c; + c = eatAndPeekInputChar(); + + if (c == '.') { // ... + t._string += c; + eatInputChar(); + } + return t; + } + + return t; + + } // switch (c) + +#undef SETUP_SYMBOL + +numLabel: + if (isDigit(c) || (c == '.')) { + + // A number. Note-- single dots have been + // parsed already, so a . indicates a number + // less than 1 in floating point form. + + // [0-9]*(\.[0-9][f]) or [0-9]+ or 0x[0-9,A-F]+ + + if (t._string != "-") { + // If we picked up a leading "-" sign above, keep it, + // otherwise drop the string parsed thus far + t._string = ""; + } + t._type = Token::NUMBER; + if (c == '.') { + t._extendedType = Token::FLOATING_POINT_TYPE; + } else { + t._extendedType = Token::INTEGER_TYPE; + } + + if ((c == '0') && (peekInputChar(1) == 'x')) { + // Hex number + t._string += "0x"; + + // skip the 0x + eatInputChar(); + eatInputChar(); + + c = peekInputChar(); + while (isDigit(c) || ((c >= 'A') && (c <= 'F')) || ((c >= 'a') && (c <= 'f'))) { + t._string += c; + c = eatAndPeekInputChar(); + } + + } else { + // Non-hex number + + // Read the part before the decimal. + while (isDigit(c)) { + t._string += c; + c = eatAndPeekInputChar(); + } + + // True if we are reading a floating-point special type + bool isSpecial = false; + + // Read the decimal, if one exists + if (c == '.') { + t._extendedType = Token::FLOATING_POINT_TYPE; + + // The '.' character was a decimal point, not the start of a + // method or range operator + t._string += c; + c = eatAndPeekInputChar(); + + // Floating point specials (msvc format only) + if (options.msvcSpecials && (c == '#')) { + isSpecial = true; + // We are reading a floating point special value + // of the form -1.#IND00, -1.#INF00, or 1.#INF00 + c = eatAndPeekInputChar(); + char test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if (test != 'I') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + c = eatAndPeekInputChar(); + test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if (test != 'N') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + t._string += "#IN"; + c = eatAndPeekInputChar(); + test = c; + if (! options.caseSensitive) { + test = toupper(c); + } + if ((test != 'F') && (test != 'D')) { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or nan) " + "format.", + t.line(), charNumber); + } + t._string += c; + for (int j = 0; j < 2; ++j) { + c = eatAndPeekInputChar(); + if (c != '0') { + throw BadMSVCSpecial + ( + "Incorrect floating-point special (inf or" + "nan) format.", + t.line(), charNumber); + } + t._string += (char)c; + } + + } else { + + // Read the part after the decimal + while (isDigit((char)c)) { + t._string += (char)c; + c = eatAndPeekInputChar(); + } + } + } + + if (! isSpecial && ((c == 'e') || (c == 'E'))) { + // Read exponent + t._extendedType = Token::FLOATING_POINT_TYPE; + t._string += c; + + c = eatAndPeekInputChar(); + if ((c == '-') || (c == '+')) { + t._string += c; + c = eatAndPeekInputChar(); + } + + while (isDigit(c)) { + t._string += c; + c = eatAndPeekInputChar(); + } + } + + if (! isSpecial && (t._extendedType == Token::FLOATING_POINT_TYPE) && (c == 'f')) { + // Trailing f on a float + t._string += c; + c = eatAndPeekInputChar(); + } + } + return t; + + } else if (isLetter(c) || (c == '_')) { + // Identifier or keyword + // [A-Za-z_][A-Za-z_0-9]* + + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + t._string = ""; + do { + t._string += c; + c = eatAndPeekInputChar(); + } while (isLetter(c) || isDigit(c) || (c == '_')); + + // See if this symbol is actually a boolean + if ((options.trueSymbols.size() > 0) || (options.falseSymbols.size() > 0)) { + std::string str = t._string; + if (! options.caseSensitive) { + str = toUpper(str); + } + if (options.trueSymbols.contains(str)) { + t._type = Token::BOOLEAN; + t._extendedType = Token::BOOLEAN_TYPE; + t._bool = true; + } else if (options.falseSymbols.contains(str)) { + t._type = Token::BOOLEAN; + t._extendedType = Token::BOOLEAN_TYPE; + t._bool = false; + } + } + + return t; + + } else if (c == '\"') { + + // Discard the double-quote. + eatInputChar(); + + // Double quoted string + parseQuotedString('\"', t); + return t; + + } else if (c == options.singleQuoteCharacter) { + + // Discard the single-quote. + eatInputChar(); + + if (options.singleQuotedStrings) { + // Single quoted string + parseQuotedString(options.singleQuoteCharacter, t); + } else { + t._string = c; + t._type = Token::SYMBOL; + t._extendedType = Token::SYMBOL_TYPE; + } + return t; + + } // end of special case tokens + + if (c == EOF) { + t._type = Token::END; + t._extendedType = Token::END_TYPE; + t._string = ""; + return t; + } + + // Some unknown token + debugAssertM(false, + format("Unrecognized token type beginning with character '%c' (ASCII %d)", + c, c)); + return t; +} + + +void TextInput::parseQuotedString(unsigned char delimiter, Token& t) { + + t._type = Token::STRING; + + if (delimiter == options.singleQuoteCharacter) { + t._extendedType = Token::SINGLE_QUOTED_TYPE; + } else { + t._extendedType = Token::DOUBLE_QUOTED_TYPE; + } + + while (true) { + // We're definitely going to consume the next input char, so we get + // it right now. This makes the condition handling below a bit easier. + int c = eatInputChar(); + + if (c == EOF) { + // END inside a quoted string. (We finish the string.) + break; + } + + if (options.escapeSequencesInStrings && (c == '\\')) { + // An escaped character. We're definitely going to consume it, + // so we get it (and consume it) now. + + c = eatInputChar(); + + switch (c) { + case 'r': + t._string += '\r'; + break; + case 'n': + t._string += '\n'; + break; + case 't': + t._string += '\t'; + break; + case '0': + t._string += '\0'; + break; + + case '\\': + case '\"': + t._string += (char)c; + break; + + default: + if (c == options.singleQuoteCharacter) { + t._string += (char)c; + break; + } + + if (((c == options.otherCommentCharacter) && + (options.otherCommentCharacter != '\0')) || + ((c == options.otherCommentCharacter2) && + (options.otherCommentCharacter2 != '\0'))) { + t._string += c; + } + // otherwise, some illegal escape sequence; skip it. + break; + + } // switch + + } else if (c == delimiter) { + // End of the string. Already consumed the character. + break; + } else { + // All other chars, go on to the string. Already consumed the + // character. + t._string += (char)c; + } + + } +} + +bool TextInput::readBoolean() { + Token t(read()); + + if (t._type == Token::BOOLEAN) { + return t.boolean(); + } + + // Push initial token back, and throw an error. We intentionally + // indicate that the wrong type is the type of the initial token. + // Logically, the number started there. + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::BOOLEAN, t._type); +} + +double TextInput::readNumber() { + Token t(read()); + + if (t._type == Token::NUMBER) { + return t.number(); + } + + // Even if signedNumbers is disabled, readNumber attempts to + // read a signed number, so we handle that case here. + if (! options.signedNumbers + && (t._type == Token::SYMBOL) + && ((t._string == "-") + || (t._string == "+"))) { + + Token t2(read()); + + if ((t2._type == Token::NUMBER) + && (t2._character == t._character + 1)) { + + if (t._string == "-") { + return -t2.number(); + } else { + return t2.number(); + } + } + + // push back the second token. + push(t2); + } + + // Push initial token back, and throw an error. We intentionally + // indicate that the wrong type is the type of the initial token. + // Logically, the number started there. + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::NUMBER, t._type); +} + + +Token TextInput::readStringToken() { + Token t(read()); + + if (t._type == Token::STRING) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::STRING, t._type); +} + +std::string TextInput::readString() { + return readStringToken()._string; +} + +void TextInput::readString(const std::string& s) { + Token t(readStringToken()); + + if (t._string == s) { // fast path + return; + } + + push(t); + throw WrongString(options.sourceFileName, t.line(), t.character(), + s, t._string); +} + +Token TextInput::readCommentToken() { + Token t(read()); + + if (t._type == Token::COMMENT) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::COMMENT, t._type); +} + +std::string TextInput::readComment() { + return readCommentToken()._string; +} + +void TextInput::readComment(const std::string& s) { + Token t(readCommentToken()); + + if (t._string == s) { // fast path + return; + } + + push(t); + throw WrongString(options.sourceFileName, t.line(), t.character(), + s, t._string); +} + +Token TextInput::readNewlineToken() { + Token t(read()); + + if (t._type == Token::NEWLINE) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::NEWLINE, t._type); +} + +std::string TextInput::readNewline() { + return readNewlineToken()._string; +} + +void TextInput::readNewline(const std::string& s) { + Token t(readNewlineToken()); + + if (t._string == s) { // fast path + return; + } + + push(t); + throw WrongString(options.sourceFileName, t.line(), t.character(), + s, t._string); +} + +Token TextInput::readSymbolToken() { + Token t(read()); + + if (t._type == Token::SYMBOL) { // fast path + return t; + } + + push(t); + throw WrongTokenType(options.sourceFileName, t.line(), t.character(), + Token::SYMBOL, t._type); +} + + +std::string TextInput::readSymbol() { + return readSymbolToken()._string; +} + +void TextInput::readSymbol(const std::string& symbol) { + Token t(readSymbolToken()); + + if (t._string == symbol) { // fast path + return; + } + + push(t); + throw WrongSymbol(options.sourceFileName, t.line(), t.character(), + symbol, t._string); +} + + +TextInput::TextInput(const std::string& filename, const Settings& opt) : options(opt) { + init(); + std::string input = readWholeFile(filename); + + if (options.sourceFileName.empty()) { + options.sourceFileName = filename; + } + int n = input.size(); + buffer.resize(n); + System::memcpy(buffer.getCArray(), input.c_str(), n); +} + + +TextInput::TextInput(FS fs, const std::string& str, const Settings& opt) : options(opt) { + (void)fs; + init(); + if (options.sourceFileName.empty()) { + if (str.length() < 14) { + options.sourceFileName = std::string("\"") + str + "\""; + } else { + options.sourceFileName = std::string("\"") + str.substr(0, 10) + "...\""; + } + } + buffer.resize(str.length()); // we don't bother copying trailing NUL. + System::memcpy(buffer.getCArray(), str.c_str(), buffer.size()); +} + + +const std::string& TextInput::filename() const { + return options.sourceFileName; +} + +/////////////////////////////////////////////////////////////////////////////////// + +TextInput::TokenException::TokenException( + const std::string& src, + int ln, + int ch) : ParseError(src, ln, ch, format("%s(%d) : ", src.c_str(), ln)), + sourceFile(src) { +} + +/////////////////////////////////////////////////////////////////////////////////// + +static const char* tokenTypeToString(Token::Type t) { + switch (t) { + case Token::SYMBOL: + return "Token::SYMBOL"; + case Token::STRING: + return "Token::STRING"; + case Token::NUMBER: + return "Token::NUMBER"; + case Token::END: + return "Token::END"; + default: + debugAssertM(false, "Fell through switch"); + return "?"; + } +} + +TextInput::WrongTokenType::WrongTokenType( + const std::string& src, + int ln, + int ch, + Token::Type e, + Token::Type a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected token of type %s, found type %s.", + tokenTypeToString(e), tokenTypeToString(a)); +} + + +TextInput::BadMSVCSpecial::BadMSVCSpecial( + const std::string& src, + int ln, + int ch) : + TokenException(src, ln, ch) { +} + + +TextInput::WrongSymbol::WrongSymbol( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected symbol '%s', found symbol '%s'.", + e.c_str(), a.c_str()); +} + + +TextInput::WrongString::WrongString( + const std::string& src, + int ln, + int ch, + const std::string& e, + const std::string& a) : + TokenException(src, ln, ch), expected(e), actual(a) { + + message += format("Expected string '%s', found string '%s'.", + e.c_str(), a.c_str()); +} + + +void deserialize(bool& b, TextInput& ti) { + b = ti.readSymbol() == "true"; +} + + +void deserialize(int& b, TextInput& ti) { + b = iRound(ti.readNumber()); +} + + +void deserialize(uint8& b, TextInput& ti) { + b = (uint8)iRound(ti.readNumber()); +} + + +void deserialize(double& b, TextInput& ti) { + b = ti.readNumber(); +} + + +void deserialize(float& b, TextInput& ti) { + b = (float)ti.readNumber(); +} + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/TextOutput.cpp b/externals/g3dlite/TextOutput.cpp new file mode 100644 index 00000000000..11347252eba --- /dev/null +++ b/externals/g3dlite/TextOutput.cpp @@ -0,0 +1,452 @@ +/** + @file TextOutput.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + @created 2004-06-21 + @edited 2006-08-14 + + Copyright 2000-2006, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/TextOutput.h" +#include "G3D/Log.h" +#include "G3D/fileutils.h" + +namespace G3D { + +TextOutput::TextOutput(const TextOutput::Settings& opt) : + startingNewLine(true), + currentColumn(0), + inDQuote(false), + filename(""), + indentLevel(0) +{ + setOptions(opt); +} + + +TextOutput::TextOutput(const std::string& fil, const TextOutput::Settings& opt) : + startingNewLine(true), + currentColumn(0), + inDQuote(false), + filename(fil), + indentLevel(0) +{ + + setOptions(opt); +} + + +void TextOutput::setIndentLevel(int i) { + indentLevel = i; + + // If there were more pops than pushes, don't let that take us below 0 indent. + // Don't ever indent more than the number of columns. + indentSpaces = + iClamp(option.spacesPerIndent * indentLevel, + 0, + option.numColumns - 1); +} + + +void TextOutput::setOptions(const Settings& _opt) { + option = _opt; + + debugAssert(option.numColumns > 1); + + setIndentLevel(indentLevel); + + newline = (option.newlineStyle == Settings::NEWLINE_WINDOWS) ? "\r\n" : "\n"; +} + + +void TextOutput::pushIndent() { + setIndentLevel(indentLevel + 1); +} + + +void TextOutput::popIndent() { + setIndentLevel(indentLevel - 1); +} + + +static std::string escape(const std::string& string) { + std::string result = ""; + + for (std::string::size_type i = 0; i < string.length(); ++i) { + char c = string.at(i); + switch (c) { + case '\0': + result += "\\0"; + break; + + case '\r': + result += "\\r"; + break; + + case '\n': + result += "\\n"; + break; + + case '\t': + result += "\\t"; + break; + + case '\\': + result += "\\\\"; + break; + + default: + result += c; + } + } + + return result; +} + +void TextOutput::writeString(const std::string& string) { + // Convert special characters to escape sequences + this->printf("\"%s\"", escape(string).c_str()); +} + + +void TextOutput::writeBoolean(bool b) { + this->printf("%s ", b ? option.trueSymbol.c_str() : option.falseSymbol.c_str()); +} + +void TextOutput::writeNumber(double n) { + this->printf("%f ", n); +} + + +void TextOutput::writeNumber(int n) { + this->printf("%d ", n); +} + + +void TextOutput::writeSymbol(const std::string& string) { + if (string.size() > 0) { + // TODO: check for legal symbols? + this->printf("%s ", string.c_str()); + } +} + +void TextOutput::writeSymbols( + const std::string& a, + const std::string& b, + const std::string& c, + const std::string& d, + const std::string& e, + const std::string& f) { + + writeSymbol(a); + writeSymbol(b); + writeSymbol(c); + writeSymbol(d); + writeSymbol(e); + writeSymbol(f); +} + + +void TextOutput::printf(const std::string formatString, ...) { + va_list argList; + va_start(argList, formatString); + this->vprintf(formatString.c_str(), argList); + va_end(argList); +} + + +void TextOutput::printf(const char* formatString, ...) { + va_list argList; + va_start(argList, formatString); + this->vprintf(formatString, argList); + va_end(argList); +} + + +void TextOutput::convertNewlines(const std::string& in, std::string& out) { + // TODO: can be significantly optimized in cases where + // single characters are copied in order by walking through + // the array and copying substrings as needed. + + if (option.convertNewlines) { + out = ""; + for (uint32 i = 0; i < in.size(); ++i) { + if (in[i] == '\n') { + // Unix newline + out += newline; + } else if ((in[i] == '\r') && (i + 1 < in.size()) && (in[i + 1] == '\n')) { + // Windows newline + out += newline; + ++i; + } else { + out += in[i]; + } + } + } else { + out = in; + } +} + + +void TextOutput::writeNewline() { + for (uint32 i = 0; i < newline.size(); ++i) { + indentAppend(newline[i]); + } +} + + +void TextOutput::writeNewlines(int numLines) { + for (int i = 0; i < numLines; ++i) { + writeNewline(); + } +} + + +void TextOutput::wordWrapIndentAppend(const std::string& str) { + // TODO: keep track of the last space character we saw so we don't + // have to always search. + + if ((option.wordWrap == Settings::WRAP_NONE) || + (currentColumn + (int)str.size() <= option.numColumns)) { + // No word-wrapping is needed + + // Add one character at a time. + // TODO: optimize for strings without newlines to add multiple + // characters. + for (uint32 i = 0; i < str.size(); ++i) { + indentAppend(str[i]); + } + return; + } + + // Number of columns to wrap against + int cols = option.numColumns - indentSpaces; + + // Copy forward until we exceed the column size, + // and then back up and try to insert newlines as needed. + for (uint32 i = 0; i < str.size(); ++i) { + + indentAppend(str[i]); + if ((str[i] == '\r') && (i + 1 < str.size()) && (str[i + 1] == '\n')) { + // \r\n, we need to hit the \n to enter word wrapping. + ++i; + indentAppend(str[i]); + } + + if (currentColumn >= cols) { + debugAssertM(str[i] != '\n' && str[i] != '\r', + "Should never enter word-wrapping on a newline character"); + + // True when we're allowed to treat a space as a space. + bool unquotedSpace = option.allowWordWrapInsideDoubleQuotes || ! inDQuote; + + // Cases: + // + // 1. Currently in a series of spaces that ends with a newline + // strip all spaces and let the newline + // flow through. + // + // 2. Currently in a series of spaces that does not end with a newline + // strip all spaces and replace them with single newline + // + // 3. Not in a series of spaces + // search backwards for a space, then execute case 2. + + // Index of most recent space + uint32 lastSpace = data.size() - 1; + + // How far back we had to look for a space + uint32 k = 0; + uint32 maxLookBackward = currentColumn - indentSpaces; + + // Search backwards (from current character), looking for a space. + while ((k < maxLookBackward) && + (lastSpace > 0) && + (! ((data[lastSpace] == ' ') && unquotedSpace))) { + --lastSpace; + ++k; + + if ((data[lastSpace] == '\"') && !option.allowWordWrapInsideDoubleQuotes) { + unquotedSpace = ! unquotedSpace; + } + } + + if (k == maxLookBackward) { + // We couldn't find a series of spaces + + if (option.wordWrap == Settings::WRAP_ALWAYS) { + // Strip the last character we wrote, force a newline, + // and replace the last character; + data.pop(); + writeNewline(); + indentAppend(str[i]); + } else { + // Must be Settings::WRAP_WITHOUT_BREAKING + // + // Don't write the newline; we'll come back to + // the word wrap code after writing another character + } + } else { + // We found a series of spaces. If they continue + // to the new string, strip spaces off both. Otherwise + // strip spaces from data only and insert a newline. + + // Find the start of the spaces. firstSpace is the index of the + // first non-space, looking backwards from lastSpace. + uint32 firstSpace = lastSpace; + while ((k < maxLookBackward) && + (firstSpace > 0) && + (data[firstSpace] == ' ')) { + --firstSpace; + ++k; + } + + if (k == maxLookBackward) { + ++firstSpace; + } + + if (lastSpace == (uint32)data.size() - 1) { + // Spaces continued up to the new string + data.resize(firstSpace + 1); + writeNewline(); + + // Delete the spaces from the new string + while ((i < str.size() - 1) && (str[i + 1] == ' ')) { + ++i; + } + } else { + // Spaces were somewhere in the middle of the old string. + // replace them with a newline. + + // Copy over the characters that should be saved + Array<char> temp; + for (uint32 j = lastSpace + 1; j < (uint32)data.size(); ++j) { + char c = data[j]; + + if (c == '\"') { + // Undo changes to quoting (they will be re-done + // when we paste these characters back on). + inDQuote = !inDQuote; + } + temp.append(c); + } + + // Remove those characters and replace with a newline. + data.resize(firstSpace + 1); + writeNewline(); + + // Write them back + for (uint32 j = 0; j < (uint32)temp.size(); ++j) { + indentAppend(temp[j]); + } + + // We are now free to continue adding from the + // new string, which may or may not begin with spaces. + + } // if spaces included new string + } // if hit indent + } // if line exceeded + } // iterate over str +} + + +void TextOutput::indentAppend(char c) { + + if (startingNewLine) { + for (int j = 0; j < indentSpaces; ++j) { + data.push(' '); + } + startingNewLine = false; + currentColumn = indentSpaces; + } + + data.push(c); + + // Don't increment the column count on return character + // newline is taken care of below. + if (c != '\r') { + ++currentColumn; + } + + if (c == '\"') { + inDQuote = ! inDQuote; + } + + startingNewLine = (c == '\n'); + if (startingNewLine) { + currentColumn = 0; + } +} + + +void TextOutput::vprintf(const char* formatString, va_list argPtr) { + std::string str = vformat(formatString, argPtr); + + std::string clean; + convertNewlines(str, clean); + wordWrapIndentAppend(clean); +} + + +void TextOutput::commit(bool flush) { + std::string p = filenamePath(filename); + if (! fileExists(p, false)) { + createDirectory(p); + } + + FILE* f = fopen(filename.c_str(), "wb"); + debugAssertM(f, "Could not open \"" + filename + "\""); + fwrite(data.getCArray(), 1, data.size(), f); + if (flush) { + fflush(f); + } + fclose(f); +} + + +void TextOutput::commitString(std::string& out) { + // Null terminate + data.push('\0'); + out = data.getCArray(); + data.pop(); +} + + +std::string TextOutput::commitString() { + std::string str; + commitString(str); + return str; +} + + + +///////////////////////////////////////////////////////////////////// + +void serialize(const float& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const bool& b, TextOutput& to) { + to.writeSymbol(b ? "true" : "false"); +} + + +void serialize(const int& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const uint8& b, TextOutput& to) { + to.writeNumber(b); +} + + +void serialize(const double& b, TextOutput& to) { + to.writeNumber(b); +} + + +} diff --git a/externals/g3dlite/Triangle.cpp b/externals/g3dlite/Triangle.cpp new file mode 100644 index 00000000000..253438ad5fb --- /dev/null +++ b/externals/g3dlite/Triangle.cpp @@ -0,0 +1,186 @@ +/** + @file Triangle.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2001-04-06 + @edited 2008-12-28 + + Copyright 2000-2009, Morgan McGuire. + All rights reserved. + */ + +#include "G3D/platform.h" +#include "G3D/Triangle.h" +#include "G3D/Plane.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/debugAssert.h" +#include "G3D/AABox.h" +#include "G3D/Ray.h" + +namespace G3D { + + +void Triangle::init(const Vector3& v0, const Vector3& v1, const Vector3& v2) { + + _plane = Plane(v0, v1, v2); + _vertex[0] = v0; + _vertex[1] = v1; + _vertex[2] = v2; + + static int next[] = {1,2,0}; + + for (int i = 0; i < 3; ++i) { + const Vector3& e = _vertex[next[i]] - _vertex[i]; + edgeMagnitude[i] = e.magnitude(); + + if (edgeMagnitude[i] == 0) { + edgeDirection[i] = Vector3::zero(); + } else { + edgeDirection[i] = e / (float)edgeMagnitude[i]; + } + } + + _edge01 = _vertex[1] - _vertex[0]; + _edge02 = _vertex[2] - _vertex[0]; + + _primaryAxis = _plane.normal().primaryAxis(); + _area = 0.5f * edgeDirection[0].cross(edgeDirection[2]).magnitude() * (edgeMagnitude[0] * edgeMagnitude[2]); + //0.5f * (_vertex[1] - _vertex[0]).cross(_vertex[2] - _vertex[0]).dot(_plane.normal()); +} + + +Triangle::Triangle() { + init(Vector3::zero(), Vector3::zero(), Vector3::zero()); +} + + +Triangle::Triangle(const Vector3& v0, const Vector3& v1, const Vector3& v2) { + init(v0, v1, v2); +} + + +Triangle::~Triangle() { +} + + +Triangle::Triangle(class BinaryInput& b) { + deserialize(b); +} + + +void Triangle::serialize(class BinaryOutput& b) { + _vertex[0].serialize(b); + _vertex[1].serialize(b); + _vertex[2].serialize(b); +} + + +void Triangle::deserialize(class BinaryInput& b) { + _vertex[0].deserialize(b); + _vertex[1].deserialize(b); + _vertex[2].deserialize(b); + init(_vertex[0], _vertex[1], _vertex[2]); +} + + +float Triangle::area() const { + return _area; +} + + +const Vector3& Triangle::normal() const { + return _plane.normal(); +} + + +const Plane& Triangle::plane() const { + return _plane; +} + + +Vector3 Triangle::center() const { + return (_vertex[0] + _vertex[1] + _vertex[2]) / 3.0; +} + +Vector3 Triangle::randomPoint() const { + // Choose a random point in the parallelogram + + float s = uniformRandom(); + float t = uniformRandom(); + + if (t > 1.0f - s) { + // Outside the triangle; reflect about the + // diagonal of the parallelogram + t = 1.0f - t; + s = 1.0f - s; + } + + return _edge01 * s + _edge02 * t + _vertex[0]; +} + + +void Triangle::getBounds(AABox& out) const { + Vector3 lo = _vertex[0]; + Vector3 hi = lo; + + for (int i = 1; i < 3; ++i) { + lo = lo.min(_vertex[i]); + hi = hi.max(_vertex[i]); + } + + out = AABox(lo, hi); +} + + +bool Triangle::intersect(const Ray& ray, float& distance, float baryCoord[3]) const { + static const float EPS = 1e-5f; + + // See RTR2 ch. 13.7 for the algorithm. + + const Vector3& e1 = edge01(); + const Vector3& e2 = edge02(); + const Vector3 p(ray.direction().cross(e2)); + const float a = e1.dot(p); + + if (abs(a) < EPS) { + // Determinant is ill-conditioned; abort early + return false; + } + + const float f = 1.0f / a; + const Vector3 s(ray.origin() - vertex(0)); + const float u = f * s.dot(p); + + if ((u < 0.0f) || (u > 1.0f)) { + // We hit the plane of the m_geometry, but outside the m_geometry + return false; + } + + const Vector3 q(s.cross(e1)); + const float v = f * ray.direction().dot(q); + + if ((v < 0.0f) || ((u + v) > 1.0f)) { + // We hit the plane of the triangle, but outside the triangle + return false; + } + + const float t = f * e2.dot(q); + + if ((t > 0.0f) && (t < distance)) { + // This is a new hit, closer than the previous one + distance = t; + + baryCoord[0] = 1.0 - u - v; + baryCoord[1] = u; + baryCoord[2] = v; + + return true; + } else { + // This hit is after the previous hit, so ignore it + return false; + } +} + +} // G3D diff --git a/externals/g3dlite/UprightFrame.cpp b/externals/g3dlite/UprightFrame.cpp new file mode 100644 index 00000000000..c80264bf4e8 --- /dev/null +++ b/externals/g3dlite/UprightFrame.cpp @@ -0,0 +1,132 @@ +/** + @file UprightFrame.cpp + Box class + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2007-05-02 + @edited 2007-05-05 +*/ + +#include "G3D/UprightFrame.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" + +namespace G3D { + +UprightFrame::UprightFrame(const CoordinateFrame& cframe) { + Vector3 look = cframe.lookVector(); + + yaw = G3D::pi() + atan2(look.x, look.z); + pitch = asin(look.y); + + translation = cframe.translation; +} + + +CoordinateFrame UprightFrame::toCoordinateFrame() const { + CoordinateFrame cframe; + + Matrix3 P(Matrix3::fromAxisAngle(Vector3::unitX(), pitch)); + Matrix3 Y(Matrix3::fromAxisAngle(Vector3::unitY(), yaw)); + + cframe.rotation = Y * P; + cframe.translation = translation; + + return cframe; +} + + +UprightFrame UprightFrame::operator+(const UprightFrame& other) const { + return UprightFrame(translation + other.translation, pitch + other.pitch, yaw + other.yaw); +} + + +UprightFrame UprightFrame::operator*(const float k) const { + return UprightFrame(translation * k, pitch * k, yaw * k); +} + + +void UprightFrame::unwrapYaw(UprightFrame* a, int N) { + // Use the first point to establish the wrapping convention + for (int i = 1; i < N; ++i) { + const float prev = a[i - 1].yaw; + float& cur = a[i].yaw; + + // No two angles should be more than pi (i.e., 180-degrees) apart. + if (abs(cur - prev) > G3D::pi()) { + // These angles must have wrapped at zero, causing them + // to be interpolated the long way. + + // Find canonical [0, 2pi] versions of these numbers + float p = wrap(prev, twoPi()); + float c = wrap(cur, twoPi()); + + // Find the difference -pi < diff < pi between the current and previous values + float diff = c - p; + if (diff < -G3D::pi()) { + diff += twoPi(); + } else if (diff > G3D::pi()) { + diff -= twoPi(); + } + + // Offset the current from the previous by the difference + // between them. + cur = prev + diff; + } + } +} + + +void UprightFrame::serialize(class BinaryOutput& b) const { + translation.serialize(b); + b.writeFloat32(pitch); + b.writeFloat32(yaw); +} + + +void UprightFrame::deserialize(class BinaryInput& b) { + translation.deserialize(b); + pitch = b.readFloat32(); + yaw = b.readFloat32(); +} + + +void UprightSpline::serialize(class BinaryOutput& b) const { + b.writeBool8(cyclic); + + b.writeInt32(control.size()); + for (int i = 0; i < control.size(); ++i) { + control[i].serialize(b); + } + b.writeInt32(time.size()); + for (int i = 0; i < time.size(); ++i) { + b.writeFloat32(time[i]); + } +} + + +void UprightSpline::deserialize(class BinaryInput& b) { + cyclic = b.readBool8(); + + control.resize(b.readInt32()); + for (int i = 0; i < control.size(); ++i) { + control[i].deserialize(b); + } + + if (b.hasMore()) { + time.resize(b.readInt32()); + for (int i = 0; i < time.size(); ++i) { + time[i] = b.readFloat32(); + } + debugAssert(time.size() == control.size()); + } else { + // Import legacy path + time.resize(control.size()); + for (int i = 0; i < time.size(); ++i) { + time[i] = i; + } + } +} + +} diff --git a/externals/g3dlite/Vector2.cpp b/externals/g3dlite/Vector2.cpp new file mode 100644 index 00000000000..ec0737c3755 --- /dev/null +++ b/externals/g3dlite/Vector2.cpp @@ -0,0 +1,224 @@ +/** + @file Vector2.cpp + + 2D vector class, used for texture coordinates primarily. + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @cite Portions based on Dave Eberly'x Magic Software Library + at http://www.magic-software.com + + @created 2001-06-02 + @edited 2009-11-16 + */ + +#include "G3D/platform.h" +#include <stdlib.h> +#include "G3D/Vector2.h" +#include "G3D/g3dmath.h" +#include "G3D/format.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/Any.h" + +namespace G3D { + + +Vector2::Vector2(const Any& any) { + any.verifyName("Vector2"); + any.verifyType(Any::TABLE, Any::ARRAY); + any.verifySize(2); + + if (any.type() == Any::ARRAY) { + x = any[0]; + y = any[1]; + } else { + // Table + x = any["x"]; + y = any["y"]; + } +} + + +Vector2::operator Any() const { + Any any(Any::ARRAY, "Vector2"); + any.append(x, y); + return any; +} + + +const Vector2& Vector2::one() { + static const Vector2 v(1, 1); return v; +} + + +const Vector2& Vector2::zero() { + static Vector2 v(0, 0); + return v; +} + +const Vector2& Vector2::unitX() { + static Vector2 v(1, 0); + return v; +} + +const Vector2& Vector2::unitY() { + static Vector2 v(0, 1); + return v; +} + +const Vector2& Vector2::inf() { + static Vector2 v((float)G3D::finf(), (float)G3D::finf()); + return v; +} + + +const Vector2& Vector2::nan() { + static Vector2 v((float)G3D::fnan(), (float)G3D::fnan()); + return v; +} + + +const Vector2& Vector2::minFinite() { + static Vector2 v(-FLT_MAX, -FLT_MAX); + return v; +} + + +const Vector2& Vector2::maxFinite() { + static Vector2 v(FLT_MAX, FLT_MAX); + return v; +} + + +size_t Vector2::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + + return xhash + (yhash * 37); +} + + +Vector2::Vector2(BinaryInput& b) { + deserialize(b); +} + + +void Vector2::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); +} + + +void Vector2::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); +} + + +void Vector2::deserialize(TextInput& t) { + t.readSymbol("("); + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(")"); +} + + +void Vector2::serialize(TextOutput& t) const { + t.writeSymbol("("); + t.writeNumber(x); + t.writeSymbol(","); + t.writeNumber(y); + t.writeSymbol(")"); +} + +//---------------------------------------------------------------------------- + +Vector2 Vector2::random(G3D::Random& r) { + Vector2 result; + + do { + result = Vector2(r.uniform(-1, 1), r.uniform(-1, 1)); + + } while (result.squaredLength() >= 1.0f); + + result.unitize(); + + return result; +} + + +Vector2 Vector2::operator/ (float k) const { + return *this * (1.0f / k); +} + +Vector2& Vector2::operator/= (float k) { + this->x /= k; + this->y /= k; + return *this; +} + +//---------------------------------------------------------------------------- +float Vector2::unitize (float fTolerance) { + float fLength = length(); + + if (fLength > fTolerance) { + float fInvLength = 1.0f / fLength; + x *= fInvLength; + y *= fInvLength; + } else { + fLength = 0.0; + } + + return fLength; +} + +//---------------------------------------------------------------------------- + +std::string Vector2::toString() const { + return G3D::format("(%g, %g)", x, y); +} + +// 2-char swizzles + +Vector2 Vector2::xx() const { return Vector2 (x, x); } +Vector2 Vector2::yx() const { return Vector2 (y, x); } +Vector2 Vector2::xy() const { return Vector2 (x, y); } +Vector2 Vector2::yy() const { return Vector2 (y, y); } + +// 3-char swizzles + +Vector3 Vector2::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector2::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector2::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector2::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector2::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector2::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector2::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector2::yyy() const { return Vector3 (y, y, y); } + +// 4-char swizzles + +Vector4 Vector2::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector2::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector2::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector2::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector2::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector2::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector2::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector2::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector2::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector2::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector2::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector2::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector2::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector2::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector2::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector2::yyyy() const { return Vector4 (y, y, y, y); } + + + +} // namespace diff --git a/externals/g3dlite/Vector3.cpp b/externals/g3dlite/Vector3.cpp new file mode 100644 index 00000000000..a53fa8269b7 --- /dev/null +++ b/externals/g3dlite/Vector3.cpp @@ -0,0 +1,507 @@ +/** + @file Vector3.cpp + + 3D vector class + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @cite Portions based on Dave Eberly's Magic Software Library at http://www.magic-software.com + + @created 2001-06-02 + @edited 2009-11-27 + */ + +#include <limits> +#include <stdlib.h> +#include "G3D/Vector3.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/TextInput.h" +#include "G3D/TextOutput.h" +#include "G3D/Vector3int16.h" +#include "G3D/Matrix3.h" +#include "G3D/Vector2.h" +#include "G3D/Color3.h" +#include "G3D/Vector4int8.h" +#include "G3D/Vector3int32.h" +#include "G3D/Any.h" + +namespace G3D { + +Vector3::Vector3(const Any& any) { + any.verifyName("Vector3"); + any.verifyType(Any::TABLE, Any::ARRAY); + any.verifySize(3); + + if (any.type() == Any::ARRAY) { + x = any[0]; + y = any[1]; + z = any[2]; + } else { + // Table + x = any["x"]; + y = any["y"]; + z = any["z"]; + } +} + +Vector3::operator Any() const { + Any any(Any::ARRAY, "Vector3"); + any.append(x, y, z); + return any; +} + +Vector3::Vector3(const class Color3& v) : x(v.r), y(v.g), z(v.b) {} + +Vector3::Vector3(const class Vector3int32& v) : x((float)v.x), y((float)v.y), z((float)v.z) {} + +Vector3::Vector3(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f) {} + +Vector3::Vector3(const class Vector2& v, float _z) : x(v.x), y(v.y), z(_z) { +} + +Vector3& Vector3::ignore() { + static Vector3 v; + return v; +} + +const Vector3& Vector3::zero() { static const Vector3 v(0, 0, 0); return v; } +const Vector3& Vector3::one() { static const Vector3 v(1, 1, 1); return v; } +const Vector3& Vector3::unitX() { static const Vector3 v(1, 0, 0); return v; } +const Vector3& Vector3::unitY() { static const Vector3 v(0, 1, 0); return v; } +const Vector3& Vector3::unitZ() { static const Vector3 v(0, 0, 1); return v; } +const Vector3& Vector3::inf() { static const Vector3 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf()); return v; } +const Vector3& Vector3::nan() { static const Vector3 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan()); return v; } +const Vector3& Vector3::minFinite(){ static const Vector3 v(-FLT_MAX, -FLT_MAX, -FLT_MAX); return v; } +const Vector3& Vector3::maxFinite(){ static const Vector3 v(FLT_MAX, FLT_MAX, FLT_MAX); return v; } + +Vector3::Axis Vector3::primaryAxis() const { + + Axis a = X_AXIS; + + double nx = abs(x); + double ny = abs(y); + double nz = abs(z); + + if (nx > ny) { + if (nx > nz) { + a = X_AXIS; + } else { + a = Z_AXIS; + } + } else { + if (ny > nz) { + a = Y_AXIS; + } else { + a = Z_AXIS; + } + } + + return a; +} + + +size_t Vector3::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + unsigned int zhash = (*(int*)(void*)(&z)); + + return xhash + (yhash * 37) + (zhash * 101); +} + +std::ostream& operator<<(std::ostream& os, const Vector3& v) { + return os << v.toString(); +} + + +//---------------------------------------------------------------------------- + +double frand() { + return rand() / (double) RAND_MAX; +} + +Vector3::Vector3(TextInput& t) { + deserialize(t); +} + +Vector3::Vector3(BinaryInput& b) { + deserialize(b); +} + + +Vector3::Vector3(const class Vector3int16& v) { + x = v.x; + y = v.y; + z = v.z; +} + + +void Vector3::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); +} + + +void Vector3::deserialize(TextInput& t) { + t.readSymbol("("); + x = (float)t.readNumber(); + t.readSymbol(","); + y = (float)t.readNumber(); + t.readSymbol(","); + z = (float)t.readNumber(); + t.readSymbol(")"); +} + + +void Vector3::serialize(TextOutput& t) const { + t.writeSymbol("("); + t.writeNumber(x); + t.writeSymbol(","); + t.writeNumber(y); + t.writeSymbol(","); + t.writeNumber(z); + t.writeSymbol(")"); +} + + +void Vector3::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); +} + + +Vector3 Vector3::random(Random& r) { + Vector3 result; + r.sphere(result.x, result.y, result.z); + return result; +} + + +float Vector3::unitize(float fTolerance) { + float fMagnitude = magnitude(); + + if (fMagnitude > fTolerance) { + float fInvMagnitude = 1.0f / fMagnitude; + x *= fInvMagnitude; + y *= fInvMagnitude; + z *= fInvMagnitude; + } else { + fMagnitude = 0.0f; + } + + return fMagnitude; +} + + +Vector3 Vector3::reflectAbout(const Vector3& normal) const { + Vector3 out; + + Vector3 N = normal.direction(); + + // 2 * normal.dot(this) * normal - this + return N * 2 * this->dot(N) - *this; +} + + +Vector3 Vector3::cosHemiRandom(const Vector3& normal, Random& r) { + debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f), + "cosHemiRandom requires its argument to have unit length"); + + float x, y, z; + r.cosHemi(x, y, z); + + // Make a coordinate system + const Vector3& Z = normal; + + Vector3 X, Y; + normal.getTangents(X, Y); + + return + x * X + + y * Y + + z * Z; +} + + +Vector3 Vector3::cosPowHemiRandom(const Vector3& normal, const float k, Random& r) { + debugAssertM(G3D::fuzzyEq(normal.length(), 1.0f), + "cosPowHemiRandom requires its argument to have unit length"); + + float x, y, z; + r.cosPowHemi(k, x, y, z); + + // Make a coordinate system + const Vector3& Z = normal; + + Vector3 X, Y; + normal.getTangents(X, Y); + + return + x * X + + y * Y + + z * Z; +} + + +Vector3 Vector3::hemiRandom(const Vector3& normal, Random& r) { + const Vector3& V = Vector3::random(r); + + if (V.dot(normal) < 0) { + return -V; + } else { + return V; + } +} + +//---------------------------------------------------------------------------- + +Vector3 Vector3::reflectionDirection(const Vector3& normal) const { + return -reflectAbout(normal).direction(); +} + +//---------------------------------------------------------------------------- + +Vector3 Vector3::refractionDirection( + const Vector3& normal, + float iInside, + float iOutside) const { + + // From pg. 24 of Henrik Wann Jensen. Realistic Image Synthesis + // Using Photon Mapping. AK Peters. ISBN: 1568811470. July 2001. + + // Invert the directions from Wann Jensen's formulation + // and normalize the vectors. + const Vector3 W = -direction(); + Vector3 N = normal.direction(); + + float h1 = iOutside; + float h2 = iInside; + + if (normal.dot(*this) > 0.0f) { + h1 = iInside; + h2 = iOutside; + N = -N; + } + + const float hRatio = h1 / h2; + const float WdotN = W.dot(N); + + float det = 1.0f - (float)square(hRatio) * (1.0f - (float)square(WdotN)); + + if (det < 0) { + // Total internal reflection + return Vector3::zero(); + } else { + return -hRatio * (W - WdotN * N) - N * sqrt(det); + } +} + +//---------------------------------------------------------------------------- +void Vector3::orthonormalize (Vector3 akVector[3]) { + // If the input vectors are v0, v1, and v2, then the Gram-Schmidt + // orthonormalization produces vectors u0, u1, and u2 as follows, + // + // u0 = v0/|v0| + // u1 = (v1-(u0*v1)u0)/|v1-(u0*v1)u0| + // u2 = (v2-(u0*v2)u0-(u1*v2)u1)/|v2-(u0*v2)u0-(u1*v2)u1| + // + // where |A| indicates length of vector A and A*B indicates dot + // product of vectors A and B. + + // compute u0 + akVector[0].unitize(); + + // compute u1 + float fDot0 = akVector[0].dot(akVector[1]); + akVector[1] -= akVector[0] * fDot0; + akVector[1].unitize(); + + // compute u2 + float fDot1 = akVector[1].dot(akVector[2]); + fDot0 = akVector[0].dot(akVector[2]); + akVector[2] -= akVector[0] * fDot0 + akVector[1] * fDot1; + akVector[2].unitize(); +} + +//---------------------------------------------------------------------------- +void Vector3::generateOrthonormalBasis (Vector3& rkU, Vector3& rkV, + Vector3& rkW, bool bUnitLengthW) { + if ( !bUnitLengthW ) + rkW.unitize(); + + if ( G3D::abs(rkW.x) >= G3D::abs(rkW.y) + && G3D::abs(rkW.x) >= G3D::abs(rkW.z) ) { + rkU.x = -rkW.y; + rkU.y = + rkW.x; + rkU.z = 0.0; + } else { + rkU.x = 0.0; + rkU.y = + rkW.z; + rkU.z = -rkW.y; + } + + rkU.unitize(); + rkV = rkW.cross(rkU); +} + +//---------------------------------------------------------------------------- + +std::string Vector3::toString() const { + return G3D::format("(%g, %g, %g)", x, y, z); +} + + +//---------------------------------------------------------------------------- + +Matrix3 Vector3::cross() const { + return Matrix3( 0, -z, y, + z, 0, -x, + -y, x, 0); +} + + +void serialize(const Vector3::Axis& a, class BinaryOutput& bo) { + bo.writeUInt8((uint8)a); +} + +void deserialize(Vector3::Axis& a, class BinaryInput& bi) { + a = (Vector3::Axis)bi.readUInt8(); +} + +//---------------------------------------------------------------------------- +// 2-char swizzles + +Vector2 Vector3::xx() const { return Vector2 (x, x); } +Vector2 Vector3::yx() const { return Vector2 (y, x); } +Vector2 Vector3::zx() const { return Vector2 (z, x); } +Vector2 Vector3::xy() const { return Vector2 (x, y); } +Vector2 Vector3::yy() const { return Vector2 (y, y); } +Vector2 Vector3::zy() const { return Vector2 (z, y); } +Vector2 Vector3::xz() const { return Vector2 (x, z); } +Vector2 Vector3::yz() const { return Vector2 (y, z); } +Vector2 Vector3::zz() const { return Vector2 (z, z); } + +// 3-char swizzles + +Vector3 Vector3::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector3::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector3::zxx() const { return Vector3 (z, x, x); } +Vector3 Vector3::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector3::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector3::zyx() const { return Vector3 (z, y, x); } +Vector3 Vector3::xzx() const { return Vector3 (x, z, x); } +Vector3 Vector3::yzx() const { return Vector3 (y, z, x); } +Vector3 Vector3::zzx() const { return Vector3 (z, z, x); } +Vector3 Vector3::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector3::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector3::zxy() const { return Vector3 (z, x, y); } +Vector3 Vector3::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector3::yyy() const { return Vector3 (y, y, y); } +Vector3 Vector3::zyy() const { return Vector3 (z, y, y); } +Vector3 Vector3::xzy() const { return Vector3 (x, z, y); } +Vector3 Vector3::yzy() const { return Vector3 (y, z, y); } +Vector3 Vector3::zzy() const { return Vector3 (z, z, y); } +Vector3 Vector3::xxz() const { return Vector3 (x, x, z); } +Vector3 Vector3::yxz() const { return Vector3 (y, x, z); } +Vector3 Vector3::zxz() const { return Vector3 (z, x, z); } +Vector3 Vector3::xyz() const { return Vector3 (x, y, z); } +Vector3 Vector3::yyz() const { return Vector3 (y, y, z); } +Vector3 Vector3::zyz() const { return Vector3 (z, y, z); } +Vector3 Vector3::xzz() const { return Vector3 (x, z, z); } +Vector3 Vector3::yzz() const { return Vector3 (y, z, z); } +Vector3 Vector3::zzz() const { return Vector3 (z, z, z); } + +// 4-char swizzles + +Vector4 Vector3::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector3::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector3::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Vector3::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector3::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector3::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Vector3::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Vector3::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Vector3::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Vector3::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector3::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector3::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Vector3::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector3::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector3::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Vector3::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Vector3::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Vector3::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Vector3::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Vector3::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Vector3::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Vector3::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Vector3::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Vector3::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Vector3::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Vector3::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Vector3::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Vector3::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector3::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector3::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Vector3::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector3::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector3::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Vector3::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Vector3::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Vector3::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Vector3::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector3::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector3::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Vector3::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector3::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Vector3::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Vector3::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Vector3::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Vector3::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Vector3::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Vector3::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Vector3::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Vector3::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Vector3::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Vector3::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Vector3::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Vector3::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Vector3::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Vector3::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Vector3::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Vector3::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Vector3::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Vector3::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Vector3::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Vector3::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Vector3::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Vector3::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Vector3::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Vector3::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Vector3::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Vector3::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Vector3::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Vector3::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Vector3::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Vector3::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Vector3::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Vector3::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Vector3::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Vector3::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Vector3::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Vector3::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Vector3::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Vector3::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Vector3::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Vector3::zzzz() const { return Vector4 (z, z, z, z); } + + + + + + +} // namespace diff --git a/externals/g3dlite/Vector4.cpp b/externals/g3dlite/Vector4.cpp new file mode 100644 index 00000000000..f6abc1a6e0c --- /dev/null +++ b/externals/g3dlite/Vector4.cpp @@ -0,0 +1,520 @@ +/** + @file Vector4.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2001-07-09 + @edited 2009-11-29 + */ + +#include <stdlib.h> +#include <limits> +#include "G3D/Vector4.h" +#include "G3D/Color4.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/Vector4int8.h" +#include "G3D/Matrix4.h" +#include "G3D/Any.h" + +namespace G3D { + +Vector4::Vector4(const Any& any) { + any.verifyName("Vector4"); + any.verifyType(Any::TABLE, Any::ARRAY); + any.verifySize(4); + + if (any.type() == Any::ARRAY) { + x = any[0]; + y = any[1]; + z = any[2]; + w = any[3]; + } else { + // Table + x = any["x"]; + y = any["y"]; + z = any["z"]; + w = any["w"]; + } +} + +Vector4::operator Any() const { + Any any(Any::ARRAY, "Vector4"); + any.append(x, y, z, w); + return any; +} + + +Vector4::Vector4(const Vector4int8& v) : x(v.x / 127.0f), y(v.y / 127.0f), z(v.z / 127.0f), w(v.w / 127.0f) { +} + + +const Vector4& Vector4::inf() { + static const Vector4 v((float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf(), (float)G3D::finf()); + return v; +} + + +const Vector4& Vector4::zero() { + static const Vector4 v(0,0,0,0); + return v; +} + +const Vector4& Vector4::nan() { + static Vector4 v((float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan(), (float)G3D::fnan()); + return v; +} + + +size_t Vector4::hashCode() const { + unsigned int xhash = (*(int*)(void*)(&x)); + unsigned int yhash = (*(int*)(void*)(&y)); + unsigned int zhash = (*(int*)(void*)(&z)); + unsigned int whash = (*(int*)(void*)(&w)); + + return xhash + (yhash * 37) + (zhash * 101) + (whash * 241); +} + + +Vector4::Vector4(const class Color4& c) { + x = c.r; + y = c.g; + z = c.b; + w = c.a; +} + + +Vector4::Vector4(const Vector2& v1, const Vector2& v2) { + x = v1.x; + y = v1.y; + z = v2.x; + w = v2.y; +} + + +Vector4::Vector4(const Vector2& v1, float fz, float fw) { + x = v1.x; + y = v1.y; + z = fz; + w = fw; +} + +Vector4::Vector4(BinaryInput& b) { + deserialize(b); +} + + +void Vector4::deserialize(BinaryInput& b) { + x = b.readFloat32(); + y = b.readFloat32(); + z = b.readFloat32(); + w = b.readFloat32(); +} + + +void Vector4::serialize(BinaryOutput& b) const { + b.writeFloat32(x); + b.writeFloat32(y); + b.writeFloat32(z); + b.writeFloat32(w); +} + +//---------------------------------------------------------------------------- + +Vector4 Vector4::operator*(const Matrix4& M) const { + Vector4 result; + for (int i = 0; i < 4; ++i) { + result[i] = 0.0f; + for (int j = 0; j < 4; ++j) { + result[i] += (*this)[j] * M[j][i]; + } + } + return result; +} + + +Vector4 Vector4::operator/ (float fScalar) const { + Vector4 kQuot; + + if ( fScalar != 0.0 ) { + float fInvScalar = 1.0f / fScalar; + kQuot.x = fInvScalar * x; + kQuot.y = fInvScalar * y; + kQuot.z = fInvScalar * z; + kQuot.w = fInvScalar * w; + return kQuot; + } else { + return Vector4::inf(); + } +} + +//---------------------------------------------------------------------------- +Vector4& Vector4::operator/= (float fScalar) { + if (fScalar != 0.0f) { + float fInvScalar = 1.0f / fScalar; + x *= fInvScalar; + y *= fInvScalar; + z *= fInvScalar; + w *= fInvScalar; + } else { + *this = Vector4::inf(); + } + + return *this; +} + + +//---------------------------------------------------------------------------- + +std::string Vector4::toString() const { + return G3D::format("(%g, %g, %g, %g)", x, y, z, w); +} +// 2-char swizzles + +Vector2 Vector4::xx() const { return Vector2 (x, x); } +Vector2 Vector4::yx() const { return Vector2 (y, x); } +Vector2 Vector4::zx() const { return Vector2 (z, x); } +Vector2 Vector4::wx() const { return Vector2 (w, x); } +Vector2 Vector4::xy() const { return Vector2 (x, y); } +Vector2 Vector4::yy() const { return Vector2 (y, y); } +Vector2 Vector4::zy() const { return Vector2 (z, y); } +Vector2 Vector4::wy() const { return Vector2 (w, y); } +Vector2 Vector4::xz() const { return Vector2 (x, z); } +Vector2 Vector4::yz() const { return Vector2 (y, z); } +Vector2 Vector4::zz() const { return Vector2 (z, z); } +Vector2 Vector4::wz() const { return Vector2 (w, z); } +Vector2 Vector4::xw() const { return Vector2 (x, w); } +Vector2 Vector4::yw() const { return Vector2 (y, w); } +Vector2 Vector4::zw() const { return Vector2 (z, w); } +Vector2 Vector4::ww() const { return Vector2 (w, w); } + +// 3-char swizzles + +Vector3 Vector4::xxx() const { return Vector3 (x, x, x); } +Vector3 Vector4::yxx() const { return Vector3 (y, x, x); } +Vector3 Vector4::zxx() const { return Vector3 (z, x, x); } +Vector3 Vector4::wxx() const { return Vector3 (w, x, x); } +Vector3 Vector4::xyx() const { return Vector3 (x, y, x); } +Vector3 Vector4::yyx() const { return Vector3 (y, y, x); } +Vector3 Vector4::zyx() const { return Vector3 (z, y, x); } +Vector3 Vector4::wyx() const { return Vector3 (w, y, x); } +Vector3 Vector4::xzx() const { return Vector3 (x, z, x); } +Vector3 Vector4::yzx() const { return Vector3 (y, z, x); } +Vector3 Vector4::zzx() const { return Vector3 (z, z, x); } +Vector3 Vector4::wzx() const { return Vector3 (w, z, x); } +Vector3 Vector4::xwx() const { return Vector3 (x, w, x); } +Vector3 Vector4::ywx() const { return Vector3 (y, w, x); } +Vector3 Vector4::zwx() const { return Vector3 (z, w, x); } +Vector3 Vector4::wwx() const { return Vector3 (w, w, x); } +Vector3 Vector4::xxy() const { return Vector3 (x, x, y); } +Vector3 Vector4::yxy() const { return Vector3 (y, x, y); } +Vector3 Vector4::zxy() const { return Vector3 (z, x, y); } +Vector3 Vector4::wxy() const { return Vector3 (w, x, y); } +Vector3 Vector4::xyy() const { return Vector3 (x, y, y); } +Vector3 Vector4::yyy() const { return Vector3 (y, y, y); } +Vector3 Vector4::zyy() const { return Vector3 (z, y, y); } +Vector3 Vector4::wyy() const { return Vector3 (w, y, y); } +Vector3 Vector4::xzy() const { return Vector3 (x, z, y); } +Vector3 Vector4::yzy() const { return Vector3 (y, z, y); } +Vector3 Vector4::zzy() const { return Vector3 (z, z, y); } +Vector3 Vector4::wzy() const { return Vector3 (w, z, y); } +Vector3 Vector4::xwy() const { return Vector3 (x, w, y); } +Vector3 Vector4::ywy() const { return Vector3 (y, w, y); } +Vector3 Vector4::zwy() const { return Vector3 (z, w, y); } +Vector3 Vector4::wwy() const { return Vector3 (w, w, y); } +Vector3 Vector4::xxz() const { return Vector3 (x, x, z); } +Vector3 Vector4::yxz() const { return Vector3 (y, x, z); } +Vector3 Vector4::zxz() const { return Vector3 (z, x, z); } +Vector3 Vector4::wxz() const { return Vector3 (w, x, z); } +Vector3 Vector4::xyz() const { return Vector3 (x, y, z); } +Vector3 Vector4::yyz() const { return Vector3 (y, y, z); } +Vector3 Vector4::zyz() const { return Vector3 (z, y, z); } +Vector3 Vector4::wyz() const { return Vector3 (w, y, z); } +Vector3 Vector4::xzz() const { return Vector3 (x, z, z); } +Vector3 Vector4::yzz() const { return Vector3 (y, z, z); } +Vector3 Vector4::zzz() const { return Vector3 (z, z, z); } +Vector3 Vector4::wzz() const { return Vector3 (w, z, z); } +Vector3 Vector4::xwz() const { return Vector3 (x, w, z); } +Vector3 Vector4::ywz() const { return Vector3 (y, w, z); } +Vector3 Vector4::zwz() const { return Vector3 (z, w, z); } +Vector3 Vector4::wwz() const { return Vector3 (w, w, z); } +Vector3 Vector4::xxw() const { return Vector3 (x, x, w); } +Vector3 Vector4::yxw() const { return Vector3 (y, x, w); } +Vector3 Vector4::zxw() const { return Vector3 (z, x, w); } +Vector3 Vector4::wxw() const { return Vector3 (w, x, w); } +Vector3 Vector4::xyw() const { return Vector3 (x, y, w); } +Vector3 Vector4::yyw() const { return Vector3 (y, y, w); } +Vector3 Vector4::zyw() const { return Vector3 (z, y, w); } +Vector3 Vector4::wyw() const { return Vector3 (w, y, w); } +Vector3 Vector4::xzw() const { return Vector3 (x, z, w); } +Vector3 Vector4::yzw() const { return Vector3 (y, z, w); } +Vector3 Vector4::zzw() const { return Vector3 (z, z, w); } +Vector3 Vector4::wzw() const { return Vector3 (w, z, w); } +Vector3 Vector4::xww() const { return Vector3 (x, w, w); } +Vector3 Vector4::yww() const { return Vector3 (y, w, w); } +Vector3 Vector4::zww() const { return Vector3 (z, w, w); } +Vector3 Vector4::www() const { return Vector3 (w, w, w); } + +// 4-char swizzles + +Vector4 Vector4::xxxx() const { return Vector4 (x, x, x, x); } +Vector4 Vector4::yxxx() const { return Vector4 (y, x, x, x); } +Vector4 Vector4::zxxx() const { return Vector4 (z, x, x, x); } +Vector4 Vector4::wxxx() const { return Vector4 (w, x, x, x); } +Vector4 Vector4::xyxx() const { return Vector4 (x, y, x, x); } +Vector4 Vector4::yyxx() const { return Vector4 (y, y, x, x); } +Vector4 Vector4::zyxx() const { return Vector4 (z, y, x, x); } +Vector4 Vector4::wyxx() const { return Vector4 (w, y, x, x); } +Vector4 Vector4::xzxx() const { return Vector4 (x, z, x, x); } +Vector4 Vector4::yzxx() const { return Vector4 (y, z, x, x); } +Vector4 Vector4::zzxx() const { return Vector4 (z, z, x, x); } +Vector4 Vector4::wzxx() const { return Vector4 (w, z, x, x); } +Vector4 Vector4::xwxx() const { return Vector4 (x, w, x, x); } +Vector4 Vector4::ywxx() const { return Vector4 (y, w, x, x); } +Vector4 Vector4::zwxx() const { return Vector4 (z, w, x, x); } +Vector4 Vector4::wwxx() const { return Vector4 (w, w, x, x); } +Vector4 Vector4::xxyx() const { return Vector4 (x, x, y, x); } +Vector4 Vector4::yxyx() const { return Vector4 (y, x, y, x); } +Vector4 Vector4::zxyx() const { return Vector4 (z, x, y, x); } +Vector4 Vector4::wxyx() const { return Vector4 (w, x, y, x); } +Vector4 Vector4::xyyx() const { return Vector4 (x, y, y, x); } +Vector4 Vector4::yyyx() const { return Vector4 (y, y, y, x); } +Vector4 Vector4::zyyx() const { return Vector4 (z, y, y, x); } +Vector4 Vector4::wyyx() const { return Vector4 (w, y, y, x); } +Vector4 Vector4::xzyx() const { return Vector4 (x, z, y, x); } +Vector4 Vector4::yzyx() const { return Vector4 (y, z, y, x); } +Vector4 Vector4::zzyx() const { return Vector4 (z, z, y, x); } +Vector4 Vector4::wzyx() const { return Vector4 (w, z, y, x); } +Vector4 Vector4::xwyx() const { return Vector4 (x, w, y, x); } +Vector4 Vector4::ywyx() const { return Vector4 (y, w, y, x); } +Vector4 Vector4::zwyx() const { return Vector4 (z, w, y, x); } +Vector4 Vector4::wwyx() const { return Vector4 (w, w, y, x); } +Vector4 Vector4::xxzx() const { return Vector4 (x, x, z, x); } +Vector4 Vector4::yxzx() const { return Vector4 (y, x, z, x); } +Vector4 Vector4::zxzx() const { return Vector4 (z, x, z, x); } +Vector4 Vector4::wxzx() const { return Vector4 (w, x, z, x); } +Vector4 Vector4::xyzx() const { return Vector4 (x, y, z, x); } +Vector4 Vector4::yyzx() const { return Vector4 (y, y, z, x); } +Vector4 Vector4::zyzx() const { return Vector4 (z, y, z, x); } +Vector4 Vector4::wyzx() const { return Vector4 (w, y, z, x); } +Vector4 Vector4::xzzx() const { return Vector4 (x, z, z, x); } +Vector4 Vector4::yzzx() const { return Vector4 (y, z, z, x); } +Vector4 Vector4::zzzx() const { return Vector4 (z, z, z, x); } +Vector4 Vector4::wzzx() const { return Vector4 (w, z, z, x); } +Vector4 Vector4::xwzx() const { return Vector4 (x, w, z, x); } +Vector4 Vector4::ywzx() const { return Vector4 (y, w, z, x); } +Vector4 Vector4::zwzx() const { return Vector4 (z, w, z, x); } +Vector4 Vector4::wwzx() const { return Vector4 (w, w, z, x); } +Vector4 Vector4::xxwx() const { return Vector4 (x, x, w, x); } +Vector4 Vector4::yxwx() const { return Vector4 (y, x, w, x); } +Vector4 Vector4::zxwx() const { return Vector4 (z, x, w, x); } +Vector4 Vector4::wxwx() const { return Vector4 (w, x, w, x); } +Vector4 Vector4::xywx() const { return Vector4 (x, y, w, x); } +Vector4 Vector4::yywx() const { return Vector4 (y, y, w, x); } +Vector4 Vector4::zywx() const { return Vector4 (z, y, w, x); } +Vector4 Vector4::wywx() const { return Vector4 (w, y, w, x); } +Vector4 Vector4::xzwx() const { return Vector4 (x, z, w, x); } +Vector4 Vector4::yzwx() const { return Vector4 (y, z, w, x); } +Vector4 Vector4::zzwx() const { return Vector4 (z, z, w, x); } +Vector4 Vector4::wzwx() const { return Vector4 (w, z, w, x); } +Vector4 Vector4::xwwx() const { return Vector4 (x, w, w, x); } +Vector4 Vector4::ywwx() const { return Vector4 (y, w, w, x); } +Vector4 Vector4::zwwx() const { return Vector4 (z, w, w, x); } +Vector4 Vector4::wwwx() const { return Vector4 (w, w, w, x); } +Vector4 Vector4::xxxy() const { return Vector4 (x, x, x, y); } +Vector4 Vector4::yxxy() const { return Vector4 (y, x, x, y); } +Vector4 Vector4::zxxy() const { return Vector4 (z, x, x, y); } +Vector4 Vector4::wxxy() const { return Vector4 (w, x, x, y); } +Vector4 Vector4::xyxy() const { return Vector4 (x, y, x, y); } +Vector4 Vector4::yyxy() const { return Vector4 (y, y, x, y); } +Vector4 Vector4::zyxy() const { return Vector4 (z, y, x, y); } +Vector4 Vector4::wyxy() const { return Vector4 (w, y, x, y); } +Vector4 Vector4::xzxy() const { return Vector4 (x, z, x, y); } +Vector4 Vector4::yzxy() const { return Vector4 (y, z, x, y); } +Vector4 Vector4::zzxy() const { return Vector4 (z, z, x, y); } +Vector4 Vector4::wzxy() const { return Vector4 (w, z, x, y); } +Vector4 Vector4::xwxy() const { return Vector4 (x, w, x, y); } +Vector4 Vector4::ywxy() const { return Vector4 (y, w, x, y); } +Vector4 Vector4::zwxy() const { return Vector4 (z, w, x, y); } +Vector4 Vector4::wwxy() const { return Vector4 (w, w, x, y); } +Vector4 Vector4::xxyy() const { return Vector4 (x, x, y, y); } +Vector4 Vector4::yxyy() const { return Vector4 (y, x, y, y); } +Vector4 Vector4::zxyy() const { return Vector4 (z, x, y, y); } +Vector4 Vector4::wxyy() const { return Vector4 (w, x, y, y); } +Vector4 Vector4::xyyy() const { return Vector4 (x, y, y, y); } +Vector4 Vector4::yyyy() const { return Vector4 (y, y, y, y); } +Vector4 Vector4::zyyy() const { return Vector4 (z, y, y, y); } +Vector4 Vector4::wyyy() const { return Vector4 (w, y, y, y); } +Vector4 Vector4::xzyy() const { return Vector4 (x, z, y, y); } +Vector4 Vector4::yzyy() const { return Vector4 (y, z, y, y); } +Vector4 Vector4::zzyy() const { return Vector4 (z, z, y, y); } +Vector4 Vector4::wzyy() const { return Vector4 (w, z, y, y); } +Vector4 Vector4::xwyy() const { return Vector4 (x, w, y, y); } +Vector4 Vector4::ywyy() const { return Vector4 (y, w, y, y); } +Vector4 Vector4::zwyy() const { return Vector4 (z, w, y, y); } +Vector4 Vector4::wwyy() const { return Vector4 (w, w, y, y); } +Vector4 Vector4::xxzy() const { return Vector4 (x, x, z, y); } +Vector4 Vector4::yxzy() const { return Vector4 (y, x, z, y); } +Vector4 Vector4::zxzy() const { return Vector4 (z, x, z, y); } +Vector4 Vector4::wxzy() const { return Vector4 (w, x, z, y); } +Vector4 Vector4::xyzy() const { return Vector4 (x, y, z, y); } +Vector4 Vector4::yyzy() const { return Vector4 (y, y, z, y); } +Vector4 Vector4::zyzy() const { return Vector4 (z, y, z, y); } +Vector4 Vector4::wyzy() const { return Vector4 (w, y, z, y); } +Vector4 Vector4::xzzy() const { return Vector4 (x, z, z, y); } +Vector4 Vector4::yzzy() const { return Vector4 (y, z, z, y); } +Vector4 Vector4::zzzy() const { return Vector4 (z, z, z, y); } +Vector4 Vector4::wzzy() const { return Vector4 (w, z, z, y); } +Vector4 Vector4::xwzy() const { return Vector4 (x, w, z, y); } +Vector4 Vector4::ywzy() const { return Vector4 (y, w, z, y); } +Vector4 Vector4::zwzy() const { return Vector4 (z, w, z, y); } +Vector4 Vector4::wwzy() const { return Vector4 (w, w, z, y); } +Vector4 Vector4::xxwy() const { return Vector4 (x, x, w, y); } +Vector4 Vector4::yxwy() const { return Vector4 (y, x, w, y); } +Vector4 Vector4::zxwy() const { return Vector4 (z, x, w, y); } +Vector4 Vector4::wxwy() const { return Vector4 (w, x, w, y); } +Vector4 Vector4::xywy() const { return Vector4 (x, y, w, y); } +Vector4 Vector4::yywy() const { return Vector4 (y, y, w, y); } +Vector4 Vector4::zywy() const { return Vector4 (z, y, w, y); } +Vector4 Vector4::wywy() const { return Vector4 (w, y, w, y); } +Vector4 Vector4::xzwy() const { return Vector4 (x, z, w, y); } +Vector4 Vector4::yzwy() const { return Vector4 (y, z, w, y); } +Vector4 Vector4::zzwy() const { return Vector4 (z, z, w, y); } +Vector4 Vector4::wzwy() const { return Vector4 (w, z, w, y); } +Vector4 Vector4::xwwy() const { return Vector4 (x, w, w, y); } +Vector4 Vector4::ywwy() const { return Vector4 (y, w, w, y); } +Vector4 Vector4::zwwy() const { return Vector4 (z, w, w, y); } +Vector4 Vector4::wwwy() const { return Vector4 (w, w, w, y); } +Vector4 Vector4::xxxz() const { return Vector4 (x, x, x, z); } +Vector4 Vector4::yxxz() const { return Vector4 (y, x, x, z); } +Vector4 Vector4::zxxz() const { return Vector4 (z, x, x, z); } +Vector4 Vector4::wxxz() const { return Vector4 (w, x, x, z); } +Vector4 Vector4::xyxz() const { return Vector4 (x, y, x, z); } +Vector4 Vector4::yyxz() const { return Vector4 (y, y, x, z); } +Vector4 Vector4::zyxz() const { return Vector4 (z, y, x, z); } +Vector4 Vector4::wyxz() const { return Vector4 (w, y, x, z); } +Vector4 Vector4::xzxz() const { return Vector4 (x, z, x, z); } +Vector4 Vector4::yzxz() const { return Vector4 (y, z, x, z); } +Vector4 Vector4::zzxz() const { return Vector4 (z, z, x, z); } +Vector4 Vector4::wzxz() const { return Vector4 (w, z, x, z); } +Vector4 Vector4::xwxz() const { return Vector4 (x, w, x, z); } +Vector4 Vector4::ywxz() const { return Vector4 (y, w, x, z); } +Vector4 Vector4::zwxz() const { return Vector4 (z, w, x, z); } +Vector4 Vector4::wwxz() const { return Vector4 (w, w, x, z); } +Vector4 Vector4::xxyz() const { return Vector4 (x, x, y, z); } +Vector4 Vector4::yxyz() const { return Vector4 (y, x, y, z); } +Vector4 Vector4::zxyz() const { return Vector4 (z, x, y, z); } +Vector4 Vector4::wxyz() const { return Vector4 (w, x, y, z); } +Vector4 Vector4::xyyz() const { return Vector4 (x, y, y, z); } +Vector4 Vector4::yyyz() const { return Vector4 (y, y, y, z); } +Vector4 Vector4::zyyz() const { return Vector4 (z, y, y, z); } +Vector4 Vector4::wyyz() const { return Vector4 (w, y, y, z); } +Vector4 Vector4::xzyz() const { return Vector4 (x, z, y, z); } +Vector4 Vector4::yzyz() const { return Vector4 (y, z, y, z); } +Vector4 Vector4::zzyz() const { return Vector4 (z, z, y, z); } +Vector4 Vector4::wzyz() const { return Vector4 (w, z, y, z); } +Vector4 Vector4::xwyz() const { return Vector4 (x, w, y, z); } +Vector4 Vector4::ywyz() const { return Vector4 (y, w, y, z); } +Vector4 Vector4::zwyz() const { return Vector4 (z, w, y, z); } +Vector4 Vector4::wwyz() const { return Vector4 (w, w, y, z); } +Vector4 Vector4::xxzz() const { return Vector4 (x, x, z, z); } +Vector4 Vector4::yxzz() const { return Vector4 (y, x, z, z); } +Vector4 Vector4::zxzz() const { return Vector4 (z, x, z, z); } +Vector4 Vector4::wxzz() const { return Vector4 (w, x, z, z); } +Vector4 Vector4::xyzz() const { return Vector4 (x, y, z, z); } +Vector4 Vector4::yyzz() const { return Vector4 (y, y, z, z); } +Vector4 Vector4::zyzz() const { return Vector4 (z, y, z, z); } +Vector4 Vector4::wyzz() const { return Vector4 (w, y, z, z); } +Vector4 Vector4::xzzz() const { return Vector4 (x, z, z, z); } +Vector4 Vector4::yzzz() const { return Vector4 (y, z, z, z); } +Vector4 Vector4::zzzz() const { return Vector4 (z, z, z, z); } +Vector4 Vector4::wzzz() const { return Vector4 (w, z, z, z); } +Vector4 Vector4::xwzz() const { return Vector4 (x, w, z, z); } +Vector4 Vector4::ywzz() const { return Vector4 (y, w, z, z); } +Vector4 Vector4::zwzz() const { return Vector4 (z, w, z, z); } +Vector4 Vector4::wwzz() const { return Vector4 (w, w, z, z); } +Vector4 Vector4::xxwz() const { return Vector4 (x, x, w, z); } +Vector4 Vector4::yxwz() const { return Vector4 (y, x, w, z); } +Vector4 Vector4::zxwz() const { return Vector4 (z, x, w, z); } +Vector4 Vector4::wxwz() const { return Vector4 (w, x, w, z); } +Vector4 Vector4::xywz() const { return Vector4 (x, y, w, z); } +Vector4 Vector4::yywz() const { return Vector4 (y, y, w, z); } +Vector4 Vector4::zywz() const { return Vector4 (z, y, w, z); } +Vector4 Vector4::wywz() const { return Vector4 (w, y, w, z); } +Vector4 Vector4::xzwz() const { return Vector4 (x, z, w, z); } +Vector4 Vector4::yzwz() const { return Vector4 (y, z, w, z); } +Vector4 Vector4::zzwz() const { return Vector4 (z, z, w, z); } +Vector4 Vector4::wzwz() const { return Vector4 (w, z, w, z); } +Vector4 Vector4::xwwz() const { return Vector4 (x, w, w, z); } +Vector4 Vector4::ywwz() const { return Vector4 (y, w, w, z); } +Vector4 Vector4::zwwz() const { return Vector4 (z, w, w, z); } +Vector4 Vector4::wwwz() const { return Vector4 (w, w, w, z); } +Vector4 Vector4::xxxw() const { return Vector4 (x, x, x, w); } +Vector4 Vector4::yxxw() const { return Vector4 (y, x, x, w); } +Vector4 Vector4::zxxw() const { return Vector4 (z, x, x, w); } +Vector4 Vector4::wxxw() const { return Vector4 (w, x, x, w); } +Vector4 Vector4::xyxw() const { return Vector4 (x, y, x, w); } +Vector4 Vector4::yyxw() const { return Vector4 (y, y, x, w); } +Vector4 Vector4::zyxw() const { return Vector4 (z, y, x, w); } +Vector4 Vector4::wyxw() const { return Vector4 (w, y, x, w); } +Vector4 Vector4::xzxw() const { return Vector4 (x, z, x, w); } +Vector4 Vector4::yzxw() const { return Vector4 (y, z, x, w); } +Vector4 Vector4::zzxw() const { return Vector4 (z, z, x, w); } +Vector4 Vector4::wzxw() const { return Vector4 (w, z, x, w); } +Vector4 Vector4::xwxw() const { return Vector4 (x, w, x, w); } +Vector4 Vector4::ywxw() const { return Vector4 (y, w, x, w); } +Vector4 Vector4::zwxw() const { return Vector4 (z, w, x, w); } +Vector4 Vector4::wwxw() const { return Vector4 (w, w, x, w); } +Vector4 Vector4::xxyw() const { return Vector4 (x, x, y, w); } +Vector4 Vector4::yxyw() const { return Vector4 (y, x, y, w); } +Vector4 Vector4::zxyw() const { return Vector4 (z, x, y, w); } +Vector4 Vector4::wxyw() const { return Vector4 (w, x, y, w); } +Vector4 Vector4::xyyw() const { return Vector4 (x, y, y, w); } +Vector4 Vector4::yyyw() const { return Vector4 (y, y, y, w); } +Vector4 Vector4::zyyw() const { return Vector4 (z, y, y, w); } +Vector4 Vector4::wyyw() const { return Vector4 (w, y, y, w); } +Vector4 Vector4::xzyw() const { return Vector4 (x, z, y, w); } +Vector4 Vector4::yzyw() const { return Vector4 (y, z, y, w); } +Vector4 Vector4::zzyw() const { return Vector4 (z, z, y, w); } +Vector4 Vector4::wzyw() const { return Vector4 (w, z, y, w); } +Vector4 Vector4::xwyw() const { return Vector4 (x, w, y, w); } +Vector4 Vector4::ywyw() const { return Vector4 (y, w, y, w); } +Vector4 Vector4::zwyw() const { return Vector4 (z, w, y, w); } +Vector4 Vector4::wwyw() const { return Vector4 (w, w, y, w); } +Vector4 Vector4::xxzw() const { return Vector4 (x, x, z, w); } +Vector4 Vector4::yxzw() const { return Vector4 (y, x, z, w); } +Vector4 Vector4::zxzw() const { return Vector4 (z, x, z, w); } +Vector4 Vector4::wxzw() const { return Vector4 (w, x, z, w); } +Vector4 Vector4::xyzw() const { return Vector4 (x, y, z, w); } +Vector4 Vector4::yyzw() const { return Vector4 (y, y, z, w); } +Vector4 Vector4::zyzw() const { return Vector4 (z, y, z, w); } +Vector4 Vector4::wyzw() const { return Vector4 (w, y, z, w); } +Vector4 Vector4::xzzw() const { return Vector4 (x, z, z, w); } +Vector4 Vector4::yzzw() const { return Vector4 (y, z, z, w); } +Vector4 Vector4::zzzw() const { return Vector4 (z, z, z, w); } +Vector4 Vector4::wzzw() const { return Vector4 (w, z, z, w); } +Vector4 Vector4::xwzw() const { return Vector4 (x, w, z, w); } +Vector4 Vector4::ywzw() const { return Vector4 (y, w, z, w); } +Vector4 Vector4::zwzw() const { return Vector4 (z, w, z, w); } +Vector4 Vector4::wwzw() const { return Vector4 (w, w, z, w); } +Vector4 Vector4::xxww() const { return Vector4 (x, x, w, w); } +Vector4 Vector4::yxww() const { return Vector4 (y, x, w, w); } +Vector4 Vector4::zxww() const { return Vector4 (z, x, w, w); } +Vector4 Vector4::wxww() const { return Vector4 (w, x, w, w); } +Vector4 Vector4::xyww() const { return Vector4 (x, y, w, w); } +Vector4 Vector4::yyww() const { return Vector4 (y, y, w, w); } +Vector4 Vector4::zyww() const { return Vector4 (z, y, w, w); } +Vector4 Vector4::wyww() const { return Vector4 (w, y, w, w); } +Vector4 Vector4::xzww() const { return Vector4 (x, z, w, w); } +Vector4 Vector4::yzww() const { return Vector4 (y, z, w, w); } +Vector4 Vector4::zzww() const { return Vector4 (z, z, w, w); } +Vector4 Vector4::wzww() const { return Vector4 (w, z, w, w); } +Vector4 Vector4::xwww() const { return Vector4 (x, w, w, w); } +Vector4 Vector4::ywww() const { return Vector4 (y, w, w, w); } +Vector4 Vector4::zwww() const { return Vector4 (z, w, w, w); } +Vector4 Vector4::wwww() const { return Vector4 (w, w, w, w); } + + +}; // namespace diff --git a/externals/g3dlite/debugAssert.cpp b/externals/g3dlite/debugAssert.cpp new file mode 100644 index 00000000000..a87161b261f --- /dev/null +++ b/externals/g3dlite/debugAssert.cpp @@ -0,0 +1,389 @@ +/** + @file debugAssert.cpp + + Windows implementation of assertion routines. + + @maintainer Morgan McGuire, graphics3d.com + + @created 2001-08-26 + @edited 2009-06-02 + */ + +#include "G3D/debugAssert.h" +#include "G3D/platform.h" +#ifdef G3D_WIN32 + #include <tchar.h> +#endif +#include "G3D/format.h" +#include "G3D/prompt.h" +#include <string> +#include "G3D/debugPrintf.h" +#include "G3D/Log.h" + +#include <cstdlib> + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif + +using namespace std; + +namespace G3D { namespace _internal { + +ConsolePrintHook _consolePrintHook; +AssertionHook _debugHook = _handleDebugAssert_; +AssertionHook _failureHook = _handleErrorCheck_; + +#ifdef G3D_LINUX +#if SOMEONE_MADE_THIS_USEFUL + Display* x11Display = NULL; + Window x11Window = 0; +#endif +#endif + + +#ifdef G3D_WIN32 +static void postToClipboard(const char *text) { + if (OpenClipboard(NULL)) { + HGLOBAL hMem = GlobalAlloc(GHND | GMEM_DDESHARE, strlen(text) + 1); + if (hMem) { + char *pMem = (char*)GlobalLock(hMem); + strcpy(pMem, text); + GlobalUnlock(hMem); + + EmptyClipboard(); + SetClipboardData(CF_TEXT, hMem); + } + + CloseClipboard(); + GlobalFree(hMem); + } +} +#endif + +/** + outTitle should be set before the call + */ +static void createErrorMessage( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + std::string& outTitle, + std::string& outMessage) { + + std::string le = ""; + const char* newline = "\n"; + + #ifdef G3D_WIN32 + newline = "\r\n"; + + // The last error value. (Which is preserved across the call). + DWORD lastErr = GetLastError(); + + // The decoded message from FormatMessage + LPTSTR formatMsg = NULL; + + if (NULL == formatMsg) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPTSTR)&formatMsg, + 0, + NULL); + } + + // Make sure the message got translated into something. + LPTSTR realLastErr; + if (NULL != formatMsg) { + realLastErr = formatMsg; + } else { + realLastErr = _T("Last error code does not exist."); + } + + if (lastErr != 0) { + le = G3D::format("Last Error (0x%08X): %s\r\n\r\n", lastErr, (LPCSTR)realLastErr); + } + + // Get rid of the allocated memory from FormatMessage. + if (NULL != formatMsg) { + LocalFree((LPVOID)formatMsg); + } + + char modulePath[MAX_PATH]; + GetModuleFileNameA(NULL, modulePath, MAX_PATH); + + const char* moduleName = strrchr(modulePath, '\\'); + outTitle = outTitle + string(" - ") + string(moduleName ? (moduleName + 1) : modulePath); + + #endif + + // Build the message. + outMessage = + G3D::format("%s%s%sExpression: %s%s%s:%d%s%s%s", + message.c_str(), newline, newline, expression, newline, + filename, lineNumber, newline, newline, le.c_str()); +} + + +bool _handleDebugAssert_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool useGuiPrompt) { + + std::string dialogTitle = "Assertion Failure"; + std::string dialogText = ""; + createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText); + + #ifdef G3D_WIN32 + DWORD lastErr = GetLastError(); + postToClipboard(dialogText.c_str()); + debugPrintf("\n%s\n", dialogText.c_str()); + #endif + + const int cBreak = 0; + const int cIgnore = 1; + const int cAbort = 2; + + static const char* choices[] = {"Debug", "Ignore", "Exit"}; + + // Log the error + Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText); + + int result = G3D::prompt(dialogTitle.c_str(), dialogText.c_str(), (const char**)choices, 3, useGuiPrompt); + +# ifdef G3D_WIN32 + // Put the incoming last error back. + SetLastError(lastErr); +# endif + + switch (result) { + // -1 shouldn't actually occur because it means + // that we're in release mode. + case -1: + case cBreak: + return true; + break; + + case cIgnore: + return false; + break; + + case cAbort: + exit(-1); + break; + } + + // Should never get here + return false; +} + + +bool _handleErrorCheck_( + const char* expression, + const std::string& message, + const char* filename, + int lineNumber, + bool useGuiPrompt) { + + std::string dialogTitle = "Critical Error"; + std::string dialogText = ""; + + createErrorMessage(expression, message, filename, lineNumber, dialogTitle, dialogText); + + // Log the error + Log::common()->print(std::string("\n**************************\n\n") + dialogTitle + "\n" + dialogText); + #ifdef G3D_WIN32 + DWORD lastErr = GetLastError(); + (void)lastErr; + postToClipboard(dialogText.c_str()); + debugPrintf("\n%s\n", dialogText.c_str()); + #endif + + static const char* choices[] = {"Ok"}; + + const std::string& m = + std::string("An internal error has occured in this program and it will now close. " + "The specific error is below. More information has been saved in \"") + + Log::getCommonLogFilename() + "\".\n" + dialogText; + + int result = G3D::prompt("Error", m.c_str(), (const char**)choices, 1, useGuiPrompt); + (void)result; + + return true; +} + + +#ifdef G3D_WIN32 +static HCURSOR oldCursor; +static RECT oldCursorRect; +static POINT oldCursorPos; +static int oldShowCursorCount; +#endif + +void _releaseInputGrab_() { + #ifdef G3D_WIN32 + + GetCursorPos(&oldCursorPos); + + // Stop hiding the cursor if the application hid it. + oldShowCursorCount = ShowCursor(true) - 1; + + if (oldShowCursorCount < -1) { + for (int c = oldShowCursorCount; c < -1; ++c) { + ShowCursor(true); + } + } + + // Set the default cursor in case the application + // set the cursor to NULL. + oldCursor = GetCursor(); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + // Allow the cursor full access to the screen + GetClipCursor(&oldCursorRect); + ClipCursor(NULL); + + #elif defined(G3D_LINUX) +#if SOMEONE_MADE_THIS_USEFUL + if (x11Display != NULL) { + XUngrabPointer(x11Display, CurrentTime); + XUngrabKeyboard(x11Display, CurrentTime); + if (x11Window != 0) { + //XUndefineCursor(x11Display, x11Window); + // TODO: Note that we leak this cursor; it should be + // freed in the restore code. + Cursor c = XCreateFontCursor(x11Display, 68); + XDefineCursor(x11Display, x11Window, c); + } + XSync(x11Display, false); + XAllowEvents(x11Display, AsyncPointer, CurrentTime); + XFlush(x11Display); + } +#endif + #elif defined(G3D_OSX) + // TODO: OS X + #endif +} + + +void _restoreInputGrab_() { + #ifdef G3D_WIN32 + + // Restore the old clipping region + ClipCursor(&oldCursorRect); + + SetCursorPos(oldCursorPos.x, oldCursorPos.y); + + // Restore the old cursor + SetCursor(oldCursor); + + // Restore old visibility count + if (oldShowCursorCount < 0) { + for (int c = 0; c > oldShowCursorCount; --c) { + ShowCursor(false); + } + } + + #elif defined(G3D_LINUX) + // TODO: Linux + #elif defined(G3D_OSX) + // TODO: OS X + #endif +} + + +}; // internal namespace + +void setAssertionHook(AssertionHook hook) { + G3D::_internal::_debugHook = hook; +} + +AssertionHook assertionHook() { + return G3D::_internal::_debugHook; +} + +void setFailureHook(AssertionHook hook) { + G3D::_internal::_failureHook = hook; +} + +AssertionHook failureHook() { + return G3D::_internal::_failureHook; +} + + +void setConsolePrintHook(ConsolePrintHook h) { + G3D::_internal::_consolePrintHook = h; +} + +ConsolePrintHook consolePrintHook() { + return G3D::_internal::_consolePrintHook; +} + + +std::string __cdecl debugPrint(const std::string& s) { +# ifdef G3D_WIN32 + const int MAX_STRING_LEN = 1024; + + // Windows can't handle really long strings sent to + // the console, so we break the string. + if (s.size() < MAX_STRING_LEN) { + OutputDebugStringA(s.c_str()); + } else { + for (unsigned int i = 0; i < s.size(); i += MAX_STRING_LEN) { + std::string sub = s.substr(i, MAX_STRING_LEN); + OutputDebugStringA(sub.c_str()); + } + } +# else + fprintf(stderr, "%s", s.c_str()); + fflush(stderr); +# endif + + return s; +} + +std::string __cdecl debugPrintf(const char* fmt ...) { + va_list argList; + va_start(argList, fmt); + std::string s = G3D::vformat(fmt, argList); + va_end(argList); + + return debugPrint(s); +// return debugPrint(consolePrint(s)); +} + +std::string consolePrint(const std::string& s) { + FILE* L = Log::common()->getFile(); + fprintf(L, "%s", s.c_str()); + + if (consolePrintHook()) { + consolePrintHook()(s); + } + + fflush(L); + return s; +} + + +std::string __cdecl consolePrintf(const char* fmt ...) { + va_list argList; + va_start(argList, fmt); + std::string s = G3D::vformat(fmt, argList); + va_end(argList); + + return consolePrint(s); +} + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/fileutils.cpp b/externals/g3dlite/fileutils.cpp new file mode 100644 index 00000000000..3f5eb579ba9 --- /dev/null +++ b/externals/g3dlite/fileutils.cpp @@ -0,0 +1,1165 @@ +/** + @file fileutils.cpp + + @author Morgan McGuire, graphics3d.com + + @author 2002-06-06 + @edited 2010-02-05 + */ + +#include <cstring> +#include <cstdio> +#include "G3D/platform.h" +#include "G3D/fileutils.h" +#include "G3D/BinaryInput.h" +#include "G3D/BinaryOutput.h" +#include "G3D/g3dmath.h" +#include "G3D/stringutils.h" +#include "G3D/Set.h" +#include "G3D/g3dfnmatch.h" + +#include <sys/stat.h> +#include <sys/types.h> +#if _HAVE_ZIP + #include "zip.h" +#endif + +#ifdef G3D_WIN32 + // Needed for _getcwd + #include <direct.h> + #include <io.h> +#else + #include <dirent.h> + #include <fnmatch.h> + #include <unistd.h> + #define _getcwd getcwd + #define _stat stat +#endif + + +namespace G3D { + +namespace _internal { + Set<std::string> currentFilesUsed; +} + +std::string pathConcat(const std::string& dirname, const std::string& file) { + // Ensure that the directory ends in a slash + if ((dirname.size() != 0) && + (dirname[dirname.size() - 1] != '/') && + (dirname[dirname.size() - 1] != '\\') && + (dirname[dirname.size() - 1] != ':')) { + return dirname + '/' + file; + } else { + return dirname + file; + } +} + +std::string resolveFilename(const std::string& filename) { + if (filename.size() >= 1) { + if ((filename[0] == '/') || (filename[0] == '\\')) { + // Already resolved + return filename; + } else { + + #ifdef G3D_WIN32 + if ((filename.size() >= 2) && (filename[1] == ':')) { + // There is a drive spec on the front. + if ((filename.size() >= 3) && ((filename[2] == '\\') || + (filename[2] == '/'))) { + // Already fully qualified + return filename; + } else { + // The drive spec is relative to the + // working directory on that drive. + debugAssertM(false, "Files of the form d:path are" + " not supported (use a fully qualified" + " name)."); + return filename; + } + } + #endif + } + } + + char buffer[1024]; + + // Prepend the working directory. + _getcwd(buffer, 1024); + + return format("%s/%s", buffer, filename.c_str()); +} + +bool zipfileExists(const std::string& filename) { + std::string outZipfile; + std::string outInternalFile; + return zipfileExists(filename, outZipfile, outInternalFile); +} + +std::string readWholeFile( + const std::string& filename) { + + _internal::currentFilesUsed.insert(filename); + + std::string s; + + debugAssert(filename != ""); + if (fileExists(filename, false)) { + + int64 length = fileLength(filename); + + char* buffer = (char*)System::alignedMalloc(length + 1, 16); + debugAssert(buffer); + FILE* f = fopen(filename.c_str(), "rb"); + debugAssert(f); + int ret = fread(buffer, 1, length, f); + debugAssert(ret == length);(void)ret; + fclose(f); + + buffer[length] = '\0'; + s = std::string(buffer); + + System::alignedFree(buffer); + + } else if (zipfileExists(filename)) { + + void* zipBuffer; + size_t length; + zipRead(filename, zipBuffer, length); + + char* buffer = (char*)System::alignedMalloc(length + 1, 16); + System::memcpy(buffer,zipBuffer, length + 1); + zipClose(zipBuffer); + + buffer[length] = '\0'; + s = std::string(buffer); + System::alignedFree(buffer); + } else { + debugAssertM(false, filename + " not found"); + } + + return s; +} + + +void zipRead(const std::string& file, + void*& data, + size_t& length) { + std::string zip, desiredFile; +#if _HAVE_ZIP + if (zipfileExists(file, zip, desiredFile)) { + struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL ); + { + struct zip_stat info; + zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required. + zip_stat( z, desiredFile.c_str(), ZIP_FL_NOCASE, &info ); + length = info.size; + // sets machines up to use MMX, if they want + data = System::alignedMalloc(length, 16); + struct zip_file *zf = zip_fopen( z, desiredFile.c_str(), ZIP_FL_NOCASE ); + { + int test = zip_fread( zf, data, length ); + debugAssertM((size_t)test == length, + desiredFile + " was corrupt because it unzipped to the wrong size."); + (void)test; + } + zip_fclose( zf ); + } + zip_close( z ); + } else { + data = NULL; + } +#else + data = NULL; +#endif +} + + +void zipClose(void* data) { + System::alignedFree(data); +} + + +int64 fileLength(const std::string& filename) { + struct _stat st; + int result = _stat(filename.c_str(), &st); + + if (result == -1) { +#if _HAVE_ZIP + std::string zip, contents; + if(zipfileExists(filename, zip, contents)){ + int64 requiredMem; + + struct zip *z = zip_open( zip.c_str(), ZIP_CHECKCONS, NULL ); + debugAssertM(z != NULL, zip + ": zip open failed."); + { + struct zip_stat info; + zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required. + int success = zip_stat( z, contents.c_str(), ZIP_FL_NOCASE, &info ); + debugAssertM(success == 0, zip + ": " + contents + ": zip stat failed."); + requiredMem = info.size; + } + zip_close( z ); + return requiredMem; + } else { + return -1; + } +#else + return -1; +#endif + } + + return st.st_size; +} + +/** Used by robustTmpfile. Returns nonzero if fread, fwrite, and fseek all +succeed on the file. + @author Morgan McGuire, http://graphics.cs.williams.edu */ +static int isFileGood(FILE* f) { + + int x, n, result; + + /* Must be a valid file handle */ + if (f == NULL) { + return 0; + } + + /* Try to write */ + x = 1234; + n = fwrite(&x, sizeof(int), 1, f); + + if (n != 1) { + return 0; + } + + /* Seek back to the beginning */ + result = fseek(f, 0, SEEK_SET); + if (result != 0) { + return 0; + } + + /* Read */ + n = fread(&x, sizeof(int), 1, f); + if (n != 1) { + return 0; + } + + /* Seek back to the beginning again */ + fseek(f, 0, SEEK_SET); + + return 1; +} + +FILE* createTempFile() { + FILE* t = NULL; + +//# ifdef G3D_WIN32 + t = tmpfile(); +//# else +// // On Unix, tmpfile generates a warning for any code that links against it. +// const char* tempfilename = "/tmp/g3dtemp.XXXXXXXX"; +// mktemp(tempfilename); +// t = fopen(tempfilename, "w"); +//# endif + +# ifdef _WIN32 + char* n = NULL; +# endif + char name[256]; + + if (isFileGood(t)) { + return t; + } + +# ifdef G3D_WIN32 + /* tmpfile failed; try the tmpnam routine */ + t = fopen(tmpnam(NULL), "w+"); + if (isFileGood(t)) { + return t; + } + + n = _tempnam("c:/tmp/", "t"); + /* Try to create something in C:\tmp */ + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + /* Try c:\temp */ + n = _tempnam("c:/temp/", "t"); + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + /* try the current directory */ + n = _tempnam("./", "t"); + t = fopen(n, "w+"); + if (isFileGood(t)) { + return t; + } + + sprintf(name, "%s/tmp%d", "c:/temp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } + + /* Try some hardcoded paths */ + sprintf(name, "%s/tmp%d", "c:/tmp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } +# else + sprintf(name, "%s/tmp%d", "/tmp", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } +#endif + + sprintf(name, "tmp%d", rand()); + t = fopen(name, "w+"); + if (isFileGood(t)) { + return t; + } + + fprintf(stderr, "Unable to create a temporary file; robustTmpfile returning NULL\n"); + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +void writeWholeFile( + const std::string& filename, + const std::string& str, + bool flush) { + + // Make sure the directory exists. + std::string root, base, ext, path; + Array<std::string> pathArray; + parseFilename(filename, root, pathArray, base, ext); + + path = root + stringJoin(pathArray, '/'); + if (! fileExists(path, false)) { + createDirectory(path); + } + + FILE* file = fopen(filename.c_str(), "wb"); + + debugAssert(file); + + fwrite(str.c_str(), str.size(), 1, file); + + if (flush) { + fflush(file); + } + fclose(file); +} + +/////////////////////////////////////////////////////////////////////////////// + +/** + Creates the directory (which may optionally end in a /) + and any parents needed to reach it. + */ +void createDirectory( + const std::string& dir) { + + if (dir == "") { + return; + } + + std::string d; + + // Add a trailing / if there isn't one. + switch (dir[dir.size() - 1]) { + case '/': + case '\\': + d = dir; + break; + + default: + d = dir + "/"; + } + + // If it already exists, do nothing + if (fileExists(d.substr(0, d.size() - 1)), false) { + return; + } + + // Parse the name apart + std::string root, base, ext; + Array<std::string> path; + + std::string lead; + parseFilename(d, root, path, base, ext); + debugAssert(base == ""); + debugAssert(ext == ""); + + // Begin with an extra period so "c:\" becomes "c:\.\" after + // appending a path and "c:" becomes "c:.\", not root: "c:\" + std::string p = root + "."; + + // Create any intermediate that doesn't exist + for (int i = 0; i < path.size(); ++i) { + p += "/" + path[i]; + if (! fileExists(p, false)) { + // Windows only requires one argument to mkdir, + // where as unix also requires the permissions. +# ifndef G3D_WIN32 + mkdir(p.c_str(), 0777); +# else + _mkdir(p.c_str()); +# endif + } + } +} + +/////////////////////////////////////////////////////////////////////////////// + +class FileSystemCache { +private: + + Table<std::string, Array<std::string> > m_files; + +public: + + bool fileExists(const std::string& filename) { + const std::string& path = resolveFilename(filenamePath(filename)); + const std::string& name = filenameBaseExt(filename); + + bool neverBeforeSeen = false; + Array<std::string>& fileList = m_files.getCreate(path, neverBeforeSeen); + if (neverBeforeSeen) { + if (! G3D::fileExists(path, true, false)) { + // The path itself doesn't exist... back out our insertion (which makes fileList& invalid) + m_files.remove(path); + return false; + } + + std::string spec = pathConcat(path, "*"); + + // Will automatically recurse into zipfiles + getFiles(spec, fileList); + getDirs(spec, fileList); + +# ifdef G3D_WIN32 + { + // Case insensitive + for (int i = 0; i < fileList.size(); ++i) { + fileList[i] = toLower(fileList[i]); + } + } +# endif + } + + if (filenameContainsWildcards(name)) { + // See if anything matches + for (int i = 0; i < fileList.size(); ++i) { + if (g3dfnmatch(name.c_str(), fileList[i].c_str(), 0) == 0) { + return true; + } + } + return false; + } else { + // On windows, this is a lower-lower comparison, so it is case insensitive + return fileList.contains(name); + } + } + + void clear() { + m_files.clear(); + } + + static FileSystemCache& instance() { + static FileSystemCache i; + return i; + } +}; + + +void clearFileSystemCache() { + FileSystemCache::instance().clear(); +} + +bool fileExists +(const std::string& _filename, + bool lookInZipfiles, + bool trustCache) { + + if (_filename.empty()) { + return false; + } + + // Remove trailing slash from directories + const std::string& filename = (endsWith(_filename, "/") || endsWith(_filename, "\\")) ? _filename.substr(0, _filename.length() - 1) : _filename; + + if (trustCache && lookInZipfiles) { +# ifdef G3D_WIN32 + // Case insensitive + return FileSystemCache::instance().fileExists(toLower(filename)); +# else + return FileSystemCache::instance().fileExists(filename); +# endif + } + + // Useful for debugging + //char curdir[1024]; _getcwd(curdir, 1024); + + struct _stat st; + int ret = _stat(filename.c_str(), &st); + + // _stat returns zero on success + bool exists = (ret == 0); + + if (! exists && lookInZipfiles) { + // Does not exist standalone, but might exist in a zipfile + + // These output arguments will be ignored + std::string zipDir, internalPath; + return zipfileExists(filename, zipDir, internalPath); + } else { + return exists; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#if _HAVE_ZIP +/* Helper methods for zipfileExists()*/ +// Given a string (the drive) and an array (the path), computes the directory +static void _zip_resolveDirectory(std::string& completeDir, const std::string& drive, const Array<std::string>& path, const int length){ + completeDir = drive; + int tempLength; + // if the given length is longer than the array, we correct it + if(length > path.length()){ + tempLength = path.length(); + } else{ + tempLength = length; + } + + for(int t = 0; t < tempLength; ++t){ + if(t > 0){ + completeDir += "/"; + } + completeDir += path[t]; + } +} + + +// assumes that zipDir references a .zip file +static bool _zip_zipContains(const std::string& zipDir, const std::string& desiredFile){ + struct zip *z = zip_open( zipDir.c_str(), ZIP_CHECKCONS, NULL ); + //the last parameter, an int, determines case sensitivity: + //1 is sensitive, 2 is not, 0 is default + int test = zip_name_locate( z, desiredFile.c_str(), ZIP_FL_NOCASE ); + zip_close( z ); + if(test == -1){ + return false; + } + return true; +} +#endif + +// If no zipfile exists, outZipfile and outInternalFile are unchanged +bool zipfileExists(const std::string& filename, std::string& outZipfile, + std::string& outInternalFile){ +#if _HAVE_ZIP + Array<std::string> path; + std::string drive, base, ext, zipfile, infile; + parseFilename(filename, drive, path, base, ext); + + // Put the filename back together + if ((base != "") && (ext != "")) { + infile = base + "." + ext; + } else { + infile = base + ext; + } + + // Remove "." from path + for (int i = 0; i < path.length(); ++i) { + if (path[i] == ".") { + path.remove(i); + --i; + } + } + + // Remove ".." from path + for (int i = 1; i < path.length(); ++i) { + if ((path[i] == "..") && (i > 0) && (path[i - 1] != "..")) { + // Remove both i and i - 1 + path.remove(i - 1, 2); + i -= 2; + } + } + + // Walk the path backwards, accumulating pieces onto the infile until + // we find a zipfile that contains it + for (int t = 0; t < path.length(); ++t){ + _zip_resolveDirectory(zipfile, drive, path, path.length() - t); + if (t > 0) { + infile = path[path.length() - t] + "/" + infile; + } + + if (endsWith(zipfile, "..")) { + return false; + } + + if (fileExists(zipfile, false)) { + // test if it actually is a zipfile + // if not, return false, a bad + // directory structure has been given, + // not a .zip + if (isZipfile(zipfile)){ + + if (_zip_zipContains(zipfile, infile)){ + outZipfile = zipfile; + outInternalFile = infile; + return true; + } else { + return false; + } + } else { + // the directory structure was valid but did not point to a .zip + return false; + } + } + + } +#endif + // not a valid directory structure ever, + // obviously no .zip was found within the path + return false; +} + +/////////////////////////////////////////////////////////////////////////////// + +std::string generateFilenameBase(const std::string& prefix, const std::string& suffix) { + Array<std::string> exist; + + // Note "template" is a reserved word in C++ + std::string templat = prefix + System::currentDateString() + "_"; + getFiles(templat + "*", exist); + + // Remove extensions + for (int i = 0; i < exist.size(); ++i) { + exist[i] = filenameBase(exist[i]); + } + + int num = 0; + std::string result; + templat += "%03d" + suffix; + do { + result = format(templat.c_str(), num); + ++num; + } while (exist.contains(result)); + + return result; +} + +/////////////////////////////////////////////////////////////////////////////// + +void copyFile( + const std::string& source, + const std::string& dest) { + + #ifdef G3D_WIN32 + CopyFileA(source.c_str(), dest.c_str(), FALSE); + #else + // TODO: don't use BinaryInput and BinaryOutput + // Read it all in, then dump it out + BinaryInput in(source, G3D_LITTLE_ENDIAN); + BinaryOutput out(dest, G3D_LITTLE_ENDIAN); + out.writeBytes(in.getCArray(), in.size()); + out.commit(false); + #endif +} + +////////////////////////////////////////////////////////////////////////////// + +void parseFilename( + const std::string& filename, + std::string& root, + Array<std::string>& path, + std::string& base, + std::string& ext) { + + std::string f = filename; + + root = ""; + path.clear(); + base = ""; + ext = ""; + + if (f == "") { + // Empty filename + return; + } + + // See if there is a root/drive spec. + if ((f.size() >= 2) && (f[1] == ':')) { + + if ((f.size() > 2) && isSlash(f[2])) { + + // e.g. c:\foo + root = f.substr(0, 3); + f = f.substr(3, f.size() - 3); + + } else { + + // e.g. c:foo + root = f.substr(2); + f = f.substr(2, f.size() - 2); + + } + + } else if ((f.size() >= 2) & isSlash(f[0]) && isSlash(f[1])) { + + // e.g. //foo + root = f.substr(0, 2); + f = f.substr(2, f.size() - 2); + + } else if (isSlash(f[0])) { + + root = f.substr(0, 1); + f = f.substr(1, f.size() - 1); + + } + + // Pull the extension off + { + // Find the period + size_t i = f.rfind('.'); + + if (i != std::string::npos) { + // Make sure it is after the last slash! + size_t j = iMax(f.rfind('/'), f.rfind('\\')); + if ((j == std::string::npos) || (i > j)) { + ext = f.substr(i + 1, f.size() - i - 1); + f = f.substr(0, i); + } + } + } + + // Pull the basename off + { + // Find the last slash + size_t i = iMax(f.rfind('/'), f.rfind('\\')); + + if (i == std::string::npos) { + + // There is no slash; the basename is the whole thing + base = f; + f = ""; + + } else if ((i != std::string::npos) && (i < f.size() - 1)) { + + base = f.substr(i + 1, f.size() - i - 1); + f = f.substr(0, i); + + } + } + + // Parse what remains into path. + size_t prev, cur = 0; + + while (cur < f.size()) { + prev = cur; + + // Allow either slash + size_t i = f.find('/', prev + 1); + size_t j = f.find('\\', prev + 1); + if (i == std::string::npos) { + i = f.size(); + } + + if (j == std::string::npos) { + j = f.size(); + } + + cur = iMin(i, j); + + if (cur == std::string::npos) { + cur = f.size(); + } + + path.append(f.substr(prev, cur - prev)); + ++cur; + } +} + + +/** + Helper for getFileList and getDirectoryList. + + @param wantFiles If false, returns the directories, otherwise + returns the files. + @param includePath If true, the names include paths + */ +static void getFileOrDirListNormal +(const std::string& filespec, + Array<std::string>& files, + bool wantFiles, + bool includePath) { + + bool test = wantFiles ? true : false; + + std::string path = ""; + + // Find the place where the path ends and the file-spec begins + size_t i = filespec.rfind('/'); + size_t j = filespec.rfind('\\'); + + // Drive letters on Windows can separate a path + size_t k = filespec.rfind(':'); + + if (((j != std::string::npos) && (j > i)) || + (i == std::string::npos)) { + i = j; + } + + if (((k != std::string::npos) && (k > i)) || + (i == std::string::npos)) { + i = k; + } + + // If there is a path, pull it off + if (i != std::string::npos) { + path = filespec.substr(0, i + 1); + } + + std::string prefix = path; + + if (path.size() > 0) { + // Strip the trailing character + path = path.substr(0, path.size() - 1); + } + +# ifdef G3D_WIN32 + { + struct _finddata_t fileinfo; + + long handle = _findfirst(filespec.c_str(), &fileinfo); + int result = handle; + + while (result != -1) { + if ((((fileinfo.attrib & _A_SUBDIR) == 0) == test) && + strcmp(fileinfo.name, ".") && + strcmp(fileinfo.name, "..")) { + + if (includePath) { + files.append(prefix + fileinfo.name); + } else { + files.append(fileinfo.name); + } + } + + result = _findnext(handle, &fileinfo); + } + } +# else + { + if (path == "") { + // Empty paths don't work on Unix + path = "."; + } + + // Unix implementation + DIR* dir = opendir(path.c_str()); + + if (dir != NULL) { + struct dirent* entry = readdir(dir); + + while (entry != NULL) { + + // Exclude '.' and '..' + if ((strcmp(entry->d_name, ".") != 0) && + (strcmp(entry->d_name, "..") != 0)) { + + // Form a name with a path + std::string filename = prefix + entry->d_name; + // See if this is a file or a directory + struct _stat st; + bool exists = _stat(filename.c_str(), &st) != -1; + + if (exists && + + // Make sure it has the correct type + (((st.st_mode & S_IFDIR) == 0) == test) && + + // Make sure it matches the wildcard + (fnmatch(filespec.c_str(), + filename.c_str(), + FNM_PATHNAME) == 0)) { + + if (includePath) { + files.append(filename); + } else { + files.append(entry->d_name); + } + } + } + + entry = readdir(dir); + } + closedir(dir); + } + } +# endif +} + +#if _HAVE_ZIP +/** + @param path The zipfile name (no trailing slash) + @param prefix Directory inside the zipfile. No leading slash, must have trailing slash if non-empty. + @param file Name inside the zipfile that we are testing to see if it matches prefix + "*" + */ +static void _zip_addEntry(const std::string& path, + const std::string& prefix, + const std::string& file, + Set<std::string>& files, + bool wantFiles, + bool includePath) { + + // Make certain we are within the desired parent folder (prefix) + if (beginsWith(file, prefix)) { + // validityTest was prefix/file + + // Extract everything to the right of the prefix + std::string s = file.substr(prefix.length()); + + if (s == "") { + // This was the name of the prefix + return; + } + + // See if there are any slashes + size_t slashPos = s.find('/'); + + bool add = false; + + if (slashPos == std::string::npos) { + // No slashes, so s must be a file + add = wantFiles; + } else if (! wantFiles) { + // Not all zipfiles list directories as explicit entries. + // Because of this, if we're looking for directories and see + // any path longer than prefix, we must add the subdirectory. + // The Set will fix duplicates for us. + s = s.substr(0, slashPos); + add = true; + } + + if (add) { + if (includePath) { + files.insert(path + "/" + prefix + s); + } else { + files.insert(s); + } + } + } +} +#endif + +static void getFileOrDirListZip(const std::string& path, + const std::string& prefix, + Array<std::string>& files, + bool wantFiles, + bool includePath){ +#if _HAVE_ZIP + struct zip *z = zip_open( path.c_str(), ZIP_CHECKCONS, NULL ); + + Set<std::string> fileSet; + + int count = zip_get_num_files( z ); + for( int i = 0; i < count; ++i ) { + struct zip_stat info; + zip_stat_init( &info ); // TODO: Docs unclear if zip_stat_init is required. + zip_stat_index( z, i, ZIP_FL_NOCASE, &info ); + _zip_addEntry(path, prefix, info.name, fileSet, wantFiles, includePath); + } + + zip_close( z ); + + fileSet.getMembers(files); +#endif +} + + +static void determineFileOrDirList( + const std::string& filespec, + Array<std::string>& files, + bool wantFiles, + bool includePath) { + + // if it is a .zip, prefix will specify the folder within + // whose contents we want to see + std::string prefix = ""; + std::string path = filenamePath(filespec); + + if ((path.size() > 0) && isSlash(path[path.size() - 1])) { + // Strip the trailing slash + path = path.substr(0, path.length() -1); + } + + if ((path == "") || fileExists(path, false)) { + if ((path != "") && isZipfile(path)) { + // .zip should only work if * is specified as the Base + Ext + // Here, we have been asked for the root's contents + debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard"); + getFileOrDirListZip(path, prefix, files, wantFiles, includePath); + } else { + // It is a normal directory + getFileOrDirListNormal(filespec, files, wantFiles, includePath); + } + } else if (zipfileExists(filenamePath(filespec), path, prefix)) { + // .zip should only work if * is specified as the Base + Ext + // Here, we have been asked for the contents of a folder within the .zip + debugAssertM(filenameBaseExt(filespec) == "*", "Can only call getFiles/getDirs on zipfiles using '*' wildcard"); + getFileOrDirListZip(path, prefix, files, wantFiles, includePath); + } +} + + +void getFiles(const std::string& filespec, + Array<std::string>& files, + bool includePath) { + + determineFileOrDirList(filespec, files, true, includePath); +} + + +void getDirs( + const std::string& filespec, + Array<std::string>& files, + bool includePath) { + + determineFileOrDirList(filespec, files, false, includePath); +} + + +std::string filenameBaseExt(const std::string& filename) { + int i = filename.rfind("/"); + int j = filename.rfind("\\"); + + if ((j > i) && (j >= 0)) { + i = j; + } + +# ifdef G3D_WIN32 + j = filename.rfind(":"); + if ((i == -1) && (j >= 0)) { + i = j; + } +# endif + + if (i == -1) { + return filename; + } else { + return filename.substr(i + 1, filename.length() - i); + } +} + + +std::string filenameBase(const std::string& s) { + std::string drive; + std::string base; + std::string ext; + Array<std::string> path; + + parseFilename(s, drive, path, base, ext); + return base; +} + + +std::string filenameExt(const std::string& filename) { + int i = filename.rfind("."); + if (i >= 0) { + return filename.substr(i + 1, filename.length() - i); + } else { + return ""; + } +} + + +std::string filenamePath(const std::string& filename) { + int i = filename.rfind("/"); + int j = filename.rfind("\\"); + + if ((j > i) && (j >= 0)) { + i = j; + } + +# ifdef G3D_WIN32 + j = filename.rfind(":"); + if ((i == -1) && (j >= 0)) { + i = j; + } +# endif + + if (i == -1) { + return ""; + } else { + return filename.substr(0, i+1); + } +} + + +bool isZipfile(const std::string& filename) { + + FILE* f = fopen(filename.c_str(), "r"); + if (f == NULL) { + return false; + } + uint8 header[4]; + fread(header, 4, 1, f); + + const uint8 zipHeader[4] = {0x50, 0x4b, 0x03, 0x04}; + for (int i = 0; i < 4; ++i) { + if (header[i] != zipHeader[i]) { + fclose(f); + return false; + } + } + + fclose(f); + return true; +} + + +bool isDirectory(const std::string& filename) { + struct _stat st; + bool exists = _stat(filename.c_str(), &st) != -1; + return exists && ((st.st_mode & S_IFDIR) != 0); +} + + +bool filenameContainsWildcards(const std::string& filename) { + return (filename.find('*') != std::string::npos) || (filename.find('?') != std::string::npos); +} + + +bool fileIsNewer(const std::string& src, const std::string& dst) { + struct _stat sts; + bool sexists = _stat(src.c_str(), &sts) != -1; + + struct _stat dts; + bool dexists = _stat(dst.c_str(), &dts) != -1; + + return sexists && ((! dexists) || (sts.st_mtime > dts.st_mtime)); +} + + +Array<std::string> filesUsed() { + Array<std::string> f; + _internal::currentFilesUsed.getMembers(f); + return f; +} + +} + +#ifndef G3D_WIN32 + #undef _stat +#endif diff --git a/externals/g3dlite/format.cpp b/externals/g3dlite/format.cpp new file mode 100644 index 00000000000..d9d1b516393 --- /dev/null +++ b/externals/g3dlite/format.cpp @@ -0,0 +1,164 @@ +/** + @file format.cpp + + @author Morgan McGuire, graphics3d.com + + @created 2000-09-09 + @edited 2006-08-14 +*/ + +#include "G3D/format.h" +#include "G3D/platform.h" +#include "G3D/System.h" + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif // _MSC_VER + +// If your platform does not have vsnprintf, you can find a +// implementation at http://www.ijs.si/software/snprintf/ + +namespace G3D { + +std::string __cdecl format(const char* fmt,...) { + va_list argList; + va_start(argList,fmt); + std::string result = vformat(fmt, argList); + va_end(argList); + + return result; +} + +#if defined(_MSC_VER) && (_MSC_VER >= 1300) +// Both MSVC seems to use the non-standard vsnprintf +// so we are using vscprintf to determine buffer size, however +// only MSVC7 and up headers include vscprintf for some reason. +std::string vformat(const char *fmt, va_list argPtr) { + // We draw the line at a 1MB string. + const int maxSize = 1000000; + + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. + const int bufSize = 161; + char stackBuffer[bufSize]; + + // MSVC does not support va_copy + int actualSize = _vscprintf(fmt, argPtr) + 1; + + if (actualSize > bufSize) { + + // Now use the heap. + char* heapBuffer = NULL; + + if (actualSize < maxSize) { + + heapBuffer = (char*)System::malloc(maxSize + 1); + _vsnprintf(heapBuffer, maxSize, fmt, argPtr); + heapBuffer[maxSize] = '\0'; + } else { + heapBuffer = (char*)System::malloc(actualSize); + vsprintf(heapBuffer, fmt, argPtr); + } + + std::string formattedString(heapBuffer); + System::free(heapBuffer); + return formattedString; + } else { + + vsprintf(stackBuffer, fmt, argPtr); + return std::string(stackBuffer); + } +} + +#elif defined(_MSC_VER) && (_MSC_VER < 1300) + +std::string vformat(const char *fmt, va_list argPtr) { + // We draw the line at a 1MB string. + const int maxSize = 1000000; + + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. + const int bufSize = 161; + char stackBuffer[bufSize]; + + // MSVC6 doesn't support va_copy, however it also seems to compile + // correctly if we just pass our argument list along. Note that + // this whole code block is only compiled if we're on MSVC6 anyway + int actualWritten = _vsnprintf(stackBuffer, bufSize, fmt, argPtr); + + // Not a big enough buffer, bufSize characters written + if (actualWritten == -1) { + + int heapSize = 512; + double powSize = 1.0; + char* heapBuffer = (char*)System::malloc(heapSize); + + while ((_vsnprintf(heapBuffer, heapSize, fmt, argPtr) == -1) && + (heapSize < maxSize)) { + + heapSize = iCeil(heapSize * ::pow((double)2.0, powSize++)); + heapBuffer = (char*)System::realloc(heapBuffer, heapSize); + } + + heapBuffer[heapSize-1] = '\0'; + + std::string heapString(heapBuffer); + System::free(heapBuffer); + + return heapString; + } else { + + return std::string(stackBuffer); + } +} + +#else + +// glibc 2.1 has been updated to the C99 standard +std::string vformat(const char* fmt, va_list argPtr) { + // If the string is less than 161 characters, + // allocate it on the stack because this saves + // the malloc/free time. The number 161 is chosen + // to support two lines of text on an 80 character + // console (plus the null terminator). + const int bufSize = 161; + char stackBuffer[bufSize]; + + va_list argPtrCopy; + va_copy(argPtrCopy, argPtr); + int numChars = vsnprintf(stackBuffer, bufSize, fmt, argPtrCopy); + va_end(argPtrCopy); + + if (numChars >= bufSize) { + // We didn't allocate a big enough string. + char* heapBuffer = (char*)System::malloc((numChars + 1) * sizeof(char)); + + debugAssert(heapBuffer); + int numChars2 = vsnprintf(heapBuffer, numChars + 1, fmt, argPtr); + debugAssert(numChars2 == numChars); + (void)numChars2; + + std::string result(heapBuffer); + + System::free(heapBuffer); + + return result; + + } else { + + return std::string(stackBuffer); + + } +} + +#endif + +} // namespace + +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/g3dlite/g3dfnmatch.cpp b/externals/g3dlite/g3dfnmatch.cpp new file mode 100644 index 00000000000..39ef7b31914 --- /dev/null +++ b/externals/g3dlite/g3dfnmatch.cpp @@ -0,0 +1,204 @@ +/*- +* Copyright (c) 1992, 1993 +*The Regents of the University of California. All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* 3. All advertising materials mentioning features or use of this software +* must display the following acknowledgement: +*This product includes software developed by the University of +*California, Berkeley and its contributors. +* 4. Neither the name of the University nor the names of its contributors +* may be used to endorse or promote products derived from this software +* without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +* SUCH DAMAGE. +* +*@(#)fnmatch.h8.1 (Berkeley) 6/2/93 +* +* From FreeBSD fnmatch.h 1.7 +* $Id: g3dfnmatch.cpp,v 1.2 2010/02/06 10:03:24 corey_taylor Exp $ +*/ +#include "G3D/g3dfnmatch.h" + +#ifdef G3D_WIN32 + +#include <ctype.h> +#include <string.h> +#include <stdio.h> + +namespace G3D { + +#define EOS '\0' + +static const char *rangematch(const char *, char, int); + +int g3dfnmatch(const char *pattern, const char *string, int flags) +{ + const char *stringstart; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + else if (c == '/' && flags & FNM_PATHNAME) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!rangematch(pattern, *string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && flags & FNM_PATHNAME) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && flags & FNM_PATHNAME) + return (FNM_NOMATCH); + if ((pattern = + rangematch(pattern, *string, flags)) == NULL) + return (FNM_NOMATCH); + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + if (c == *string) + ; + else if ((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string))) + ; + else if ((flags & FNM_PREFIX_DIRS) && *string == EOS && + ((c == '/' && string != stringstart) || + (string == stringstart+1 && *stringstart == '/'))) + return (0); + else + return (FNM_NOMATCH); + string++; + break; + } + /* NOTREACHED */ +} + +static const char * +rangematch(const char *pattern, char test, int flags) +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ( (negate = (*pattern == '!' || *pattern == '^')) ) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = tolower((unsigned char)test); + + for (ok = 0; (c = *pattern++) != ']';) { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (NULL); + + if (flags & FNM_CASEFOLD) + c = tolower((unsigned char)c); + + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (NULL); + + if (flags & FNM_CASEFOLD) + c2 = tolower((unsigned char)c2); + + if ((unsigned char)c <= (unsigned char)test && + (unsigned char)test <= (unsigned char)c2) + ok = 1; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} + +} + +#else + +namespace G3D { +int g3dfnmatch(const char * a, const char *b, int c) { + return fnmatch(a, b, c); +} +} + +#endif + diff --git a/externals/g3dlite/g3dmath.cpp b/externals/g3dlite/g3dmath.cpp new file mode 100644 index 00000000000..ad85e9efb9b --- /dev/null +++ b/externals/g3dlite/g3dmath.cpp @@ -0,0 +1,108 @@ +/** + @file g3dmath.cpp + + @author Morgan McGuire, graphics3d.com + + @created 2001-06-02 + @edited 2004-02-24 + */ + +#include "G3D/g3dmath.h" +#include <cstdlib> +#include <cstring> + +namespace G3D { + +float gaussRandom(float mean, float stdev) { + + // Using Box-Mueller method from http://www.taygeta.com/random/gaussian.html + // Modified to specify standard deviation and mean of distribution + float w, x1, x2; + + // Loop until w is less than 1 so that log(w) is negative + do { + x1 = uniformRandom(-1.0, 1.0); + x2 = uniformRandom(-1.0, 1.0); + + w = float(square(x1) + square(x2)); + } while (w > 1.0f); + + // Transform to gassian distribution + // Multiply by sigma (stdev ^ 2) and add mean. + return x2 * (float)square(stdev) * sqrtf((-2.0f * logf(w) ) / w) + mean; +} + +/** + This value should not be tested against directly, instead + G3D::isNan() and G3D::isFinite() will return reliable results. */ +double inf() { + return std::numeric_limits<double>::infinity(); +} + +bool isNaN(float x) { + static const float n = nan(); + return memcmp(&x, &n, sizeof(float)) == 0; +} + +bool isNaN(double x) { + static const double n = nan(); + return memcmp(&x, &n, sizeof(double)) == 0; +} + + +/** + This value should not be tested against directly, instead + G3D::isNan() and G3D::isFinite() will return reliable results. */ +float finf() { + return std::numeric_limits<float>::infinity(); +} + +/** This value should not be tested against directly, instead + G3D::isNan() and G3D::isFinite() will return reliable results. */ +double nan() { + // double is a standard type and should have quiet NaN + return std::numeric_limits<double>::quiet_NaN(); +} + +float fnan() { + // double is a standard type and should have quiet NaN + return std::numeric_limits<float>::quiet_NaN(); +} + + +int highestBit(uint32 x) { + // Binary search. + int base = 0; + + if (x & 0xffff0000) { + base = 16; + x >>= 16; + } + if (x & 0x0000ff00) { + base += 8; + x >>= 8; + } + if (x & 0x000000f0) { + base += 4; + x >>= 4; + } + + static const int lut[] = {-1,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3}; + return base + lut[x]; +} + + +int iRandom(int low, int high) { + int r = iFloor(low + (high - low + 1) * (double)rand() / RAND_MAX); + + // There is a *very small* chance of generating + // a number larger than high. + if (r > high) { + return high; + } else { + return r; + } +} + + +} diff --git a/externals/g3dlite/prompt.cpp b/externals/g3dlite/prompt.cpp new file mode 100644 index 00000000000..6a28e6462b4 --- /dev/null +++ b/externals/g3dlite/prompt.cpp @@ -0,0 +1,729 @@ +/** + @file prompt.cpp + + @author Morgan McGuire, http://graphics.cs.williams.edu + @cite Windows dialog interface by Max McGuire, mmcguire@ironlore.com + @cite Font setting code by Kurt Miller, kurt@flipcode.com + + @created 2000-08-26 + @edited 2005-01-14 + */ + +#include "G3D/prompt.h" +#include "G3D/platform.h" + +#include <stdio.h> + +#ifdef G3D_WIN32 +# include <sstream> +# include <conio.h> +#else +# define _getch getchar +#endif + +#ifdef G3D_OSX + +/*#ifdef __LP64__ +# undef __LP64__ +#endif +*/ + +# include <Carbon/Carbon.h> + +/* +#ifdef G3D_64BIT +# define __LP64__ +#endif +*/ + +#endif + +namespace G3D { + +#ifdef G3D_WIN32 + +namespace _internal { +/** + Generic Win32 dialog facility. + @author Max McGuire + */ +class DialogTemplate { +public: + + DialogTemplate(LPCSTR caption, DWORD style, + int x, int y, int w, int h, + LPCSTR font = NULL, WORD fontSize = 8) { + + usedBufferLength = sizeof(DLGTEMPLATE); + totalBufferLength = usedBufferLength; + + dialogTemplate = (DLGTEMPLATE*)malloc(totalBufferLength); + + dialogTemplate->style = style; + + if (font != NULL) { + dialogTemplate->style |= DS_SETFONT; + } + + dialogTemplate->x = (short)x; + dialogTemplate->y = (short)y; + dialogTemplate->cx = (short)w; + dialogTemplate->cy = (short)h; + dialogTemplate->cdit = 0; + + dialogTemplate->dwExtendedStyle = 0; + + // The dialog box doesn't have a menu or a special class + AppendData("\0", 2); + AppendData("\0", 2); + + // Add the dialog's caption to the template + + AppendString(caption); + + if (font != NULL) { + AppendData(&fontSize, sizeof(WORD)); + AppendString(font); + } + } + + void AddComponent(LPCSTR type, LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + DLGITEMTEMPLATE item; + + item.style = style; + item.x = (short)x; + item.y = (short)y; + item.cx = (short)w; + item.cy = (short)h; + item.id = id; + + item.dwExtendedStyle = exStyle; + + AppendData(&item, sizeof(DLGITEMTEMPLATE)); + + AppendString(type); + AppendString(caption); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + // Increment the component count + dialogTemplate->cdit++; + + } + + + void AddButton(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0080, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddEditBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0081, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddStatic(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0082, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddListBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0083, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = sizeof(WORD) + 5 * sizeof(WCHAR); + AppendData(&creationDataLength, sizeof(WORD)); + + AppendString("TEST"); + + } + + + void AddScrollBar(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0084, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + void AddComboBox(LPCSTR caption, DWORD style, DWORD exStyle, int x, int y, int w, int h, WORD id) { + + AddStandardComponent(0x0085, caption, style, exStyle, x, y, w, h, id); + + WORD creationDataLength = 0; + AppendData(&creationDataLength, sizeof(WORD)); + + } + + + /** + * + * Returns a pointer to the Win32 dialog template which the object + * represents. This pointer may become invalid if additional components + * are added to the template. + * + */ + operator const DLGTEMPLATE*() const { + return dialogTemplate; + } + + virtual ~DialogTemplate() { + free(dialogTemplate); + } + +protected: + + void AddStandardComponent(WORD type, LPCSTR caption, DWORD style, DWORD exStyle, + int x, int y, int w, int h, WORD id, LPSTR font = NULL, WORD fontSize = 8) { + + DLGITEMTEMPLATE item; + + // DWORD align the beginning of the component data + + AlignData(sizeof(DWORD)); + + item.style = style; + if (font != NULL) { + item.style |= DS_SETFONT; + } + item.x = (short)x; + item.y = (short)y; + item.cx = (short)w; + item.cy = (short)h; + item.id = id; + + item.dwExtendedStyle = exStyle; + + AppendData(&item, sizeof(DLGITEMTEMPLATE)); + + WORD preType = 0xFFFF; + + AppendData(&preType, sizeof(WORD)); + AppendData(&type, sizeof(WORD)); + + AppendString(caption); + + if (font != NULL) { + AppendData(&fontSize, sizeof(WORD)); + AppendString(font); + } + + // Increment the component count + dialogTemplate->cdit++; + } + + + void AlignData(int size) { + + int paddingSize = usedBufferLength % size; + + if (paddingSize != 0) { + EnsureSpace(paddingSize); + usedBufferLength += paddingSize; + } + + } + + void AppendString(LPCSTR string) { + + int length = MultiByteToWideChar(CP_ACP, 0, string, -1, NULL, 0); + + WCHAR* wideString = (WCHAR*)malloc(sizeof(WCHAR) * length); + MultiByteToWideChar(CP_ACP, 0, string, -1, wideString, length); + + AppendData(wideString, length * sizeof(WCHAR)); + free(wideString); + + } + + void AppendData(const void* data, int dataLength) { + + EnsureSpace(dataLength); + + memcpy((char*)dialogTemplate + usedBufferLength, data, dataLength); + usedBufferLength += dataLength; + + } + + void EnsureSpace(int length) { + if (length + usedBufferLength > totalBufferLength) { + totalBufferLength += length * 2; + + void* newBuffer = malloc(totalBufferLength); + memcpy(newBuffer, dialogTemplate, usedBufferLength); + + free(dialogTemplate); + dialogTemplate = (DLGTEMPLATE*)newBuffer; + } + } + +private: + + DLGTEMPLATE* dialogTemplate; + + int totalBufferLength; + int usedBufferLength; + +}; + + +struct PromptParams { + const char* message; + const char* title; +}; + +/** + * Constants for controls. + */ +#define IDC_MESSAGE 1000 +#define IDC_BUTTON0 2000 + +INT_PTR CALLBACK PromptDlgProc(HWND hDlg, UINT msg, + WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + { + PromptParams *params = (PromptParams*)lParam; + ::SetWindowTextA(::GetDlgItem(hDlg, IDC_MESSAGE), params->message); + + ::SetFocus(::GetDlgItem(hDlg, IDC_BUTTON0)); + + SetWindowTextA(hDlg, params->title); + + HFONT hfont = + CreateFontA(16, 0, 0, 0, FW_NORMAL, + FALSE, FALSE, FALSE, + ANSI_CHARSET,OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS, + PROOF_QUALITY, FIXED_PITCH | FF_MODERN, "Courier New"); + + SendDlgItemMessage(hDlg, IDC_MESSAGE, WM_SETFONT, (WPARAM)hfont, MAKELPARAM(TRUE,0)); + + + break; + } + case WM_COMMAND: + { + int choiceNumber = LOWORD(wParam) - IDC_BUTTON0; + if ((choiceNumber >= 0) && (choiceNumber < 10)) { + EndDialog(hDlg, choiceNumber); + return TRUE; + } + } + + break; + + case WM_NCDESTROY: + // Under SDL 1.2.6 we get a NCDESTROY message for no reason and the + // window is immediately closed. This is here to debug the problem. + (void)0; + break; + + } + + return FALSE; +} + +}; // namespace _internal + + +using namespace _internal; + +/** + * Show a dialog prompt. + */ +static int guiPrompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + int width = 280; + int height = 128; + + const int buttonSpacing = 2; + const int buttonWidth = + (width - buttonSpacing * 2 - + buttonSpacing * (numChoices - 1)) / numChoices; + const int buttonHeight = 13; + + + DialogTemplate dialogTemplate( + windowTitle, + WS_CAPTION | DS_CENTER | WS_SYSMENU, + 10, 10, width, height, + "Tahoma"); + + dialogTemplate.AddEditBox( + "Edit", WS_VISIBLE | ES_READONLY | ES_OEMCONVERT | ES_MULTILINE | WS_TABSTOP, WS_EX_STATICEDGE, + 2, 2, width - 4, height - buttonHeight - 7, IDC_MESSAGE); + + int i; + for (i = 0; i < numChoices; i++) { + + int x = buttonSpacing + i * (buttonWidth + buttonSpacing); + int y = height - buttonHeight - buttonSpacing; + + dialogTemplate.AddButton(choice[i], WS_VISIBLE | WS_TABSTOP, 0, + x, y, buttonWidth, buttonHeight, IDC_BUTTON0 + (WORD)i); + + } + + // Convert all single \n characters to \r\n for proper printing + int strLen = 0; + const char* pStr = prompt; + + while (*pStr != '\0') { + if ((*pStr == '\n') && (pStr != prompt)) { + if (*(pStr - 1) != '\r') { + ++strLen; + } + } + ++strLen; + ++pStr; + } + + char* newStr = (char*)malloc(strLen + 1); + + const char* pStr2 = prompt; + char* pNew = newStr; + + while (*pStr2 != '\0') { + if ((*pStr2 == '\n') && (pStr2 != prompt)) { + if (*(pStr2 - 1) != '\r') { + *pNew = '\r'; + ++pNew; + } + } + *pNew = *pStr2; + ++pNew; + ++pStr2; + } + + *pNew = '\0'; + + PromptParams params; + params.message = newStr;; + params.title = windowTitle; + + HMODULE module = GetModuleHandle(0); + int ret = DialogBoxIndirectParam(module, dialogTemplate, NULL, (DLGPROC) PromptDlgProc, (DWORD)¶ms); + + free(newStr); + + /* + For debugging when DialogBoxIndirectParam fails: + + // The last error value. (Which is preserved across the call). + DWORD lastErr = GetLastError(); + + // The decoded message from FormatMessage + LPTSTR formatMsg = NULL; + + if (NULL == formatMsg) { + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + lastErr, + 0, + (LPTSTR)&formatMsg, + 0, + NULL); + } + + // Make sure the message got translated into something. + LPTSTR realLastErr; + if (NULL != formatMsg) { + realLastErr = formatMsg; + } else { + realLastErr = "Last error code does not exist."; + } + + // Get rid of the allocated memory from FormatMessage. + if (NULL != formatMsg) { + LocalFree((LPVOID)formatMsg); + } + */ + + return ret; +} + +#endif + + +/** + * Show a prompt on stdout + */ +static int textPrompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + printf("\n___________________________________________________\n"); + printf("%s\n", windowTitle); + printf("%s", prompt); + + if (numChoices > 10) { + numChoices = 10; + } + + int c = -1; + if (numChoices > 1) { + printf("\n"); + printf("Choose an option by number:"); + + while ((c < 0) || (c >= numChoices)) { + printf("\n"); + + for (int i = 0; i < numChoices; i++) { + if (numChoices <= 3) { + printf(" (%d) %s ", i, choice[i]); + } else { + printf(" (%d) %s\n", i, choice[i]); + } + } + + printf("\n> "); + c = _getch() - '0'; + + if ((c < 0) || (c >= numChoices)) { + printf("'%d' is not a valid choice.", c); + } else { + printf("%d", c); + } + } + + } else if (numChoices == 1) { + + printf("\nPress any key for '%s'...", choice[0]); + _getch(); + c = 0; + + } else { + + printf("\nPress any key..."); + _getch(); + c = 0; + } + + printf("\n___________________________________________________\n"); + return c; +} + +#ifdef G3D_OSX + +// See http://developer.apple.com/documentation/Carbon/Reference/Carbon_Event_Manager_Ref/index.html + +#define CARBON_COMMANDID_START 128 +#define CARBON_BUTTON_SPACING 12 +#define CARBON_BUTTON_HEIGHT 20 +#define CARBON_BUTTON_MINWIDTH 69 +#define CARBON_WINDOW_PADDING 20 + +struct CallbackData { + WindowRef refWindow; + + /** Index of this particular button */ + int myIndex; + + /** Buttons store their index into here when pressed. */ + int* whichButton; +}; + +/** + Assumes that userData is a pointer to a carbon_evt_data_t. + + */ +static pascal OSStatus DoCommandEvent(EventHandlerCallRef handlerRef, EventRef event, void* userData) { + // See http://developer.apple.com/documentation/Carbon/Conceptual/HandlingWindowsControls/index.html + + CallbackData& callbackData = *(CallbackData*)userData; + +# pragma unused(handlerRef) + + callbackData.whichButton[0] = callbackData.myIndex; + + // If we get here we can close the window + ::QuitAppModalLoopForWindow(callbackData.refWindow); + + // Return noErr to indicate that we handled the event + return noErr; +} + +static int guiPrompt +(const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices) { + + WindowRef window; + + int iNumButtonRows = 0; + int iButtonWidth = -1; + OSStatus err = noErr; + + // Determine number of rows of buttons + while (iButtonWidth < CARBON_BUTTON_MINWIDTH) { + ++iNumButtonRows; + iButtonWidth = + (550 - (CARBON_WINDOW_PADDING*2 + + (CARBON_BUTTON_SPACING*numChoices))) / + (numChoices/iNumButtonRows); + } + + // Window Variables + Rect rectWin = {0, 0, 200 + ((iNumButtonRows-1) * (CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)), 550}; // top, left, bottom, right + CFStringRef szWindowTitle = CFStringCreateWithCString(kCFAllocatorDefault, windowTitle, kCFStringEncodingUTF8); + + window = NULL; + + err = CreateNewWindow(kMovableAlertWindowClass, kWindowStandardHandlerAttribute|kWindowCompositingAttribute, &rectWin, &window); + err = SetWindowTitleWithCFString(window, szWindowTitle); + err = SetThemeWindowBackground(window, kThemeBrushAlertBackgroundActive, false); + assert(err == noErr); + + // Event Handler Variables + EventTypeSpec buttonSpec[] = {{ kEventClassControl, kEventControlHit }, { kEventClassCommand, kEventCommandProcess }}; + EventHandlerUPP buttonHandler = NewEventHandlerUPP(DoCommandEvent); + + // Static Text Variables + Rect rectStatic = {20, 20, 152, 530}; + CFStringRef szStaticText = CFStringCreateWithCString(kCFAllocatorDefault, prompt, kCFStringEncodingUTF8); + ControlRef refStaticText = NULL; + err = CreateStaticTextControl(window, &rectStatic, szStaticText, NULL, &refStaticText); + + // Button Variables + Rect bounds[numChoices]; + CFStringRef caption[numChoices]; + ControlRef button[numChoices]; + + int whichButton=-1; + CallbackData callbackData[numChoices]; + + // Create the Buttons and assign event handlers + for (int i = 0; i < numChoices; ++i) { + bounds[i].top = 160 + ((CARBON_BUTTON_HEIGHT+CARBON_BUTTON_SPACING)*(i%iNumButtonRows)); + bounds[i].right = 530 - ((iButtonWidth+CARBON_BUTTON_SPACING)*(i/iNumButtonRows)); + bounds[i].left = bounds[i].right - iButtonWidth; + bounds[i].bottom = bounds[i].top + CARBON_BUTTON_HEIGHT; + + // Convert the button captions to Apple strings + caption[i] = CFStringCreateWithCString(kCFAllocatorDefault, choice[i], kCFStringEncodingUTF8); + + err = CreatePushButtonControl(window, &bounds[i], caption[i], &button[i]); + assert(err == noErr); + + err = SetControlCommandID(button[i], CARBON_COMMANDID_START + i); + assert(err == noErr); + + callbackData[i].refWindow = window; + callbackData[i].myIndex = i; + callbackData[i].whichButton = &whichButton; + + err = InstallControlEventHandler(button[i], buttonHandler, + GetEventTypeCount(buttonSpec), buttonSpec, + &callbackData[i], NULL); + assert(err == noErr); + } + + // Show Dialog + err = RepositionWindow(window, NULL, kWindowCenterOnMainScreen); + ShowWindow(window); + BringToFront(window); + err = ActivateWindow(window, true); + + // Hack to get our window/process to the front... + ProcessSerialNumber psn = { 0, kCurrentProcess}; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + SetFrontProcess (&psn); + + // Run in Modal State + err = RunAppModalLoopForWindow(window); + + // Dispose of Button Related Data + for (int i = 0; i < numChoices; ++i) { + // Dispose of controls + DisposeControl(button[i]); + + // Release CFStrings + CFRelease(caption[i]); + } + + // Dispose of Other Controls + DisposeControl(refStaticText); + + // Dispose of Event Handlers + DisposeEventHandlerUPP(buttonHandler); + + // Dispose of Window + DisposeWindow(window); + + // Release CFStrings + CFRelease(szWindowTitle); + CFRelease(szStaticText); + + // Return Selection + return whichButton; +} + +#endif + +int prompt( + const char* windowTitle, + const char* prompt, + const char** choice, + int numChoices, + bool useGui) { + + #ifdef G3D_WIN32 + if (useGui) { + // Build the message box + return guiPrompt(windowTitle, prompt, choice, numChoices); + } + #endif + + #ifdef G3D_OSX + if (useGui){ + //Will default to text prompt if numChoices > 4 + return guiPrompt(windowTitle, prompt, choice, numChoices); + } + #endif + return textPrompt(windowTitle, prompt, choice, numChoices); +} + + +void msgBox( + const std::string& message, + const std::string& title) { + + const char *choice[] = {"Ok"}; + prompt(title.c_str(), message.c_str(), choice, 1, true); +} + +#ifndef G3D_WIN32 + #undef _getch +#endif + +};// namespace + diff --git a/externals/g3dlite/stringutils.cpp b/externals/g3dlite/stringutils.cpp new file mode 100644 index 00000000000..c3876ebb6a4 --- /dev/null +++ b/externals/g3dlite/stringutils.cpp @@ -0,0 +1,275 @@ +/** + @file stringutils.cpp + + @maintainer Morgan McGuire, http://graphics.cs.williams.edu + + @created 2000-09-09 + @edited 2008-01-10 +*/ + +#include "G3D/platform.h" +#include "G3D/stringutils.h" +#include "G3D/BinaryInput.h" +#include <algorithm> + +namespace G3D { + +#ifdef _MSC_VER + // disable: "C++ exception handler used" +# pragma warning (push) +# pragma warning (disable : 4530) +#endif +#ifdef G3D_WIN32 + const char* NEWLINE = "\r\n"; +#else + const char* NEWLINE = "\n"; + static bool iswspace(int ch) { return (ch==' ' || ch=='\t' || ch=='\n' || ch=='\r'); } +#endif + +void parseCommaSeparated(const std::string s, Array<std::string>& array, bool stripQuotes) { + array.fastClear(); + if (s == "") { + return; + } + + size_t begin = 0; + const char delimiter = ','; + const char quote = '\"'; + do { + size_t end = begin; + // Find the next comma, or the end of the string + bool inQuotes = false; + while ((end < s.length()) && (inQuotes || (s[end] != delimiter))) { + if (s[end] == quote) { + if ((end < s.length() - 2) && (s[end + 1] == quote) && (s[end + 2]) == quote) { + // Skip over the superquote + end += 2; + } + inQuotes = ! inQuotes; + } + ++end; + } + array.append(s.substr(begin, end - begin)); + begin = end + 1; + } while (begin < s.length()); + + if (stripQuotes) { + for (int i = 0; i < array.length(); ++i) { + std::string& t = array[i]; + int L = t.length(); + if ((L > 1) && (t[0] == quote) && (t[L - 1] == quote)) { + if ((L > 6) && (t[1] == quote) && (t[2] == quote) && (t[L - 3] == quote) && (t[L - 2] == quote)) { + // Triple-quote + t = t.substr(3, L - 6); + } else { + // Double-quote + t = t.substr(1, L - 2); + } + } + } + } +} + +bool beginsWith( + const std::string& test, + const std::string& pattern) { + + if (test.size() >= pattern.size()) { + for (int i = 0; i < (int)pattern.size(); ++i) { + if (pattern[i] != test[i]) { + return false; + } + } + return true; + } else { + return false; + } +} + + +bool endsWith( + const std::string& test, + const std::string& pattern) { + + if (test.size() >= pattern.size()) { + int te = test.size() - 1; + int pe = pattern.size() - 1; + for (int i = pattern.size() - 1; i >= 0; --i) { + if (pattern[pe - i] != test[te - i]) { + return false; + } + } + return true; + } else { + return false; + } +} + + +std::string wordWrap( + const std::string& input, + int numCols) { + + std::string output; + size_t c = 0; + int len; + + // Don't make lines less than this length + int minLength = numCols / 4; + size_t inLen = input.size(); + + bool first = true; + while (c < inLen) { + if (first) { + first = false; + } else { + output += NEWLINE; + } + + if ((int)inLen - (int)c - 1 < numCols) { + // The end + output += input.substr(c, inLen - c); + break; + } + + len = numCols; + + // Look at character c + numCols, see if it is a space. + while ((len > minLength) && + (input[c + len] != ' ')) { + len--; + } + + if (len == minLength) { + // Just crop + len = numCols; + + } + + output += input.substr(c, len); + c += len; + if (c < input.size()) { + // Collapse multiple spaces. + while ((input[c] == ' ') && (c < input.size())) { + c++; + } + } + } + + return output; +} + + +int stringCompare( + const std::string& s1, + const std::string& s2) { + + return stringPtrCompare(&s1, &s2); +} + + +int stringPtrCompare( + const std::string* s1, + const std::string* s2) { + + return s1->compare(*s2); +} + + +std::string toUpper(const std::string& x) { + std::string result = x; + std::transform(result.begin(), result.end(), result.begin(), toupper); + return result; +} + + +std::string toLower(const std::string& x) { + std::string result = x; + std::transform(result.begin(), result.end(), result.begin(), tolower); + return result; +} + + +Array<std::string> stringSplit( + const std::string& x, + char splitChar) { + + Array<std::string> out; + + // Pointers to the beginning and end of the substring + const char* start = x.c_str(); + const char* stop = start; + + while ((stop = strchr(start, splitChar))) { + out.append(std::string(start, stop - start)); + start = stop + 1; + } + + // Append the last one + out.append(std::string(start)); + + return out; +} + + +std::string stringJoin( + const Array<std::string>& a, + char joinChar) { + + std::string out; + + for (int i = 0; i < (int)a.size() - 1; ++i) { + out += a[i] + joinChar; + } + + if (a.size() > 0) { + return out + a.last(); + } else { + return out; + } +} + + +std::string stringJoin( + const Array<std::string>& a, + const std::string& joinStr) { + + std::string out; + + for (int i = 0; i < (int)a.size() - 1; ++i) { + out += a[i] + joinStr; + } + + if (a.size() > 0) { + return out + a.last(); + } else { + return out; + } +} + + +std::string trimWhitespace( + const std::string& s) { + + size_t left = 0; + + // Trim from left + while ((left < s.length()) && iswspace(s[left])) { + ++left; + } + + int right = s.length() - 1; + // Trim from right + while ((right > (int)left) && iswspace(s[right])) { + --right; + } + + return s.substr(left, right - left + 1); +} + +}; // namespace + +#undef NEWLINE +#ifdef _MSC_VER +# pragma warning (pop) +#endif diff --git a/externals/jemalloc/CMakeLists.txt b/externals/jemalloc/CMakeLists.txt index c3e4e81782c..1e32407b456 100644 --- a/externals/jemalloc/CMakeLists.txt +++ b/externals/jemalloc/CMakeLists.txt @@ -1,27 +1,15 @@ -SET(jmalloc_STAT_SRC - arena.c - chunk.c - chunk_mmap.c - ckh.c - extent.c - huge.c - mb.c - prof.c - tcache.c - base.c - chunk_dss.c - chunk_swap.c - ctl.c - hash.c - jemalloc.c - mutex.c - stats.c - ) + +file(GLOB sources *.c) +set(jemalloc_STAT_SRC + ${sources} +) include_directories( - ${CMAKE_SOURCE_DIR}/dep/include - ) + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/include/internal +) add_definitions(-D_GNU_SOURCE -D_REENTRANT) -add_library(jmalloc STATIC ${jmalloc_STAT_SRC})
\ No newline at end of file +add_library(jemalloc STATIC ${sources}) diff --git a/externals/jemalloc/include/internal/arena.h b/externals/jemalloc/jemalloc/internal/arena.h index bb4ce2a54f7..bb4ce2a54f7 100644 --- a/externals/jemalloc/include/internal/arena.h +++ b/externals/jemalloc/jemalloc/internal/arena.h diff --git a/externals/jemalloc/include/internal/base.h b/externals/jemalloc/jemalloc/internal/base.h index e353f309bd2..e353f309bd2 100644 --- a/externals/jemalloc/include/internal/base.h +++ b/externals/jemalloc/jemalloc/internal/base.h diff --git a/externals/jemalloc/include/internal/chunk.h b/externals/jemalloc/jemalloc/internal/chunk.h index 1f6abf782f1..1f6abf782f1 100644 --- a/externals/jemalloc/include/internal/chunk.h +++ b/externals/jemalloc/jemalloc/internal/chunk.h diff --git a/externals/jemalloc/include/internal/chunk_dss.h b/externals/jemalloc/jemalloc/internal/chunk_dss.h index 6be4ad1f212..6be4ad1f212 100644 --- a/externals/jemalloc/include/internal/chunk_dss.h +++ b/externals/jemalloc/jemalloc/internal/chunk_dss.h diff --git a/externals/jemalloc/include/internal/chunk_mmap.h b/externals/jemalloc/jemalloc/internal/chunk_mmap.h index 8fb90b77c9b..8fb90b77c9b 100644 --- a/externals/jemalloc/include/internal/chunk_mmap.h +++ b/externals/jemalloc/jemalloc/internal/chunk_mmap.h diff --git a/externals/jemalloc/include/internal/chunk_swap.h b/externals/jemalloc/jemalloc/internal/chunk_swap.h index d50cb197449..d50cb197449 100644 --- a/externals/jemalloc/include/internal/chunk_swap.h +++ b/externals/jemalloc/jemalloc/internal/chunk_swap.h diff --git a/externals/jemalloc/include/internal/ckh.h b/externals/jemalloc/jemalloc/internal/ckh.h index c39ea5c75ef..c39ea5c75ef 100644 --- a/externals/jemalloc/include/internal/ckh.h +++ b/externals/jemalloc/jemalloc/internal/ckh.h diff --git a/externals/jemalloc/include/internal/ctl.h b/externals/jemalloc/jemalloc/internal/ctl.h index 7bbf21e0e85..7bbf21e0e85 100644 --- a/externals/jemalloc/include/internal/ctl.h +++ b/externals/jemalloc/jemalloc/internal/ctl.h diff --git a/externals/jemalloc/include/internal/extent.h b/externals/jemalloc/jemalloc/internal/extent.h index 33a4e9a3852..33a4e9a3852 100644 --- a/externals/jemalloc/include/internal/extent.h +++ b/externals/jemalloc/jemalloc/internal/extent.h diff --git a/externals/jemalloc/include/internal/hash.h b/externals/jemalloc/jemalloc/internal/hash.h index d12cdb8359f..d12cdb8359f 100644 --- a/externals/jemalloc/include/internal/hash.h +++ b/externals/jemalloc/jemalloc/internal/hash.h diff --git a/externals/jemalloc/include/internal/huge.h b/externals/jemalloc/jemalloc/internal/huge.h index 3cf32f7506d..3cf32f7506d 100644 --- a/externals/jemalloc/include/internal/huge.h +++ b/externals/jemalloc/jemalloc/internal/huge.h diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h b/externals/jemalloc/jemalloc/internal/jemalloc_internal.h index 03782dd6690..03782dd6690 100644 --- a/externals/jemalloc/include/internal/jemalloc_internal.h +++ b/externals/jemalloc/jemalloc/internal/jemalloc_internal.h diff --git a/externals/jemalloc/include/internal/jemalloc_internal.h.in b/externals/jemalloc/jemalloc/internal/jemalloc_internal.h.in index 2c3f32f126d..2c3f32f126d 100644 --- a/externals/jemalloc/include/internal/jemalloc_internal.h.in +++ b/externals/jemalloc/jemalloc/internal/jemalloc_internal.h.in diff --git a/externals/jemalloc/include/internal/mb.h b/externals/jemalloc/jemalloc/internal/mb.h index 1707aa91d68..1707aa91d68 100644 --- a/externals/jemalloc/include/internal/mb.h +++ b/externals/jemalloc/jemalloc/internal/mb.h diff --git a/externals/jemalloc/include/internal/mutex.h b/externals/jemalloc/jemalloc/internal/mutex.h index 108bfa8abfd..108bfa8abfd 100644 --- a/externals/jemalloc/include/internal/mutex.h +++ b/externals/jemalloc/jemalloc/internal/mutex.h diff --git a/externals/jemalloc/include/internal/prof.h b/externals/jemalloc/jemalloc/internal/prof.h index 6e71552d85e..6e71552d85e 100644 --- a/externals/jemalloc/include/internal/prof.h +++ b/externals/jemalloc/jemalloc/internal/prof.h diff --git a/externals/jemalloc/include/internal/ql.h b/externals/jemalloc/jemalloc/internal/ql.h index a9ed2393f0c..a9ed2393f0c 100644 --- a/externals/jemalloc/include/internal/ql.h +++ b/externals/jemalloc/jemalloc/internal/ql.h diff --git a/externals/jemalloc/include/internal/qr.h b/externals/jemalloc/jemalloc/internal/qr.h index fe22352fedd..fe22352fedd 100644 --- a/externals/jemalloc/include/internal/qr.h +++ b/externals/jemalloc/jemalloc/internal/qr.h diff --git a/externals/jemalloc/include/internal/rb.h b/externals/jemalloc/jemalloc/internal/rb.h index ee9b009d235..ee9b009d235 100644 --- a/externals/jemalloc/include/internal/rb.h +++ b/externals/jemalloc/jemalloc/internal/rb.h diff --git a/externals/jemalloc/include/internal/stats.h b/externals/jemalloc/jemalloc/internal/stats.h index cbf035ff2b9..cbf035ff2b9 100644 --- a/externals/jemalloc/include/internal/stats.h +++ b/externals/jemalloc/jemalloc/internal/stats.h diff --git a/externals/jemalloc/include/internal/tcache.h b/externals/jemalloc/jemalloc/internal/tcache.h index c76597fafab..c76597fafab 100644 --- a/externals/jemalloc/include/internal/tcache.h +++ b/externals/jemalloc/jemalloc/internal/tcache.h diff --git a/externals/jemalloc/include/internal/totally_not_p_r_n.h b/externals/jemalloc/jemalloc/internal/totally_not_p_r_n.h index 0709d708012..0709d708012 100644 --- a/externals/jemalloc/include/internal/totally_not_p_r_n.h +++ b/externals/jemalloc/jemalloc/internal/totally_not_p_r_n.h diff --git a/externals/jemalloc/include/jemalloc.h b/externals/jemalloc/jemalloc/jemalloc.h index d9bafbfff55..d9bafbfff55 100644 --- a/externals/jemalloc/include/jemalloc.h +++ b/externals/jemalloc/jemalloc/jemalloc.h diff --git a/externals/jemalloc/include/jemalloc.h.in b/externals/jemalloc/jemalloc/jemalloc.h.in index 8ef8183686e..8ef8183686e 100644 --- a/externals/jemalloc/include/jemalloc.h.in +++ b/externals/jemalloc/jemalloc/jemalloc.h.in diff --git a/externals/jemalloc/include/jemalloc_defs.h b/externals/jemalloc/jemalloc/jemalloc_defs.h index e8acaed3abd..e8acaed3abd 100644 --- a/externals/jemalloc/include/jemalloc_defs.h +++ b/externals/jemalloc/jemalloc/jemalloc_defs.h diff --git a/externals/jemalloc/include/jemalloc_defs.h.in b/externals/jemalloc/jemalloc/jemalloc_defs.h.in index 8b98d670acc..8b98d670acc 100644 --- a/externals/jemalloc/include/jemalloc_defs.h.in +++ b/externals/jemalloc/jemalloc/jemalloc_defs.h.in diff --git a/externals/mersennetwister/delme b/externals/mersennetwister/delme deleted file mode 100644 index e69de29bb2d..00000000000 --- a/externals/mersennetwister/delme +++ /dev/null diff --git a/externals/sockets/CMakeLists.txt b/externals/sockets/CMakeLists.txt index a47c8dee75f..716da975ca9 100644 --- a/externals/sockets/CMakeLists.txt +++ b/externals/sockets/CMakeLists.txt @@ -1,26 +1,10 @@ -SET(trinitysockets_STAT_SRCS - Base64.cpp - Exception.cpp - Ipv4Address.cpp - Ipv6Address.cpp - Lock.cpp - Mutex.cpp - Parse.cpp - ResolvServer.cpp - ResolvSocket.cpp - Socket.cpp - SocketHandler.cpp - StdoutLog.cpp - StreamSocket.cpp - TcpSocket.cpp - Thread.cpp - UdpSocket.cpp - Utility.cpp - socket_include.cpp +file(GLOB sources *.cpp) +set(trinitysockets_STAT_SRCS + ${sources} ) include_directories( - ${CMAKE_SOURCE_DIR}/dep/include/sockets + ${CMAKE_CURRENT_SOURCE_DIR}/include ) add_library(trinitysockets STATIC ${trinitysockets_STAT_SRCS}) diff --git a/externals/sockets/Makefile b/externals/sockets/Makefile new file mode 100644 index 00000000000..80faf67aed9 --- /dev/null +++ b/externals/sockets/Makefile @@ -0,0 +1,136 @@ +# CMAKE generated file: DO NOT EDIT! +# Generated by "Unix Makefiles" Generator, CMake Version 2.6 + +# Default target executed when no arguments are given to make. +default_target: all +.PHONY : default_target + +#============================================================================= +# Special targets provided by cmake. + +# Disable implicit rules so canoncical targets will work. +.SUFFIXES: + +# Remove some rules from gmake that .SUFFIXES does not remove. +SUFFIXES = + +.SUFFIXES: .hpux_make_needs_suffix_list + +# Suppress display of executed commands. +$(VERBOSE).SILENT: + +# A target that is always out of date. +cmake_force: +.PHONY : cmake_force + +#============================================================================= +# Set environment variables for the build. + +# The shell in which to execute make rules. +SHELL = /bin/sh + +# The CMake executable. +CMAKE_COMMAND = /usr/bin/cmake + +# The command to remove a file. +RM = /usr/bin/cmake -E remove -f + +# The program to use to edit the cache. +CMAKE_EDIT_COMMAND = /usr/bin/ccmake + +# The top-level source directory on which CMake was run. +CMAKE_SOURCE_DIR = /home/trinity/dev/trinitycore/externals/sockets + +# The top-level build directory on which CMake was run. +CMAKE_BINARY_DIR = /home/trinity/dev/trinitycore/externals/sockets + +#============================================================================= +# Targets provided globally by CMake. + +# Special rule for the target edit_cache +edit_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake cache editor..." + /usr/bin/ccmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : edit_cache + +# Special rule for the target edit_cache +edit_cache/fast: edit_cache +.PHONY : edit_cache/fast + +# Special rule for the target rebuild_cache +rebuild_cache: + @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." + /usr/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) +.PHONY : rebuild_cache + +# Special rule for the target rebuild_cache +rebuild_cache/fast: rebuild_cache +.PHONY : rebuild_cache/fast + +# The main all target +all: cmake_check_build_system + $(CMAKE_COMMAND) -E cmake_progress_start /home/trinity/dev/trinitycore/externals/sockets/CMakeFiles /home/trinity/dev/trinitycore/externals/sockets/CMakeFiles/progress.make + $(MAKE) -f CMakeFiles/Makefile2 all + $(CMAKE_COMMAND) -E cmake_progress_start /home/trinity/dev/trinitycore/externals/sockets/CMakeFiles 0 +.PHONY : all + +# The main clean target +clean: + $(MAKE) -f CMakeFiles/Makefile2 clean +.PHONY : clean + +# The main clean target +clean/fast: clean +.PHONY : clean/fast + +# Prepare targets for installation. +preinstall: all + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall + +# Prepare targets for installation. +preinstall/fast: + $(MAKE) -f CMakeFiles/Makefile2 preinstall +.PHONY : preinstall/fast + +# clear depends +depend: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 +.PHONY : depend + +#============================================================================= +# Target rules for targets named trinitysockets + +# Build rule for target. +trinitysockets: cmake_check_build_system + $(MAKE) -f CMakeFiles/Makefile2 trinitysockets +.PHONY : trinitysockets + +# fast build rule for target. +trinitysockets/fast: + $(MAKE) -f CMakeFiles/trinitysockets.dir/build.make CMakeFiles/trinitysockets.dir/build +.PHONY : trinitysockets/fast + +# Help Target +help: + @echo "The following are some of the valid targets for this Makefile:" + @echo "... all (the default if no target is provided)" + @echo "... clean" + @echo "... depend" + @echo "... edit_cache" + @echo "... rebuild_cache" + @echo "... trinitysockets" +.PHONY : help + + + +#============================================================================= +# Special targets to cleanup operation of make. + +# Special rule to run CMake to check the build system integrity. +# No rule that depends on this can have commands that come from listfiles +# because they might be regenerated. +cmake_check_build_system: + $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 +.PHONY : cmake_check_build_system + diff --git a/externals/zlib/CMakeLists.txt b/externals/zlib/CMakeLists.txt new file mode 100644 index 00000000000..1887110020c --- /dev/null +++ b/externals/zlib/CMakeLists.txt @@ -0,0 +1,12 @@ +file(GLOB sources *.c) +file(GLOB headers *.h) + +SET(zlib_STAT_SRCS + ${sources} + ) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ) + +add_library(zlib STATIC ${zlib_STAT_SRCS}) diff --git a/externals/zlib/ChangeLog b/externals/zlib/ChangeLog deleted file mode 100644 index f310bb0fcdb..00000000000 --- a/externals/zlib/ChangeLog +++ /dev/null @@ -1,1208 +0,0 @@ - - ChangeLog file for zlib - -Changes in 1.2.5 (19 Apr 2010) -- Disable visibility attribute in win32/Makefile.gcc [Bar-Lev] -- Default to libdir as sharedlibdir in configure [Nieder] -- Update copyright dates on modified source files -- Update trees.c to be able to generate modified trees.h -- Exit configure for MinGW, suggesting win32/Makefile.gcc - -Changes in 1.2.4.5 (18 Apr 2010) -- Set sharedlibdir in configure [Torok] -- Set LDFLAGS in Makefile.in [Bar-Lev] -- Avoid mkdir objs race condition in Makefile.in [Bowler] -- Add ZLIB_INTERNAL in front of internal inter-module functions and arrays -- Define ZLIB_INTERNAL to hide internal functions and arrays for GNU C -- Don't use hidden attribute when it is a warning generator (e.g. Solaris) - -Changes in 1.2.4.4 (18 Apr 2010) -- Fix CROSS_PREFIX executable testing, CHOST extract, mingw* [Torok] -- Undefine _LARGEFILE64_SOURCE in zconf.h if it is zero, but not if empty -- Try to use bash or ksh regardless of functionality of /bin/sh -- Fix configure incompatibility with NetBSD sh -- Remove attempt to run under bash or ksh since have better NetBSD fix -- Fix win32/Makefile.gcc for MinGW [Bar-Lev] -- Add diagnostic messages when using CROSS_PREFIX in configure -- Added --sharedlibdir option to configure [Weigelt] -- Use hidden visibility attribute when available [Frysinger] - -Changes in 1.2.4.3 (10 Apr 2010) -- Only use CROSS_PREFIX in configure for ar and ranlib if they exist -- Use CROSS_PREFIX for nm [Bar-Lev] -- Assume _LARGEFILE64_SOURCE defined is equivalent to true -- Avoid use of undefined symbols in #if with && and || -- Make *64 prototypes in gzguts.h consistent with functions -- Add -shared load option for MinGW in configure [Bowler] -- Move z_off64_t to public interface, use instead of off64_t -- Remove ! from shell test in configure (not portable to Solaris) -- Change +0 macro tests to -0 for possibly increased portability - -Changes in 1.2.4.2 (9 Apr 2010) -- Add consistent carriage returns to readme.txt's in masmx86 and masmx64 -- Really provide prototypes for *64 functions when building without LFS -- Only define unlink() in minigzip.c if unistd.h not included -- Update README to point to contrib/vstudio project files -- Move projects/vc6 to old/ and remove projects/ -- Include stdlib.h in minigzip.c for setmode() definition under WinCE -- Clean up assembler builds in win32/Makefile.msc [Rowe] -- Include sys/types.h for Microsoft for off_t definition -- Fix memory leak on error in gz_open() -- Symbolize nm as $NM in configure [Weigelt] -- Use TEST_LDSHARED instead of LDSHARED to link test programs [Weigelt] -- Add +0 to _FILE_OFFSET_BITS and _LFS64_LARGEFILE in case not defined -- Fix bug in gzeof() to take into account unused input data -- Avoid initialization of structures with variables in puff.c -- Updated win32/README-WIN32.txt [Rowe] - -Changes in 1.2.4.1 (28 Mar 2010) -- Remove the use of [a-z] constructs for sed in configure [gentoo 310225] -- Remove $(SHAREDLIB) from LIBS in Makefile.in [Creech] -- Restore "for debugging" comment on sprintf() in gzlib.c -- Remove fdopen for MVS from gzguts.h -- Put new README-WIN32.txt in win32 [Rowe] -- Add check for shell to configure and invoke another shell if needed -- Fix big fat stinking bug in gzseek() on uncompressed files -- Remove vestigial F_OPEN64 define in zutil.h -- Set and check the value of _LARGEFILE_SOURCE and _LARGEFILE64_SOURCE -- Avoid errors on non-LFS systems when applications define LFS macros -- Set EXE to ".exe" in configure for MINGW [Kahle] -- Match crc32() in crc32.c exactly to the prototype in zlib.h [Sherrill] -- Add prefix for cross-compilation in win32/makefile.gcc [Bar-Lev] -- Add DLL install in win32/makefile.gcc [Bar-Lev] -- Allow Linux* or linux* from uname in configure [Bar-Lev] -- Allow ldconfig to be redefined in configure and Makefile.in [Bar-Lev] -- Add cross-compilation prefixes to configure [Bar-Lev] -- Match type exactly in gz_load() invocation in gzread.c -- Match type exactly of zcalloc() in zutil.c to zlib.h alloc_func -- Provide prototypes for *64 functions when building zlib without LFS -- Don't use -lc when linking shared library on MinGW -- Remove errno.h check in configure and vestigial errno code in zutil.h - -Changes in 1.2.4 (14 Mar 2010) -- Fix VER3 extraction in configure for no fourth subversion -- Update zlib.3, add docs to Makefile.in to make .pdf out of it -- Add zlib.3.pdf to distribution -- Don't set error code in gzerror() if passed pointer is NULL -- Apply destination directory fixes to CMakeLists.txt [Lowman] -- Move #cmakedefine's to a new zconf.in.cmakein -- Restore zconf.h for builds that don't use configure or cmake -- Add distclean to dummy Makefile for convenience -- Update and improve INDEX, README, and FAQ -- Update CMakeLists.txt for the return of zconf.h [Lowman] -- Update contrib/vstudio/vc9 and vc10 [Vollant] -- Change libz.dll.a back to libzdll.a in win32/Makefile.gcc -- Apply license and readme changes to contrib/asm686 [Raiter] -- Check file name lengths and add -c option in minigzip.c [Li] -- Update contrib/amd64 and contrib/masmx86/ [Vollant] -- Avoid use of "eof" parameter in trees.c to not shadow library variable -- Update make_vms.com for removal of zlibdefs.h [Zinser] -- Update assembler code and vstudio projects in contrib [Vollant] -- Remove outdated assembler code contrib/masm686 and contrib/asm586 -- Remove old vc7 and vc8 from contrib/vstudio -- Update win32/Makefile.msc, add ZLIB_VER_SUBREVISION [Rowe] -- Fix memory leaks in gzclose_r() and gzclose_w(), file leak in gz_open() -- Add contrib/gcc_gvmat64 for longest_match and inflate_fast [Vollant] -- Remove *64 functions from win32/zlib.def (they're not 64-bit yet) -- Fix bug in void-returning vsprintf() case in gzwrite.c -- Fix name change from inflate.h in contrib/inflate86/inffas86.c -- Check if temporary file exists before removing in make_vms.com [Zinser] -- Fix make install and uninstall for --static option -- Fix usage of _MSC_VER in gzguts.h and zutil.h [Truta] -- Update readme.txt in contrib/masmx64 and masmx86 to assemble - -Changes in 1.2.3.9 (21 Feb 2010) -- Expunge gzio.c -- Move as400 build information to old -- Fix updates in contrib/minizip and contrib/vstudio -- Add const to vsnprintf test in configure to avoid warnings [Weigelt] -- Delete zconf.h (made by configure) [Weigelt] -- Change zconf.in.h to zconf.h.in per convention [Weigelt] -- Check for NULL buf in gzgets() -- Return empty string for gzgets() with len == 1 (like fgets()) -- Fix description of gzgets() in zlib.h for end-of-file, NULL return -- Update minizip to 1.1 [Vollant] -- Avoid MSVC loss of data warnings in gzread.c, gzwrite.c -- Note in zlib.h that gzerror() should be used to distinguish from EOF -- Remove use of snprintf() from gzlib.c -- Fix bug in gzseek() -- Update contrib/vstudio, adding vc9 and vc10 [Kuno, Vollant] -- Fix zconf.h generation in CMakeLists.txt [Lowman] -- Improve comments in zconf.h where modified by configure - -Changes in 1.2.3.8 (13 Feb 2010) -- Clean up text files (tabs, trailing whitespace, etc.) [Oberhumer] -- Use z_off64_t in gz_zero() and gz_skip() to match state->skip -- Avoid comparison problem when sizeof(int) == sizeof(z_off64_t) -- Revert to Makefile.in from 1.2.3.6 (live with the clutter) -- Fix missing error return in gzflush(), add zlib.h note -- Add *64 functions to zlib.map [Levin] -- Fix signed/unsigned comparison in gz_comp() -- Use SFLAGS when testing shared linking in configure -- Add --64 option to ./configure to use -m64 with gcc -- Fix ./configure --help to correctly name options -- Have make fail if a test fails [Levin] -- Avoid buffer overrun in contrib/masmx64/gvmat64.asm [Simpson] -- Remove assembler object files from contrib - -Changes in 1.2.3.7 (24 Jan 2010) -- Always gzopen() with O_LARGEFILE if available -- Fix gzdirect() to work immediately after gzopen() or gzdopen() -- Make gzdirect() more precise when the state changes while reading -- Improve zlib.h documentation in many places -- Catch memory allocation failure in gz_open() -- Complete close operation if seek forward in gzclose_w() fails -- Return Z_ERRNO from gzclose_r() if close() fails -- Return Z_STREAM_ERROR instead of EOF for gzclose() being passed NULL -- Return zero for gzwrite() errors to match zlib.h description -- Return -1 on gzputs() error to match zlib.h description -- Add zconf.in.h to allow recovery from configure modification [Weigelt] -- Fix static library permissions in Makefile.in [Weigelt] -- Avoid warnings in configure tests that hide functionality [Weigelt] -- Add *BSD and DragonFly to Linux case in configure [gentoo 123571] -- Change libzdll.a to libz.dll.a in win32/Makefile.gcc [gentoo 288212] -- Avoid access of uninitialized data for first inflateReset2 call [Gomes] -- Keep object files in subdirectories to reduce the clutter somewhat -- Remove default Makefile and zlibdefs.h, add dummy Makefile -- Add new external functions to Z_PREFIX, remove duplicates, z_z_ -> z_ -- Remove zlibdefs.h completely -- modify zconf.h instead - -Changes in 1.2.3.6 (17 Jan 2010) -- Avoid void * arithmetic in gzread.c and gzwrite.c -- Make compilers happier with const char * for gz_error message -- Avoid unused parameter warning in inflate.c -- Avoid signed-unsigned comparison warning in inflate.c -- Indent #pragma's for traditional C -- Fix usage of strwinerror() in glib.c, change to gz_strwinerror() -- Correct email address in configure for system options -- Update make_vms.com and add make_vms.com to contrib/minizip [Zinser] -- Update zlib.map [Brown] -- Fix Makefile.in for Solaris 10 make of example64 and minizip64 [Torok] -- Apply various fixes to CMakeLists.txt [Lowman] -- Add checks on len in gzread() and gzwrite() -- Add error message for no more room for gzungetc() -- Remove zlib version check in gzwrite() -- Defer compression of gzprintf() result until need to -- Use snprintf() in gzdopen() if available -- Remove USE_MMAP configuration determination (only used by minigzip) -- Remove examples/pigz.c (available separately) -- Update examples/gun.c to 1.6 - -Changes in 1.2.3.5 (8 Jan 2010) -- Add space after #if in zutil.h for some compilers -- Fix relatively harmless bug in deflate_fast() [Exarevsky] -- Fix same problem in deflate_slow() -- Add $(SHAREDLIBV) to LIBS in Makefile.in [Brown] -- Add deflate_rle() for faster Z_RLE strategy run-length encoding -- Add deflate_huff() for faster Z_HUFFMAN_ONLY encoding -- Change name of "write" variable in inffast.c to avoid library collisions -- Fix premature EOF from gzread() in gzio.c [Brown] -- Use zlib header window size if windowBits is 0 in inflateInit2() -- Remove compressBound() call in deflate.c to avoid linking compress.o -- Replace use of errno in gz* with functions, support WinCE [Alves] -- Provide alternative to perror() in minigzip.c for WinCE [Alves] -- Don't use _vsnprintf on later versions of MSVC [Lowman] -- Add CMake build script and input file [Lowman] -- Update contrib/minizip to 1.1 [Svensson, Vollant] -- Moved nintendods directory from contrib to . -- Replace gzio.c with a new set of routines with the same functionality -- Add gzbuffer(), gzoffset(), gzclose_r(), gzclose_w() as part of above -- Update contrib/minizip to 1.1b -- Change gzeof() to return 0 on error instead of -1 to agree with zlib.h - -Changes in 1.2.3.4 (21 Dec 2009) -- Use old school .SUFFIXES in Makefile.in for FreeBSD compatibility -- Update comments in configure and Makefile.in for default --shared -- Fix test -z's in configure [Marquess] -- Build examplesh and minigzipsh when not testing -- Change NULL's to Z_NULL's in deflate.c and in comments in zlib.h -- Import LDFLAGS from the environment in configure -- Fix configure to populate SFLAGS with discovered CFLAGS options -- Adapt make_vms.com to the new Makefile.in [Zinser] -- Add zlib2ansi script for C++ compilation [Marquess] -- Add _FILE_OFFSET_BITS=64 test to make test (when applicable) -- Add AMD64 assembler code for longest match to contrib [Teterin] -- Include options from $SFLAGS when doing $LDSHARED -- Simplify 64-bit file support by introducing z_off64_t type -- Make shared object files in objs directory to work around old Sun cc -- Use only three-part version number for Darwin shared compiles -- Add rc option to ar in Makefile.in for when ./configure not run -- Add -WI,-rpath,. to LDFLAGS for OSF 1 V4* -- Set LD_LIBRARYN32_PATH for SGI IRIX shared compile -- Protect against _FILE_OFFSET_BITS being defined when compiling zlib -- Rename Makefile.in targets allstatic to static and allshared to shared -- Fix static and shared Makefile.in targets to be independent -- Correct error return bug in gz_open() by setting state [Brown] -- Put spaces before ;;'s in configure for better sh compatibility -- Add pigz.c (parallel implementation of gzip) to examples/ -- Correct constant in crc32.c to UL [Leventhal] -- Reject negative lengths in crc32_combine() -- Add inflateReset2() function to work like inflateEnd()/inflateInit2() -- Include sys/types.h for _LARGEFILE64_SOURCE [Brown] -- Correct typo in doc/algorithm.txt [Janik] -- Fix bug in adler32_combine() [Zhu] -- Catch missing-end-of-block-code error in all inflates and in puff - Assures that random input to inflate eventually results in an error -- Added enough.c (calculation of ENOUGH for inftrees.h) to examples/ -- Update ENOUGH and its usage to reflect discovered bounds -- Fix gzerror() error report on empty input file [Brown] -- Add ush casts in trees.c to avoid pedantic runtime errors -- Fix typo in zlib.h uncompress() description [Reiss] -- Correct inflate() comments with regard to automatic header detection -- Remove deprecation comment on Z_PARTIAL_FLUSH (it stays) -- Put new version of gzlog (2.0) in examples with interruption recovery -- Add puff compile option to permit invalid distance-too-far streams -- Add puff TEST command options, ability to read piped input -- Prototype the *64 functions in zlib.h when _FILE_OFFSET_BITS == 64, but - _LARGEFILE64_SOURCE not defined -- Fix Z_FULL_FLUSH to truly erase the past by resetting s->strstart -- Fix deflateSetDictionary() to use all 32K for output consistency -- Remove extraneous #define MIN_LOOKAHEAD in deflate.c (in deflate.h) -- Clear bytes after deflate lookahead to avoid use of uninitialized data -- Change a limit in inftrees.c to be more transparent to Coverity Prevent -- Update win32/zlib.def with exported symbols from zlib.h -- Correct spelling error in zlib.h [Willem] -- Allow Z_BLOCK for deflate() to force a new block -- Allow negative bits in inflatePrime() to delete existing bit buffer -- Add Z_TREES flush option to inflate() to return at end of trees -- Add inflateMark() to return current state information for random access -- Add Makefile for NintendoDS to contrib [Costa] -- Add -w in configure compile tests to avoid spurious warnings [Beucler] -- Fix typos in zlib.h comments for deflateSetDictionary() -- Fix EOF detection in transparent gzread() [Maier] - -Changes in 1.2.3.3 (2 October 2006) -- Make --shared the default for configure, add a --static option -- Add compile option to permit invalid distance-too-far streams -- Add inflateUndermine() function which is required to enable above -- Remove use of "this" variable name for C++ compatibility [Marquess] -- Add testing of shared library in make test, if shared library built -- Use ftello() and fseeko() if available instead of ftell() and fseek() -- Provide two versions of all functions that use the z_off_t type for - binary compatibility -- a normal version and a 64-bit offset version, - per the Large File Support Extension when _LARGEFILE64_SOURCE is - defined; use the 64-bit versions by default when _FILE_OFFSET_BITS - is defined to be 64 -- Add a --uname= option to configure to perhaps help with cross-compiling - -Changes in 1.2.3.2 (3 September 2006) -- Turn off silly Borland warnings [Hay] -- Use off64_t and define _LARGEFILE64_SOURCE when present -- Fix missing dependency on inffixed.h in Makefile.in -- Rig configure --shared to build both shared and static [Teredesai, Truta] -- Remove zconf.in.h and instead create a new zlibdefs.h file -- Fix contrib/minizip/unzip.c non-encrypted after encrypted [Vollant] -- Add treebuild.xml (see http://treebuild.metux.de/) [Weigelt] - -Changes in 1.2.3.1 (16 August 2006) -- Add watcom directory with OpenWatcom make files [Daniel] -- Remove #undef of FAR in zconf.in.h for MVS [Fedtke] -- Update make_vms.com [Zinser] -- Use -fPIC for shared build in configure [Teredesai, Nicholson] -- Use only major version number for libz.so on IRIX and OSF1 [Reinholdtsen] -- Use fdopen() (not _fdopen()) for Interix in zutil.h [Bck] -- Add some FAQ entries about the contrib directory -- Update the MVS question in the FAQ -- Avoid extraneous reads after EOF in gzio.c [Brown] -- Correct spelling of "successfully" in gzio.c [Randers-Pehrson] -- Add comments to zlib.h about gzerror() usage [Brown] -- Set extra flags in gzip header in gzopen() like deflate() does -- Make configure options more compatible with double-dash conventions - [Weigelt] -- Clean up compilation under Solaris SunStudio cc [Rowe, Reinholdtsen] -- Fix uninstall target in Makefile.in [Truta] -- Add pkgconfig support [Weigelt] -- Use $(DESTDIR) macro in Makefile.in [Reinholdtsen, Weigelt] -- Replace set_data_type() with a more accurate detect_data_type() in - trees.c, according to the txtvsbin.txt document [Truta] -- Swap the order of #include <stdio.h> and #include "zlib.h" in - gzio.c, example.c and minigzip.c [Truta] -- Shut up annoying VS2005 warnings about standard C deprecation [Rowe, - Truta] (where?) -- Fix target "clean" from win32/Makefile.bor [Truta] -- Create .pdb and .manifest files in win32/makefile.msc [Ziegler, Rowe] -- Update zlib www home address in win32/DLL_FAQ.txt [Truta] -- Update contrib/masmx86/inffas32.asm for VS2005 [Vollant, Van Wassenhove] -- Enable browse info in the "Debug" and "ASM Debug" configurations in - the Visual C++ 6 project, and set (non-ASM) "Debug" as default [Truta] -- Add pkgconfig support [Weigelt] -- Add ZLIB_VER_MAJOR, ZLIB_VER_MINOR and ZLIB_VER_REVISION in zlib.h, - for use in win32/zlib1.rc [Polushin, Rowe, Truta] -- Add a document that explains the new text detection scheme to - doc/txtvsbin.txt [Truta] -- Add rfc1950.txt, rfc1951.txt and rfc1952.txt to doc/ [Truta] -- Move algorithm.txt into doc/ [Truta] -- Synchronize FAQ with website -- Fix compressBound(), was low for some pathological cases [Fearnley] -- Take into account wrapper variations in deflateBound() -- Set examples/zpipe.c input and output to binary mode for Windows -- Update examples/zlib_how.html with new zpipe.c (also web site) -- Fix some warnings in examples/gzlog.c and examples/zran.c (it seems - that gcc became pickier in 4.0) -- Add zlib.map for Linux: "All symbols from zlib-1.1.4 remain - un-versioned, the patch adds versioning only for symbols introduced in - zlib-1.2.0 or later. It also declares as local those symbols which are - not designed to be exported." [Levin] -- Update Z_PREFIX list in zconf.in.h, add --zprefix option to configure -- Do not initialize global static by default in trees.c, add a response - NO_INIT_GLOBAL_POINTERS to initialize them if needed [Marquess] -- Don't use strerror() in gzio.c under WinCE [Yakimov] -- Don't use errno.h in zutil.h under WinCE [Yakimov] -- Move arguments for AR to its usage to allow replacing ar [Marot] -- Add HAVE_VISIBILITY_PRAGMA in zconf.in.h for Mozilla [Randers-Pehrson] -- Improve inflateInit() and inflateInit2() documentation -- Fix structure size comment in inflate.h -- Change configure help option from --h* to --help [Santos] - -Changes in 1.2.3 (18 July 2005) -- Apply security vulnerability fixes to contrib/infback9 as well -- Clean up some text files (carriage returns, trailing space) -- Update testzlib, vstudio, masmx64, and masmx86 in contrib [Vollant] - -Changes in 1.2.2.4 (11 July 2005) -- Add inflatePrime() function for starting inflation at bit boundary -- Avoid some Visual C warnings in deflate.c -- Avoid more silly Visual C warnings in inflate.c and inftrees.c for 64-bit - compile -- Fix some spelling errors in comments [Betts] -- Correct inflateInit2() error return documentation in zlib.h -- Add zran.c example of compressed data random access to examples - directory, shows use of inflatePrime() -- Fix cast for assignments to strm->state in inflate.c and infback.c -- Fix zlibCompileFlags() in zutil.c to use 1L for long shifts [Oberhumer] -- Move declarations of gf2 functions to right place in crc32.c [Oberhumer] -- Add cast in trees.c t avoid a warning [Oberhumer] -- Avoid some warnings in fitblk.c, gun.c, gzjoin.c in examples [Oberhumer] -- Update make_vms.com [Zinser] -- Initialize state->write in inflateReset() since copied in inflate_fast() -- Be more strict on incomplete code sets in inflate_table() and increase - ENOUGH and MAXD -- this repairs a possible security vulnerability for - invalid inflate input. Thanks to Tavis Ormandy and Markus Oberhumer for - discovering the vulnerability and providing test cases. -- Add ia64 support to configure for HP-UX [Smith] -- Add error return to gzread() for format or i/o error [Levin] -- Use malloc.h for OS/2 [Necasek] - -Changes in 1.2.2.3 (27 May 2005) -- Replace 1U constants in inflate.c and inftrees.c for 64-bit compile -- Typecast fread() return values in gzio.c [Vollant] -- Remove trailing space in minigzip.c outmode (VC++ can't deal with it) -- Fix crc check bug in gzread() after gzungetc() [Heiner] -- Add the deflateTune() function to adjust internal compression parameters -- Add a fast gzip decompressor, gun.c, to examples (use of inflateBack) -- Remove an incorrect assertion in examples/zpipe.c -- Add C++ wrapper in infback9.h [Donais] -- Fix bug in inflateCopy() when decoding fixed codes -- Note in zlib.h how much deflateSetDictionary() actually uses -- Remove USE_DICT_HEAD in deflate.c (would mess up inflate if used) -- Add _WIN32_WCE to define WIN32 in zconf.in.h [Spencer] -- Don't include stderr.h or errno.h for _WIN32_WCE in zutil.h [Spencer] -- Add gzdirect() function to indicate transparent reads -- Update contrib/minizip [Vollant] -- Fix compilation of deflate.c when both ASMV and FASTEST [Oberhumer] -- Add casts in crc32.c to avoid warnings [Oberhumer] -- Add contrib/masmx64 [Vollant] -- Update contrib/asm586, asm686, masmx86, testzlib, vstudio [Vollant] - -Changes in 1.2.2.2 (30 December 2004) -- Replace structure assignments in deflate.c and inflate.c with zmemcpy to - avoid implicit memcpy calls (portability for no-library compilation) -- Increase sprintf() buffer size in gzdopen() to allow for large numbers -- Add INFLATE_STRICT to check distances against zlib header -- Improve WinCE errno handling and comments [Chang] -- Remove comment about no gzip header processing in FAQ -- Add Z_FIXED strategy option to deflateInit2() to force fixed trees -- Add updated make_vms.com [Coghlan], update README -- Create a new "examples" directory, move gzappend.c there, add zpipe.c, - fitblk.c, gzlog.[ch], gzjoin.c, and zlib_how.html. -- Add FAQ entry and comments in deflate.c on uninitialized memory access -- Add Solaris 9 make options in configure [Gilbert] -- Allow strerror() usage in gzio.c for STDC -- Fix DecompressBuf in contrib/delphi/ZLib.pas [ManChesTer] -- Update contrib/masmx86/inffas32.asm and gvmat32.asm [Vollant] -- Use z_off_t for adler32_combine() and crc32_combine() lengths -- Make adler32() much faster for small len -- Use OS_CODE in deflate() default gzip header - -Changes in 1.2.2.1 (31 October 2004) -- Allow inflateSetDictionary() call for raw inflate -- Fix inflate header crc check bug for file names and comments -- Add deflateSetHeader() and gz_header structure for custom gzip headers -- Add inflateGetheader() to retrieve gzip headers -- Add crc32_combine() and adler32_combine() functions -- Add alloc_func, free_func, in_func, out_func to Z_PREFIX list -- Use zstreamp consistently in zlib.h (inflate_back functions) -- Remove GUNZIP condition from definition of inflate_mode in inflate.h - and in contrib/inflate86/inffast.S [Truta, Anderson] -- Add support for AMD64 in contrib/inflate86/inffas86.c [Anderson] -- Update projects/README.projects and projects/visualc6 [Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Avoid warning under NO_GZCOMPRESS in gzio.c; fix typo [Truta] -- Deprecate Z_ASCII; use Z_TEXT instead [Truta] -- Use a new algorithm for setting strm->data_type in trees.c [Truta] -- Do not define an exit() prototype in zutil.c unless DEBUG defined -- Remove prototype of exit() from zutil.c, example.c, minigzip.c [Truta] -- Add comment in zlib.h for Z_NO_FLUSH parameter to deflate() -- Fix Darwin build version identification [Peterson] - -Changes in 1.2.2 (3 October 2004) -- Update zlib.h comments on gzip in-memory processing -- Set adler to 1 in inflateReset() to support Java test suite [Walles] -- Add contrib/dotzlib [Ravn] -- Update win32/DLL_FAQ.txt [Truta] -- Update contrib/minizip [Vollant] -- Move contrib/visual-basic.txt to old/ [Truta] -- Fix assembler builds in projects/visualc6/ [Truta] - -Changes in 1.2.1.2 (9 September 2004) -- Update INDEX file -- Fix trees.c to update strm->data_type (no one ever noticed!) -- Fix bug in error case in inflate.c, infback.c, and infback9.c [Brown] -- Add "volatile" to crc table flag declaration (for DYNAMIC_CRC_TABLE) -- Add limited multitasking protection to DYNAMIC_CRC_TABLE -- Add NO_vsnprintf for VMS in zutil.h [Mozilla] -- Don't declare strerror() under VMS [Mozilla] -- Add comment to DYNAMIC_CRC_TABLE to use get_crc_table() to initialize -- Update contrib/ada [Anisimkov] -- Update contrib/minizip [Vollant] -- Fix configure to not hardcode directories for Darwin [Peterson] -- Fix gzio.c to not return error on empty files [Brown] -- Fix indentation; update version in contrib/delphi/ZLib.pas and - contrib/pascal/zlibpas.pas [Truta] -- Update mkasm.bat in contrib/masmx86 [Truta] -- Update contrib/untgz [Truta] -- Add projects/README.projects [Truta] -- Add project for MS Visual C++ 6.0 in projects/visualc6 [Cadieux, Truta] -- Update win32/DLL_FAQ.txt [Truta] -- Update list of Z_PREFIX symbols in zconf.h [Randers-Pehrson, Truta] -- Remove an unnecessary assignment to curr in inftrees.c [Truta] -- Add OS/2 to exe builds in configure [Poltorak] -- Remove err dummy parameter in zlib.h [Kientzle] - -Changes in 1.2.1.1 (9 January 2004) -- Update email address in README -- Several FAQ updates -- Fix a big fat bug in inftrees.c that prevented decoding valid - dynamic blocks with only literals and no distance codes -- - Thanks to "Hot Emu" for the bug report and sample file -- Add a note to puff.c on no distance codes case. - -Changes in 1.2.1 (17 November 2003) -- Remove a tab in contrib/gzappend/gzappend.c -- Update some interfaces in contrib for new zlib functions -- Update zlib version number in some contrib entries -- Add Windows CE definition for ptrdiff_t in zutil.h [Mai, Truta] -- Support shared libraries on Hurd and KFreeBSD [Brown] -- Fix error in NO_DIVIDE option of adler32.c - -Changes in 1.2.0.8 (4 November 2003) -- Update version in contrib/delphi/ZLib.pas and contrib/pascal/zlibpas.pas -- Add experimental NO_DIVIDE #define in adler32.c - - Possibly faster on some processors (let me know if it is) -- Correct Z_BLOCK to not return on first inflate call if no wrap -- Fix strm->data_type on inflate() return to correctly indicate EOB -- Add deflatePrime() function for appending in the middle of a byte -- Add contrib/gzappend for an example of appending to a stream -- Update win32/DLL_FAQ.txt [Truta] -- Delete Turbo C comment in README [Truta] -- Improve some indentation in zconf.h [Truta] -- Fix infinite loop on bad input in configure script [Church] -- Fix gzeof() for concatenated gzip files [Johnson] -- Add example to contrib/visual-basic.txt [Michael B.] -- Add -p to mkdir's in Makefile.in [vda] -- Fix configure to properly detect presence or lack of printf functions -- Add AS400 support [Monnerat] -- Add a little Cygwin support [Wilson] - -Changes in 1.2.0.7 (21 September 2003) -- Correct some debug formats in contrib/infback9 -- Cast a type in a debug statement in trees.c -- Change search and replace delimiter in configure from % to # [Beebe] -- Update contrib/untgz to 0.2 with various fixes [Truta] -- Add build support for Amiga [Nikl] -- Remove some directories in old that have been updated to 1.2 -- Add dylib building for Mac OS X in configure and Makefile.in -- Remove old distribution stuff from Makefile -- Update README to point to DLL_FAQ.txt, and add comment on Mac OS X -- Update links in README - -Changes in 1.2.0.6 (13 September 2003) -- Minor FAQ updates -- Update contrib/minizip to 1.00 [Vollant] -- Remove test of gz functions in example.c when GZ_COMPRESS defined [Truta] -- Update POSTINC comment for 68060 [Nikl] -- Add contrib/infback9 with deflate64 decoding (unsupported) -- For MVS define NO_vsnprintf and undefine FAR [van Burik] -- Add pragma for fdopen on MVS [van Burik] - -Changes in 1.2.0.5 (8 September 2003) -- Add OF to inflateBackEnd() declaration in zlib.h -- Remember start when using gzdopen in the middle of a file -- Use internal off_t counters in gz* functions to properly handle seeks -- Perform more rigorous check for distance-too-far in inffast.c -- Add Z_BLOCK flush option to return from inflate at block boundary -- Set strm->data_type on return from inflate - - Indicate bits unused, if at block boundary, and if in last block -- Replace size_t with ptrdiff_t in crc32.c, and check for correct size -- Add condition so old NO_DEFLATE define still works for compatibility -- FAQ update regarding the Windows DLL [Truta] -- INDEX update: add qnx entry, remove aix entry [Truta] -- Install zlib.3 into mandir [Wilson] -- Move contrib/zlib_dll_FAQ.txt to win32/DLL_FAQ.txt; update [Truta] -- Adapt the zlib interface to the new DLL convention guidelines [Truta] -- Introduce ZLIB_WINAPI macro to allow the export of functions using - the WINAPI calling convention, for Visual Basic [Vollant, Truta] -- Update msdos and win32 scripts and makefiles [Truta] -- Export symbols by name, not by ordinal, in win32/zlib.def [Truta] -- Add contrib/ada [Anisimkov] -- Move asm files from contrib/vstudio/vc70_32 to contrib/asm386 [Truta] -- Rename contrib/asm386 to contrib/masmx86 [Truta, Vollant] -- Add contrib/masm686 [Truta] -- Fix offsets in contrib/inflate86 and contrib/masmx86/inffas32.asm - [Truta, Vollant] -- Update contrib/delphi; rename to contrib/pascal; add example [Truta] -- Remove contrib/delphi2; add a new contrib/delphi [Truta] -- Avoid inclusion of the nonstandard <memory.h> in contrib/iostream, - and fix some method prototypes [Truta] -- Fix the ZCR_SEED2 constant to avoid warnings in contrib/minizip - [Truta] -- Avoid the use of backslash (\) in contrib/minizip [Vollant] -- Fix file time handling in contrib/untgz; update makefiles [Truta] -- Update contrib/vstudio/vc70_32 to comply with the new DLL guidelines - [Vollant] -- Remove contrib/vstudio/vc15_16 [Vollant] -- Rename contrib/vstudio/vc70_32 to contrib/vstudio/vc7 [Truta] -- Update README.contrib [Truta] -- Invert the assignment order of match_head and s->prev[...] in - INSERT_STRING [Truta] -- Compare TOO_FAR with 32767 instead of 32768, to avoid 16-bit warnings - [Truta] -- Compare function pointers with 0, not with NULL or Z_NULL [Truta] -- Fix prototype of syncsearch in inflate.c [Truta] -- Introduce ASMINF macro to be enabled when using an ASM implementation - of inflate_fast [Truta] -- Change NO_DEFLATE to NO_GZCOMPRESS [Truta] -- Modify test_gzio in example.c to take a single file name as a - parameter [Truta] -- Exit the example.c program if gzopen fails [Truta] -- Add type casts around strlen in example.c [Truta] -- Remove casting to sizeof in minigzip.c; give a proper type - to the variable compared with SUFFIX_LEN [Truta] -- Update definitions of STDC and STDC99 in zconf.h [Truta] -- Synchronize zconf.h with the new Windows DLL interface [Truta] -- Use SYS16BIT instead of __32BIT__ to distinguish between - 16- and 32-bit platforms [Truta] -- Use far memory allocators in small 16-bit memory models for - Turbo C [Truta] -- Add info about the use of ASMV, ASMINF and ZLIB_WINAPI in - zlibCompileFlags [Truta] -- Cygwin has vsnprintf [Wilson] -- In Windows16, OS_CODE is 0, as in MSDOS [Truta] -- In Cygwin, OS_CODE is 3 (Unix), not 11 (Windows32) [Wilson] - -Changes in 1.2.0.4 (10 August 2003) -- Minor FAQ updates -- Be more strict when checking inflateInit2's windowBits parameter -- Change NO_GUNZIP compile option to NO_GZIP to cover deflate as well -- Add gzip wrapper option to deflateInit2 using windowBits -- Add updated QNX rule in configure and qnx directory [Bonnefoy] -- Make inflate distance-too-far checks more rigorous -- Clean up FAR usage in inflate -- Add casting to sizeof() in gzio.c and minigzip.c - -Changes in 1.2.0.3 (19 July 2003) -- Fix silly error in gzungetc() implementation [Vollant] -- Update contrib/minizip and contrib/vstudio [Vollant] -- Fix printf format in example.c -- Correct cdecl support in zconf.in.h [Anisimkov] -- Minor FAQ updates - -Changes in 1.2.0.2 (13 July 2003) -- Add ZLIB_VERNUM in zlib.h for numerical preprocessor comparisons -- Attempt to avoid warnings in crc32.c for pointer-int conversion -- Add AIX to configure, remove aix directory [Bakker] -- Add some casts to minigzip.c -- Improve checking after insecure sprintf() or vsprintf() calls -- Remove #elif's from crc32.c -- Change leave label to inf_leave in inflate.c and infback.c to avoid - library conflicts -- Remove inflate gzip decoding by default--only enable gzip decoding by - special request for stricter backward compatibility -- Add zlibCompileFlags() function to return compilation information -- More typecasting in deflate.c to avoid warnings -- Remove leading underscore from _Capital #defines [Truta] -- Fix configure to link shared library when testing -- Add some Windows CE target adjustments [Mai] -- Remove #define ZLIB_DLL in zconf.h [Vollant] -- Add zlib.3 [Rodgers] -- Update RFC URL in deflate.c and algorithm.txt [Mai] -- Add zlib_dll_FAQ.txt to contrib [Truta] -- Add UL to some constants [Truta] -- Update minizip and vstudio [Vollant] -- Remove vestigial NEED_DUMMY_RETURN from zconf.in.h -- Expand use of NO_DUMMY_DECL to avoid all dummy structures -- Added iostream3 to contrib [Schwardt] -- Replace rewind() with fseek() for WinCE [Truta] -- Improve setting of zlib format compression level flags - - Report 0 for huffman and rle strategies and for level == 0 or 1 - - Report 2 only for level == 6 -- Only deal with 64K limit when necessary at compile time [Truta] -- Allow TOO_FAR check to be turned off at compile time [Truta] -- Add gzclearerr() function [Souza] -- Add gzungetc() function - -Changes in 1.2.0.1 (17 March 2003) -- Add Z_RLE strategy for run-length encoding [Truta] - - When Z_RLE requested, restrict matches to distance one - - Update zlib.h, minigzip.c, gzopen(), gzdopen() for Z_RLE -- Correct FASTEST compilation to allow level == 0 -- Clean up what gets compiled for FASTEST -- Incorporate changes to zconf.in.h [Vollant] - - Refine detection of Turbo C need for dummy returns - - Refine ZLIB_DLL compilation - - Include additional header file on VMS for off_t typedef -- Try to use _vsnprintf where it supplants vsprintf [Vollant] -- Add some casts in inffast.c -- Enchance comments in zlib.h on what happens if gzprintf() tries to - write more than 4095 bytes before compression -- Remove unused state from inflateBackEnd() -- Remove exit(0) from minigzip.c, example.c -- Get rid of all those darn tabs -- Add "check" target to Makefile.in that does the same thing as "test" -- Add "mostlyclean" and "maintainer-clean" targets to Makefile.in -- Update contrib/inflate86 [Anderson] -- Update contrib/testzlib, contrib/vstudio, contrib/minizip [Vollant] -- Add msdos and win32 directories with makefiles [Truta] -- More additions and improvements to the FAQ - -Changes in 1.2.0 (9 March 2003) -- New and improved inflate code - - About 20% faster - - Does not allocate 32K window unless and until needed - - Automatically detects and decompresses gzip streams - - Raw inflate no longer needs an extra dummy byte at end - - Added inflateBack functions using a callback interface--even faster - than inflate, useful for file utilities (gzip, zip) - - Added inflateCopy() function to record state for random access on - externally generated deflate streams (e.g. in gzip files) - - More readable code (I hope) -- New and improved crc32() - - About 50% faster, thanks to suggestions from Rodney Brown -- Add deflateBound() and compressBound() functions -- Fix memory leak in deflateInit2() -- Permit setting dictionary for raw deflate (for parallel deflate) -- Fix const declaration for gzwrite() -- Check for some malloc() failures in gzio.c -- Fix bug in gzopen() on single-byte file 0x1f -- Fix bug in gzread() on concatenated file with 0x1f at end of buffer - and next buffer doesn't start with 0x8b -- Fix uncompress() to return Z_DATA_ERROR on truncated input -- Free memory at end of example.c -- Remove MAX #define in trees.c (conflicted with some libraries) -- Fix static const's in deflate.c, gzio.c, and zutil.[ch] -- Declare malloc() and free() in gzio.c if STDC not defined -- Use malloc() instead of calloc() in zutil.c if int big enough -- Define STDC for AIX -- Add aix/ with approach for compiling shared library on AIX -- Add HP-UX support for shared libraries in configure -- Add OpenUNIX support for shared libraries in configure -- Use $cc instead of gcc to build shared library -- Make prefix directory if needed when installing -- Correct Macintosh avoidance of typedef Byte in zconf.h -- Correct Turbo C memory allocation when under Linux -- Use libz.a instead of -lz in Makefile (assure use of compiled library) -- Update configure to check for snprintf or vsnprintf functions and their - return value, warn during make if using an insecure function -- Fix configure problem with compile-time knowledge of HAVE_UNISTD_H that - is lost when library is used--resolution is to build new zconf.h -- Documentation improvements (in zlib.h): - - Document raw deflate and inflate - - Update RFCs URL - - Point out that zlib and gzip formats are different - - Note that Z_BUF_ERROR is not fatal - - Document string limit for gzprintf() and possible buffer overflow - - Note requirement on avail_out when flushing - - Note permitted values of flush parameter of inflate() -- Add some FAQs (and even answers) to the FAQ -- Add contrib/inflate86/ for x86 faster inflate -- Add contrib/blast/ for PKWare Data Compression Library decompression -- Add contrib/puff/ simple inflate for deflate format description - -Changes in 1.1.4 (11 March 2002) -- ZFREE was repeated on same allocation on some error conditions. - This creates a security problem described in - http://www.zlib.org/advisory-2002-03-11.txt -- Returned incorrect error (Z_MEM_ERROR) on some invalid data -- Avoid accesses before window for invalid distances with inflate window - less than 32K. -- force windowBits > 8 to avoid a bug in the encoder for a window size - of 256 bytes. (A complete fix will be available in 1.1.5). - -Changes in 1.1.3 (9 July 1998) -- fix "an inflate input buffer bug that shows up on rare but persistent - occasions" (Mark) -- fix gzread and gztell for concatenated .gz files (Didier Le Botlan) -- fix gzseek(..., SEEK_SET) in write mode -- fix crc check after a gzeek (Frank Faubert) -- fix miniunzip when the last entry in a zip file is itself a zip file - (J Lillge) -- add contrib/asm586 and contrib/asm686 (Brian Raiter) - See http://www.muppetlabs.com/~breadbox/software/assembly.html -- add support for Delphi 3 in contrib/delphi (Bob Dellaca) -- add support for C++Builder 3 and Delphi 3 in contrib/delphi2 (Davide Moretti) -- do not exit prematurely in untgz if 0 at start of block (Magnus Holmgren) -- use macro EXTERN instead of extern to support DLL for BeOS (Sander Stoks) -- added a FAQ file - -- Support gzdopen on Mac with Metrowerks (Jason Linhart) -- Do not redefine Byte on Mac (Brad Pettit & Jason Linhart) -- define SEEK_END too if SEEK_SET is not defined (Albert Chin-A-Young) -- avoid some warnings with Borland C (Tom Tanner) -- fix a problem in contrib/minizip/zip.c for 16-bit MSDOS (Gilles Vollant) -- emulate utime() for WIN32 in contrib/untgz (Gilles Vollant) -- allow several arguments to configure (Tim Mooney, Frodo Looijaard) -- use libdir and includedir in Makefile.in (Tim Mooney) -- support shared libraries on OSF1 V4 (Tim Mooney) -- remove so_locations in "make clean" (Tim Mooney) -- fix maketree.c compilation error (Glenn, Mark) -- Python interface to zlib now in Python 1.5 (Jeremy Hylton) -- new Makefile.riscos (Rich Walker) -- initialize static descriptors in trees.c for embedded targets (Nick Smith) -- use "foo-gz" in example.c for RISCOS and VMS (Nick Smith) -- add the OS/2 files in Makefile.in too (Andrew Zabolotny) -- fix fdopen and halloc macros for Microsoft C 6.0 (Tom Lane) -- fix maketree.c to allow clean compilation of inffixed.h (Mark) -- fix parameter check in deflateCopy (Gunther Nikl) -- cleanup trees.c, use compressed_len only in debug mode (Christian Spieler) -- Many portability patches by Christian Spieler: - . zutil.c, zutil.h: added "const" for zmem* - . Make_vms.com: fixed some typos - . Make_vms.com: msdos/Makefile.*: removed zutil.h from some dependency lists - . msdos/Makefile.msc: remove "default rtl link library" info from obj files - . msdos/Makefile.*: use model-dependent name for the built zlib library - . msdos/Makefile.emx, nt/Makefile.emx, nt/Makefile.gcc: - new makefiles, for emx (DOS/OS2), emx&rsxnt and mingw32 (Windows 9x / NT) -- use define instead of typedef for Bytef also for MSC small/medium (Tom Lane) -- replace __far with _far for better portability (Christian Spieler, Tom Lane) -- fix test for errno.h in configure (Tim Newsham) - -Changes in 1.1.2 (19 March 98) -- added contrib/minzip, mini zip and unzip based on zlib (Gilles Vollant) - See http://www.winimage.com/zLibDll/unzip.html -- preinitialize the inflate tables for fixed codes, to make the code - completely thread safe (Mark) -- some simplifications and slight speed-up to the inflate code (Mark) -- fix gzeof on non-compressed files (Allan Schrum) -- add -std1 option in configure for OSF1 to fix gzprintf (Martin Mokrejs) -- use default value of 4K for Z_BUFSIZE for 16-bit MSDOS (Tim Wegner + Glenn) -- added os2/Makefile.def and os2/zlib.def (Andrew Zabolotny) -- add shared lib support for UNIX_SV4.2MP (MATSUURA Takanori) -- do not wrap extern "C" around system includes (Tom Lane) -- mention zlib binding for TCL in README (Andreas Kupries) -- added amiga/Makefile.pup for Amiga powerUP SAS/C PPC (Andreas Kleinert) -- allow "make install prefix=..." even after configure (Glenn Randers-Pehrson) -- allow "configure --prefix $HOME" (Tim Mooney) -- remove warnings in example.c and gzio.c (Glenn Randers-Pehrson) -- move Makefile.sas to amiga/Makefile.sas - -Changes in 1.1.1 (27 Feb 98) -- fix macros _tr_tally_* in deflate.h for debug mode (Glenn Randers-Pehrson) -- remove block truncation heuristic which had very marginal effect for zlib - (smaller lit_bufsize than in gzip 1.2.4) and degraded a little the - compression ratio on some files. This also allows inlining _tr_tally for - matches in deflate_slow. -- added msdos/Makefile.w32 for WIN32 Microsoft Visual C++ (Bob Frazier) - -Changes in 1.1.0 (24 Feb 98) -- do not return STREAM_END prematurely in inflate (John Bowler) -- revert to the zlib 1.0.8 inflate to avoid the gcc 2.8.0 bug (Jeremy Buhler) -- compile with -DFASTEST to get compression code optimized for speed only -- in minigzip, try mmap'ing the input file first (Miguel Albrecht) -- increase size of I/O buffers in minigzip.c and gzio.c (not a big gain - on Sun but significant on HP) - -- add a pointer to experimental unzip library in README (Gilles Vollant) -- initialize variable gcc in configure (Chris Herborth) - -Changes in 1.0.9 (17 Feb 1998) -- added gzputs and gzgets functions -- do not clear eof flag in gzseek (Mark Diekhans) -- fix gzseek for files in transparent mode (Mark Diekhans) -- do not assume that vsprintf returns the number of bytes written (Jens Krinke) -- replace EXPORT with ZEXPORT to avoid conflict with other programs -- added compress2 in zconf.h, zlib.def, zlib.dnt -- new asm code from Gilles Vollant in contrib/asm386 -- simplify the inflate code (Mark): - . Replace ZALLOC's in huft_build() with single ZALLOC in inflate_blocks_new() - . ZALLOC the length list in inflate_trees_fixed() instead of using stack - . ZALLOC the value area for huft_build() instead of using stack - . Simplify Z_FINISH check in inflate() - -- Avoid gcc 2.8.0 comparison bug a little differently than zlib 1.0.8 -- in inftrees.c, avoid cc -O bug on HP (Farshid Elahi) -- in zconf.h move the ZLIB_DLL stuff earlier to avoid problems with - the declaration of FAR (Gilles VOllant) -- install libz.so* with mode 755 (executable) instead of 644 (Marc Lehmann) -- read_buf buf parameter of type Bytef* instead of charf* -- zmemcpy parameters are of type Bytef*, not charf* (Joseph Strout) -- do not redeclare unlink in minigzip.c for WIN32 (John Bowler) -- fix check for presence of directories in "make install" (Ian Willis) - -Changes in 1.0.8 (27 Jan 1998) -- fixed offsets in contrib/asm386/gvmat32.asm (Gilles Vollant) -- fix gzgetc and gzputc for big endian systems (Markus Oberhumer) -- added compress2() to allow setting the compression level -- include sys/types.h to get off_t on some systems (Marc Lehmann & QingLong) -- use constant arrays for the static trees in trees.c instead of computing - them at run time (thanks to Ken Raeburn for this suggestion). To create - trees.h, compile with GEN_TREES_H and run "make test". -- check return code of example in "make test" and display result -- pass minigzip command line options to file_compress -- simplifying code of inflateSync to avoid gcc 2.8 bug - -- support CC="gcc -Wall" in configure -s (QingLong) -- avoid a flush caused by ftell in gzopen for write mode (Ken Raeburn) -- fix test for shared library support to avoid compiler warnings -- zlib.lib -> zlib.dll in msdos/zlib.rc (Gilles Vollant) -- check for TARGET_OS_MAC in addition to MACOS (Brad Pettit) -- do not use fdopen for Metrowerks on Mac (Brad Pettit)) -- add checks for gzputc and gzputc in example.c -- avoid warnings in gzio.c and deflate.c (Andreas Kleinert) -- use const for the CRC table (Ken Raeburn) -- fixed "make uninstall" for shared libraries -- use Tracev instead of Trace in infblock.c -- in example.c use correct compressed length for test_sync -- suppress +vnocompatwarnings in configure for HPUX (not always supported) - -Changes in 1.0.7 (20 Jan 1998) -- fix gzseek which was broken in write mode -- return error for gzseek to negative absolute position -- fix configure for Linux (Chun-Chung Chen) -- increase stack space for MSC (Tim Wegner) -- get_crc_table and inflateSyncPoint are EXPORTed (Gilles Vollant) -- define EXPORTVA for gzprintf (Gilles Vollant) -- added man page zlib.3 (Rick Rodgers) -- for contrib/untgz, fix makedir() and improve Makefile - -- check gzseek in write mode in example.c -- allocate extra buffer for seeks only if gzseek is actually called -- avoid signed/unsigned comparisons (Tim Wegner, Gilles Vollant) -- add inflateSyncPoint in zconf.h -- fix list of exported functions in nt/zlib.dnt and mdsos/zlib.def - -Changes in 1.0.6 (19 Jan 1998) -- add functions gzprintf, gzputc, gzgetc, gztell, gzeof, gzseek, gzrewind and - gzsetparams (thanks to Roland Giersig and Kevin Ruland for some of this code) -- Fix a deflate bug occurring only with compression level 0 (thanks to - Andy Buckler for finding this one). -- In minigzip, pass transparently also the first byte for .Z files. -- return Z_BUF_ERROR instead of Z_OK if output buffer full in uncompress() -- check Z_FINISH in inflate (thanks to Marc Schluper) -- Implement deflateCopy (thanks to Adam Costello) -- make static libraries by default in configure, add --shared option. -- move MSDOS or Windows specific files to directory msdos -- suppress the notion of partial flush to simplify the interface - (but the symbol Z_PARTIAL_FLUSH is kept for compatibility with 1.0.4) -- suppress history buffer provided by application to simplify the interface - (this feature was not implemented anyway in 1.0.4) -- next_in and avail_in must be initialized before calling inflateInit or - inflateInit2 -- add EXPORT in all exported functions (for Windows DLL) -- added Makefile.nt (thanks to Stephen Williams) -- added the unsupported "contrib" directory: - contrib/asm386/ by Gilles Vollant <info@winimage.com> - 386 asm code replacing longest_match(). - contrib/iostream/ by Kevin Ruland <kevin@rodin.wustl.edu> - A C++ I/O streams interface to the zlib gz* functions - contrib/iostream2/ by Tyge Lvset <Tyge.Lovset@cmr.no> - Another C++ I/O streams interface - contrib/untgz/ by "Pedro A. Aranda Guti\irrez" <paag@tid.es> - A very simple tar.gz file extractor using zlib - contrib/visual-basic.txt by Carlos Rios <c_rios@sonda.cl> - How to use compress(), uncompress() and the gz* functions from VB. -- pass params -f (filtered data), -h (huffman only), -1 to -9 (compression - level) in minigzip (thanks to Tom Lane) - -- use const for rommable constants in deflate -- added test for gzseek and gztell in example.c -- add undocumented function inflateSyncPoint() (hack for Paul Mackerras) -- add undocumented function zError to convert error code to string - (for Tim Smithers) -- Allow compilation of gzio with -DNO_DEFLATE to avoid the compression code. -- Use default memcpy for Symantec MSDOS compiler. -- Add EXPORT keyword for check_func (needed for Windows DLL) -- add current directory to LD_LIBRARY_PATH for "make test" -- create also a link for libz.so.1 -- added support for FUJITSU UXP/DS (thanks to Toshiaki Nomura) -- use $(SHAREDLIB) instead of libz.so in Makefile.in (for HPUX) -- added -soname for Linux in configure (Chun-Chung Chen, -- assign numbers to the exported functions in zlib.def (for Windows DLL) -- add advice in zlib.h for best usage of deflateSetDictionary -- work around compiler bug on Atari (cast Z_NULL in call of s->checkfn) -- allow compilation with ANSI keywords only enabled for TurboC in large model -- avoid "versionString"[0] (Borland bug) -- add NEED_DUMMY_RETURN for Borland -- use variable z_verbose for tracing in debug mode (L. Peter Deutsch). -- allow compilation with CC -- defined STDC for OS/2 (David Charlap) -- limit external names to 8 chars for MVS (Thomas Lund) -- in minigzip.c, use static buffers only for 16-bit systems -- fix suffix check for "minigzip -d foo.gz" -- do not return an error for the 2nd of two consecutive gzflush() (Felix Lee) -- use _fdopen instead of fdopen for MSC >= 6.0 (Thomas Fanslau) -- added makelcc.bat for lcc-win32 (Tom St Denis) -- in Makefile.dj2, use copy and del instead of install and rm (Frank Donahoe) -- Avoid expanded $Id$. Use "rcs -kb" or "cvs admin -kb" to avoid Id expansion. -- check for unistd.h in configure (for off_t) -- remove useless check parameter in inflate_blocks_free -- avoid useless assignment of s->check to itself in inflate_blocks_new -- do not flush twice in gzclose (thanks to Ken Raeburn) -- rename FOPEN as F_OPEN to avoid clash with /usr/include/sys/file.h -- use NO_ERRNO_H instead of enumeration of operating systems with errno.h -- work around buggy fclose on pipes for HP/UX -- support zlib DLL with BORLAND C++ 5.0 (thanks to Glenn Randers-Pehrson) -- fix configure if CC is already equal to gcc - -Changes in 1.0.5 (3 Jan 98) -- Fix inflate to terminate gracefully when fed corrupted or invalid data -- Use const for rommable constants in inflate -- Eliminate memory leaks on error conditions in inflate -- Removed some vestigial code in inflate -- Update web address in README - -Changes in 1.0.4 (24 Jul 96) -- In very rare conditions, deflate(s, Z_FINISH) could fail to produce an EOF - bit, so the decompressor could decompress all the correct data but went - on to attempt decompressing extra garbage data. This affected minigzip too. -- zlibVersion and gzerror return const char* (needed for DLL) -- port to RISCOS (no fdopen, no multiple dots, no unlink, no fileno) -- use z_error only for DEBUG (avoid problem with DLLs) - -Changes in 1.0.3 (2 Jul 96) -- use z_streamp instead of z_stream *, which is now a far pointer in MSDOS - small and medium models; this makes the library incompatible with previous - versions for these models. (No effect in large model or on other systems.) -- return OK instead of BUF_ERROR if previous deflate call returned with - avail_out as zero but there is nothing to do -- added memcmp for non STDC compilers -- define NO_DUMMY_DECL for more Mac compilers (.h files merged incorrectly) -- define __32BIT__ if __386__ or i386 is defined (pb. with Watcom and SCO) -- better check for 16-bit mode MSC (avoids problem with Symantec) - -Changes in 1.0.2 (23 May 96) -- added Windows DLL support -- added a function zlibVersion (for the DLL support) -- fixed declarations using Bytef in infutil.c (pb with MSDOS medium model) -- Bytef is define's instead of typedef'd only for Borland C -- avoid reading uninitialized memory in example.c -- mention in README that the zlib format is now RFC1950 -- updated Makefile.dj2 -- added algorithm.doc - -Changes in 1.0.1 (20 May 96) [1.0 skipped to avoid confusion] -- fix array overlay in deflate.c which sometimes caused bad compressed data -- fix inflate bug with empty stored block -- fix MSDOS medium model which was broken in 0.99 -- fix deflateParams() which could generated bad compressed data. -- Bytef is define'd instead of typedef'ed (work around Borland bug) -- added an INDEX file -- new makefiles for DJGPP (Makefile.dj2), 32-bit Borland (Makefile.b32), - Watcom (Makefile.wat), Amiga SAS/C (Makefile.sas) -- speed up adler32 for modern machines without auto-increment -- added -ansi for IRIX in configure -- static_init_done in trees.c is an int -- define unlink as delete for VMS -- fix configure for QNX -- add configure branch for SCO and HPUX -- avoid many warnings (unused variables, dead assignments, etc...) -- no fdopen for BeOS -- fix the Watcom fix for 32 bit mode (define FAR as empty) -- removed redefinition of Byte for MKWERKS -- work around an MWKERKS bug (incorrect merge of all .h files) - -Changes in 0.99 (27 Jan 96) -- allow preset dictionary shared between compressor and decompressor -- allow compression level 0 (no compression) -- add deflateParams in zlib.h: allow dynamic change of compression level - and compression strategy. -- test large buffers and deflateParams in example.c -- add optional "configure" to build zlib as a shared library -- suppress Makefile.qnx, use configure instead -- fixed deflate for 64-bit systems (detected on Cray) -- fixed inflate_blocks for 64-bit systems (detected on Alpha) -- declare Z_DEFLATED in zlib.h (possible parameter for deflateInit2) -- always return Z_BUF_ERROR when deflate() has nothing to do -- deflateInit and inflateInit are now macros to allow version checking -- prefix all global functions and types with z_ with -DZ_PREFIX -- make falloc completely reentrant (inftrees.c) -- fixed very unlikely race condition in ct_static_init -- free in reverse order of allocation to help memory manager -- use zlib-1.0/* instead of zlib/* inside the tar.gz -- make zlib warning-free with "gcc -O3 -Wall -Wwrite-strings -Wpointer-arith - -Wconversion -Wstrict-prototypes -Wmissing-prototypes" -- allow gzread on concatenated .gz files -- deflateEnd now returns Z_DATA_ERROR if it was premature -- deflate is finally (?) fully deterministic (no matches beyond end of input) -- Document Z_SYNC_FLUSH -- add uninstall in Makefile -- Check for __cpluplus in zlib.h -- Better test in ct_align for partial flush -- avoid harmless warnings for Borland C++ -- initialize hash_head in deflate.c -- avoid warning on fdopen (gzio.c) for HP cc -Aa -- include stdlib.h for STDC compilers -- include errno.h for Cray -- ignore error if ranlib doesn't exist -- call ranlib twice for NeXTSTEP -- use exec_prefix instead of prefix for libz.a -- renamed ct_* as _tr_* to avoid conflict with applications -- clear z->msg in inflateInit2 before any error return -- initialize opaque in example.c, gzio.c, deflate.c and inflate.c -- fixed typo in zconf.h (_GNUC__ => __GNUC__) -- check for WIN32 in zconf.h and zutil.c (avoid farmalloc in 32-bit mode) -- fix typo in Make_vms.com (f$trnlnm -> f$getsyi) -- in fcalloc, normalize pointer if size > 65520 bytes -- don't use special fcalloc for 32 bit Borland C++ -- use STDC instead of __GO32__ to avoid redeclaring exit, calloc, etc... -- use Z_BINARY instead of BINARY -- document that gzclose after gzdopen will close the file -- allow "a" as mode in gzopen. -- fix error checking in gzread -- allow skipping .gz extra-field on pipes -- added reference to Perl interface in README -- put the crc table in FAR data (I dislike more and more the medium model :) -- added get_crc_table -- added a dimension to all arrays (Borland C can't count). -- workaround Borland C bug in declaration of inflate_codes_new & inflate_fast -- guard against multiple inclusion of *.h (for precompiled header on Mac) -- Watcom C pretends to be Microsoft C small model even in 32 bit mode. -- don't use unsized arrays to avoid silly warnings by Visual C++: - warning C4746: 'inflate_mask' : unsized array treated as '__far' - (what's wrong with far data in far model?). -- define enum out of inflate_blocks_state to allow compilation with C++ - -Changes in 0.95 (16 Aug 95) -- fix MSDOS small and medium model (now easier to adapt to any compiler) -- inlined send_bits -- fix the final (:-) bug for deflate with flush (output was correct but - not completely flushed in rare occasions). -- default window size is same for compression and decompression - (it's now sufficient to set MAX_WBITS in zconf.h). -- voidp -> voidpf and voidnp -> voidp (for consistency with other - typedefs and because voidnp was not near in large model). - -Changes in 0.94 (13 Aug 95) -- support MSDOS medium model -- fix deflate with flush (could sometimes generate bad output) -- fix deflateReset (zlib header was incorrectly suppressed) -- added support for VMS -- allow a compression level in gzopen() -- gzflush now calls fflush -- For deflate with flush, flush even if no more input is provided. -- rename libgz.a as libz.a -- avoid complex expression in infcodes.c triggering Turbo C bug -- work around a problem with gcc on Alpha (in INSERT_STRING) -- don't use inline functions (problem with some gcc versions) -- allow renaming of Byte, uInt, etc... with #define. -- avoid warning about (unused) pointer before start of array in deflate.c -- avoid various warnings in gzio.c, example.c, infblock.c, adler32.c, zutil.c -- avoid reserved word 'new' in trees.c - -Changes in 0.93 (25 June 95) -- temporarily disable inline functions -- make deflate deterministic -- give enough lookahead for PARTIAL_FLUSH -- Set binary mode for stdin/stdout in minigzip.c for OS/2 -- don't even use signed char in inflate (not portable enough) -- fix inflate memory leak for segmented architectures - -Changes in 0.92 (3 May 95) -- don't assume that char is signed (problem on SGI) -- Clear bit buffer when starting a stored block -- no memcpy on Pyramid -- suppressed inftest.c -- optimized fill_window, put longest_match inline for gcc -- optimized inflate on stored blocks. -- untabify all sources to simplify patches - -Changes in 0.91 (2 May 95) -- Default MEM_LEVEL is 8 (not 9 for Unix) as documented in zlib.h -- Document the memory requirements in zconf.h -- added "make install" -- fix sync search logic in inflateSync -- deflate(Z_FULL_FLUSH) now works even if output buffer too short -- after inflateSync, don't scare people with just "lo world" -- added support for DJGPP - -Changes in 0.9 (1 May 95) -- don't assume that zalloc clears the allocated memory (the TurboC bug - was Mark's bug after all :) -- let again gzread copy uncompressed data unchanged (was working in 0.71) -- deflate(Z_FULL_FLUSH), inflateReset and inflateSync are now fully implemented -- added a test of inflateSync in example.c -- moved MAX_WBITS to zconf.h because users might want to change that. -- document explicitly that zalloc(64K) on MSDOS must return a normalized - pointer (zero offset) -- added Makefiles for Microsoft C, Turbo C, Borland C++ -- faster crc32() - -Changes in 0.8 (29 April 95) -- added fast inflate (inffast.c) -- deflate(Z_FINISH) now returns Z_STREAM_END when done. Warning: this - is incompatible with previous versions of zlib which returned Z_OK. -- work around a TurboC compiler bug (bad code for b << 0, see infutil.h) - (actually that was not a compiler bug, see 0.81 above) -- gzread no longer reads one extra byte in certain cases -- In gzio destroy(), don't reference a freed structure -- avoid many warnings for MSDOS -- avoid the ERROR symbol which is used by MS Windows - -Changes in 0.71 (14 April 95) -- Fixed more MSDOS compilation problems :( There is still a bug with - TurboC large model. - -Changes in 0.7 (14 April 95) -- Added full inflate support. -- Simplified the crc32() interface. The pre- and post-conditioning - (one's complement) is now done inside crc32(). WARNING: this is - incompatible with previous versions; see zlib.h for the new usage. - -Changes in 0.61 (12 April 95) -- workaround for a bug in TurboC. example and minigzip now work on MSDOS. - -Changes in 0.6 (11 April 95) -- added minigzip.c -- added gzdopen to reopen a file descriptor as gzFile -- added transparent reading of non-gziped files in gzread. -- fixed bug in gzread (don't read crc as data) -- fixed bug in destroy (gzio.c) (don't return Z_STREAM_END for gzclose). -- don't allocate big arrays in the stack (for MSDOS) -- fix some MSDOS compilation problems - -Changes in 0.5: -- do real compression in deflate.c. Z_PARTIAL_FLUSH is supported but - not yet Z_FULL_FLUSH. -- support decompression but only in a single step (forced Z_FINISH) -- added opaque object for zalloc and zfree. -- added deflateReset and inflateReset -- added a variable zlib_version for consistency checking. -- renamed the 'filter' parameter of deflateInit2 as 'strategy'. - Added Z_FILTERED and Z_HUFFMAN_ONLY constants. - -Changes in 0.4: -- avoid "zip" everywhere, use zlib instead of ziplib. -- suppress Z_BLOCK_FLUSH, interpret Z_PARTIAL_FLUSH as block flush - if compression method == 8. -- added adler32 and crc32 -- renamed deflateOptions as deflateInit2, call one or the other but not both -- added the method parameter for deflateInit2. -- added inflateInit2 -- simplied considerably deflateInit and inflateInit by not supporting - user-provided history buffer. This is supported only in deflateInit2 - and inflateInit2. - -Changes in 0.3: -- prefix all macro names with Z_ -- use Z_FINISH instead of deflateEnd to finish compression. -- added Z_HUFFMAN_ONLY -- added gzerror() diff --git a/externals/zlib/README b/externals/zlib/README deleted file mode 100644 index d4219bf889f..00000000000 --- a/externals/zlib/README +++ /dev/null @@ -1,115 +0,0 @@ -ZLIB DATA COMPRESSION LIBRARY - -zlib 1.2.5 is a general purpose data compression library. All the code is -thread safe. The data format used by the zlib library is described by RFCs -(Request for Comments) 1950 to 1952 in the files -http://www.ietf.org/rfc/rfc1950.txt (zlib format), rfc1951.txt (deflate format) -and rfc1952.txt (gzip format). - -All functions of the compression library are documented in the file zlib.h -(volunteer to write man pages welcome, contact zlib@gzip.org). A usage example -of the library is given in the file example.c which also tests that the library -is working correctly. Another example is given in the file minigzip.c. The -compression library itself is composed of all source files except example.c and -minigzip.c. - -To compile all files and run the test program, follow the instructions given at -the top of Makefile.in. In short "./configure; make test", and if that goes -well, "make install" should work for most flavors of Unix. For Windows, use one -of the special makefiles in win32/ or contrib/vstudio/ . For VMS, use -make_vms.com. - -Questions about zlib should be sent to <zlib@gzip.org>, or to Gilles Vollant -<info@winimage.com> for the Windows DLL version. The zlib home page is -http://zlib.net/ . Before reporting a problem, please check this site to -verify that you have the latest version of zlib; otherwise get the latest -version and check whether the problem still exists or not. - -PLEASE read the zlib FAQ http://zlib.net/zlib_faq.html before asking for help. - -Mark Nelson <markn@ieee.org> wrote an article about zlib for the Jan. 1997 -issue of Dr. Dobb's Journal; a copy of the article is available at -http://marknelson.us/1997/01/01/zlib-engine/ . - -The changes made in version 1.2.5 are documented in the file ChangeLog. - -Unsupported third party contributions are provided in directory contrib/ . - -zlib is available in Java using the java.util.zip package, documented at -http://java.sun.com/developer/technicalArticles/Programming/compression/ . - -A Perl interface to zlib written by Paul Marquess <pmqs@cpan.org> is available -at CPAN (Comprehensive Perl Archive Network) sites, including -http://search.cpan.org/~pmqs/IO-Compress-Zlib/ . - -A Python interface to zlib written by A.M. Kuchling <amk@amk.ca> is -available in Python 1.5 and later versions, see -http://www.python.org/doc/lib/module-zlib.html . - -zlib is built into tcl: http://wiki.tcl.tk/4610 . - -An experimental package to read and write files in .zip format, written on top -of zlib by Gilles Vollant <info@winimage.com>, is available in the -contrib/minizip directory of zlib. - - -Notes for some targets: - -- For Windows DLL versions, please see win32/DLL_FAQ.txt - -- For 64-bit Irix, deflate.c must be compiled without any optimization. With - -O, one libpng test fails. The test works in 32 bit mode (with the -n32 - compiler flag). The compiler bug has been reported to SGI. - -- zlib doesn't work with gcc 2.6.3 on a DEC 3000/300LX under OSF/1 2.1 it works - when compiled with cc. - -- On Digital Unix 4.0D (formely OSF/1) on AlphaServer, the cc option -std1 is - necessary to get gzprintf working correctly. This is done by configure. - -- zlib doesn't work on HP-UX 9.05 with some versions of /bin/cc. It works with - other compilers. Use "make test" to check your compiler. - -- gzdopen is not supported on RISCOS or BEOS. - -- For PalmOs, see http://palmzlib.sourceforge.net/ - - -Acknowledgments: - - The deflate format used by zlib was defined by Phil Katz. The deflate and - zlib specifications were written by L. Peter Deutsch. Thanks to all the - people who reported problems and suggested various improvements in zlib; they - are too numerous to cite here. - -Copyright notice: - - (C) 1995-2010 Jean-loup Gailly and Mark Adler - - This software is provided 'as-is', without any express or implied - warranty. In no event will the authors be held liable for any damages - arising from the use of this software. - - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it - freely, subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would be - appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be - misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - Jean-loup Gailly Mark Adler - jloup@gzip.org madler@alumni.caltech.edu - -If you use the zlib library in a product, we would appreciate *not* receiving -lengthy legal documents to sign. The sources are provided for free but without -warranty of any kind. The library has been entirely written by Jean-loup -Gailly and Mark Adler; it does not include third-party code. - -If you redistribute modified sources, we would appreciate that you include in -the file ChangeLog history information documenting your changes. Please read -the FAQ for more information on the distribution of modified source versions. diff --git a/src/server/collision/CMakeLists.txt b/src/server/collision/CMakeLists.txt index 913ec7ecd01..a058c14378b 100644 --- a/src/server/collision/CMakeLists.txt +++ b/src/server/collision/CMakeLists.txt @@ -4,21 +4,21 @@ SET(collision_STAT_SRCS BoundingIntervalHierarchy.h BoundingIntervalHierarchy.cpp - Management/IVMapManager.h Maps/MapTree.cpp Maps/MapTree.h - Models/ModelInstance.cpp - Models/ModelInstance.h Maps/TileAssembler.cpp Maps/TileAssembler.h - VMapDefinitions.h + Models/ModelInstance.cpp + Models/ModelInstance.h + Models/WorldModel.cpp + Models/WorldModel.h + Management/IVMapManager.h Management/VMapFactory.cpp Management/VMapFactory.h Management/VMapManager2.cpp Management/VMapManager2.h + VMapDefinitions.h VMapTools.h - Models/WorldModel.cpp - Models/WorldModel.h ) include_directories( diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt index 7f9c936cff9..bf7b5d9f1ea 100644 --- a/src/server/game/CMakeLists.txt +++ b/src/server/game/CMakeLists.txt @@ -404,6 +404,8 @@ include_directories( if(NOT DO_SCRIPTS) SET(game_STAT_SRCS ${game_STAT_SRCS} + PrecompiledHeaders/ScriptPCH.cpp + PrecompiledHeaders/ScriptPCH.h AI/ScriptedAI/ScriptedEscortAI.cpp AI/ScriptedAI/ScriptedEscortAI.h AI/ScriptedAI/ScriptedCreature.cpp @@ -416,8 +418,6 @@ if(NOT DO_SCRIPTS) AI/ScriptedAI/ScriptedInstance.h AI/ScriptedAI/ScriptedSimpleAI.cpp AI/ScriptedAI/ScriptedSimpleAI.h - PrecompiledHeaders/ScriptPCH.cpp - PrecompiledHeaders/ScriptPCH.h ) message("-- Added Script Engine to GAME lib") endif(NOT DO_SCRIPTS) diff --git a/src/server/worldserver/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt index c908e6617f2..db8c75a6772 100644 --- a/src/server/worldserver/CMakeLists.txt +++ b/src/server/worldserver/CMakeLists.txt @@ -133,10 +133,14 @@ target_link_libraries( trinity-core game shared +zlib +trinitysockets trinitydatabase trinityauth trinityconfig collision +g3dlib +jemalloc ${SCRIPT_LIB} ${READLINE_LIBRARY} ${TERMCAP_LIBRARY} |